Fix "impossible d'aller plus loin vers la droite" — défilement automatique du canevas
Cause confirmée (tous les objets bloquaient au même endroit, pas spécifique à un widget) : une scène à taille fixe (960×540 par ex.) peut être plus LARGE que la zone visible de .canvasFrame (overflow:auto) selon la largeur de fenêtre — sa partie droite/basse défile alors HORS de vue, et le curseur atteignait le bord de cette zone VISIBLE bien avant celui de la scène elle-même : la souris ne pouvait tout simplement plus bouger physiquement plus loin, sans aucun rapport avec la limite réelle (960px) de la scène. onSceneObjectMouseDown/onSceneObjectResizeMouseDown font maintenant défiler .canvasFrame automatiquement quand le curseur approche un bord pendant un glisser/redimensionnement — et continuent de déplacer l'objet même si la souris reste immobile près du bord (mirror du comportement standard d'un glisser-déposer dans une zone scrollable), grâce à une position ACCUMULÉE plutôt que recalculée depuis le point de départ fixe. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
046c38b4dc
commit
7c9b2c9b25
@@ -36,16 +36,53 @@ function updateScenePosFields(x, y, w, h) {
|
|||||||
if (hEl) hEl.value = Math.round(h);
|
if (hEl) hEl.value = Math.round(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Défilement automatique de .canvasFrame (overflow:auto, voir static/
|
||||||
|
// style.css) pendant un glisser/redimensionnement — bug corrigé : une
|
||||||
|
// scène à taille FIXE (960×540 par ex.) peut être plus LARGE que la zone
|
||||||
|
// visible de .canvasFrame (fenêtre pas assez large, panneaux ouverts...),
|
||||||
|
// auquel cas sa partie droite/basse défile HORS de vue. Sans défilement
|
||||||
|
// automatique, le curseur atteignait le bord de la zone visible (donc de
|
||||||
|
// .canvasFrame) bien avant celui de la scène elle-même — "impossible
|
||||||
|
// d'aller plus loin vers la droite" alors qu'il restait pourtant de la
|
||||||
|
// place dans la scène. `onNudge(dx, dy)` est rappelé à chaque tick tant
|
||||||
|
// que le curseur reste dans la marge d'un bord, MÊME SI la souris ne
|
||||||
|
// bouge plus (immobile près du bord) — mirror du comportement standard
|
||||||
|
// d'un glisser-déposer proche d'un bord de zone scrollable.
|
||||||
|
function forgeStartCanvasAutoScroll(getMouse, onNudge) {
|
||||||
|
var MARGIN = 40, SPEED = 14;
|
||||||
|
var timer = setInterval(function () {
|
||||||
|
var frame = document.querySelector('.canvasFrame');
|
||||||
|
if (!frame) return;
|
||||||
|
var rect = frame.getBoundingClientRect();
|
||||||
|
var mouse = getMouse();
|
||||||
|
var dx = 0, dy = 0;
|
||||||
|
if (mouse.x > rect.right - MARGIN) dx = SPEED;
|
||||||
|
else if (mouse.x < rect.left + MARGIN) dx = -SPEED;
|
||||||
|
if (mouse.y > rect.bottom - MARGIN) dy = SPEED;
|
||||||
|
else if (mouse.y < rect.top + MARGIN) dy = -SPEED;
|
||||||
|
if (!dx && !dy) return;
|
||||||
|
frame.scrollLeft = Math.max(0, frame.scrollLeft + dx);
|
||||||
|
frame.scrollTop = Math.max(0, frame.scrollTop + dy);
|
||||||
|
onNudge(dx, dy);
|
||||||
|
}, 30);
|
||||||
|
return function stop() { clearInterval(timer); };
|
||||||
|
}
|
||||||
|
|
||||||
// Glisser-déposer en pixels, clampé à la taille FIXE de la scène
|
// Glisser-déposer en pixels, clampé à la taille FIXE de la scène
|
||||||
// (SCENE_WIDTH/SCENE_HEIGHT, voir templates/scene_edit.html) — mirror
|
// (SCENE_WIDTH/SCENE_HEIGHT, voir templates/scene_edit.html) — mirror
|
||||||
// d'onElementMouseDown() (tree-panels.js), mais sans conversion en % :
|
// d'onElementMouseDown() (tree-panels.js), mais sans conversion en % :
|
||||||
// une scène a des coordonnées pixel absolues, pas fluides.
|
// une scène a des coordonnées pixel absolues, pas fluides. Position
|
||||||
|
// ACCUMULÉE (curLeft/curTop, jamais recalculée depuis le point de départ
|
||||||
|
// fixe) : c'est ce qui permet à forgeStartCanvasAutoScroll ci-dessus de
|
||||||
|
// continuer à la déplacer même quand la souris ne bouge plus (immobile
|
||||||
|
// près d'un bord).
|
||||||
function onSceneObjectMouseDown(e, id) {
|
function onSceneObjectMouseDown(e, id) {
|
||||||
if (e.target.classList.contains('resizeHandle')) return;
|
if (e.target.classList.contains('resizeHandle')) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var el = document.getElementById('el-' + id);
|
var el = document.getElementById('el-' + id);
|
||||||
var startX = e.clientX, startY = e.clientY;
|
var startX = e.clientX, startY = e.clientY;
|
||||||
var startLeft = parseFloat(el.style.left) || 0, startTop = parseFloat(el.style.top) || 0;
|
var lastClientX = e.clientX, lastClientY = e.clientY;
|
||||||
|
var curLeft = parseFloat(el.style.left) || 0, curTop = parseFloat(el.style.top) || 0;
|
||||||
var width = parseFloat(el.style.width) || 0, height = parseFloat(el.style.height) || 0;
|
var width = parseFloat(el.style.width) || 0, height = parseFloat(el.style.height) || 0;
|
||||||
var moved = false;
|
var moved = false;
|
||||||
// Les panneaux flottants "🧩 Objets"/"⚙️ Propriétés" (voir static/style.css
|
// Les panneaux flottants "🧩 Objets"/"⚙️ Propriétés" (voir static/style.css
|
||||||
@@ -59,25 +96,32 @@ function onSceneObjectMouseDown(e, id) {
|
|||||||
// à sa valeur d'origine à la fin.
|
// à sa valeur d'origine à la fin.
|
||||||
var previousZIndex = el.style.zIndex;
|
var previousZIndex = el.style.zIndex;
|
||||||
|
|
||||||
|
function applyDelta(dx, dy) {
|
||||||
|
curLeft = Math.max(0, Math.min(SCENE_WIDTH - width, curLeft + dx));
|
||||||
|
curTop = Math.max(0, Math.min(SCENE_HEIGHT - height, curTop + dy));
|
||||||
|
el.style.left = curLeft + 'px';
|
||||||
|
el.style.top = curTop + 'px';
|
||||||
|
}
|
||||||
function onMove(ev) {
|
function onMove(ev) {
|
||||||
var dx = ev.clientX - startX, dy = ev.clientY - startY;
|
var dx = ev.clientX - lastClientX, dy = ev.clientY - lastClientY;
|
||||||
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
|
lastClientX = ev.clientX; lastClientY = ev.clientY;
|
||||||
|
if (Math.abs(ev.clientX - startX) > 3 || Math.abs(ev.clientY - startY) > 3) {
|
||||||
if (!moved) el.style.zIndex = 9999;
|
if (!moved) el.style.zIndex = 9999;
|
||||||
moved = true;
|
moved = true;
|
||||||
}
|
}
|
||||||
var newLeft = Math.max(0, Math.min(SCENE_WIDTH - width, startLeft + dx));
|
applyDelta(dx, dy);
|
||||||
var newTop = Math.max(0, Math.min(SCENE_HEIGHT - height, startTop + dy));
|
|
||||||
el.style.left = newLeft + 'px';
|
|
||||||
el.style.top = newTop + 'px';
|
|
||||||
}
|
}
|
||||||
|
var stopAutoScroll = forgeStartCanvasAutoScroll(
|
||||||
|
function () { return { x: lastClientX, y: lastClientY }; }, applyDelta
|
||||||
|
);
|
||||||
function onUp() {
|
function onUp() {
|
||||||
document.removeEventListener('mousemove', onMove);
|
document.removeEventListener('mousemove', onMove);
|
||||||
document.removeEventListener('mouseup', onUp);
|
document.removeEventListener('mouseup', onUp);
|
||||||
|
stopAutoScroll();
|
||||||
if (moved) {
|
if (moved) {
|
||||||
el.style.zIndex = previousZIndex;
|
el.style.zIndex = previousZIndex;
|
||||||
var newLeft = parseFloat(el.style.left), newTop = parseFloat(el.style.top);
|
updateScenePosFields(curLeft, curTop, width, height);
|
||||||
updateScenePosFields(newLeft, newTop, width, height);
|
saveSceneObjectGeometry(id, curLeft, curTop, width, height);
|
||||||
saveSceneObjectGeometry(id, newLeft, newTop, width, height);
|
|
||||||
} else {
|
} else {
|
||||||
window.location.href = SCREEN_EDIT_URL + '?selected=' + id;
|
window.location.href = SCREEN_EDIT_URL + '?selected=' + id;
|
||||||
}
|
}
|
||||||
@@ -90,8 +134,8 @@ function onSceneObjectResizeMouseDown(e, id) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var el = document.getElementById('el-' + id);
|
var el = document.getElementById('el-' + id);
|
||||||
var startX = e.clientX, startY = e.clientY;
|
var lastClientX = e.clientX, lastClientY = e.clientY;
|
||||||
var startWidth = parseFloat(el.style.width) || 0, startHeight = parseFloat(el.style.height) || 0;
|
var curWidth = parseFloat(el.style.width) || 0, curHeight = parseFloat(el.style.height) || 0;
|
||||||
var left = parseFloat(el.style.left) || 0, top = parseFloat(el.style.top) || 0;
|
var left = parseFloat(el.style.left) || 0, top = parseFloat(el.style.top) || 0;
|
||||||
// Voir la même note dans onSceneObjectMouseDown ci-dessus : au-dessus
|
// Voir la même note dans onSceneObjectMouseDown ci-dessus : au-dessus
|
||||||
// des panneaux flottants le temps du geste, pour rester visible même
|
// des panneaux flottants le temps du geste, pour rester visible même
|
||||||
@@ -99,18 +143,26 @@ function onSceneObjectResizeMouseDown(e, id) {
|
|||||||
var previousZIndex = el.style.zIndex;
|
var previousZIndex = el.style.zIndex;
|
||||||
el.style.zIndex = 9999;
|
el.style.zIndex = 9999;
|
||||||
|
|
||||||
function onMove(ev) {
|
function applyDelta(dx, dy) {
|
||||||
var dx = ev.clientX - startX, dy = ev.clientY - startY;
|
curWidth = Math.max(8, Math.min(SCENE_WIDTH - left, curWidth + dx));
|
||||||
var newWidth = Math.max(8, Math.min(SCENE_WIDTH - left, startWidth + dx));
|
curHeight = Math.max(8, Math.min(SCENE_HEIGHT - top, curHeight + dy));
|
||||||
var newHeight = Math.max(8, Math.min(SCENE_HEIGHT - top, startHeight + dy));
|
el.style.width = curWidth + 'px';
|
||||||
el.style.width = newWidth + 'px';
|
el.style.height = curHeight + 'px';
|
||||||
el.style.height = newHeight + 'px';
|
|
||||||
}
|
}
|
||||||
|
function onMove(ev) {
|
||||||
|
var dx = ev.clientX - lastClientX, dy = ev.clientY - lastClientY;
|
||||||
|
lastClientX = ev.clientX; lastClientY = ev.clientY;
|
||||||
|
applyDelta(dx, dy);
|
||||||
|
}
|
||||||
|
var stopAutoScroll = forgeStartCanvasAutoScroll(
|
||||||
|
function () { return { x: lastClientX, y: lastClientY }; }, applyDelta
|
||||||
|
);
|
||||||
function onUp() {
|
function onUp() {
|
||||||
document.removeEventListener('mousemove', onMove);
|
document.removeEventListener('mousemove', onMove);
|
||||||
document.removeEventListener('mouseup', onUp);
|
document.removeEventListener('mouseup', onUp);
|
||||||
|
stopAutoScroll();
|
||||||
el.style.zIndex = previousZIndex;
|
el.style.zIndex = previousZIndex;
|
||||||
var newWidth = parseFloat(el.style.width), newHeight = parseFloat(el.style.height);
|
var newWidth = curWidth, newHeight = curHeight;
|
||||||
updateScenePosFields(left, top, newWidth, newHeight);
|
updateScenePosFields(left, top, newWidth, newHeight);
|
||||||
saveSceneObjectGeometry(id, left, top, newWidth, newHeight);
|
saveSceneObjectGeometry(id, left, top, newWidth, newHeight);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user