Поведение Pan Zoom с использованием EaselJS - PullRequest
0 голосов
/ 20 февраля 2020

У меня возникли проблемы с включением режима панорамирования / масштабирования с возможностью перетаскивания и перемещения некоторых фигур по холсту с помощью Easel JS.

Я хочу иметь возможность перемещать фигуру ТОЛЬКО, если я косил ее, но если я косил ее на сцене (т.е. не на фигуре), то я хочу иметь возможность панорамировать сцену. Это поведение должно быть согласованным независимо от уровня масштабирования (который изменяется колесом мыши).

Я прочитал это: Как остановить всплывающее окно события в easl js? Что говорит о том, что события mousedown этапа будут срабатывать независимо от того, нажал ли я на форму или пустое место , поэтому было бы лучше создать «фоновую» фигуру, чтобы фиксировать мои mousedown события, которые не имеют «правильной» формы.

Вот как я настроил эту скрипку: https://jsfiddle.net/hmcleay/mzheuLbg/

var stage = new createjs.Stage("myCanvas");
console.log('stage.scaleX: ', stage.scaleX);
console.log('stage.scaleY: ', stage.scaleY);

function addCircle(r,x,y){
    var g=new createjs.Graphics().beginFill("#ff0000").drawCircle(0,0,r);
    var s=new createjs.Shape(g)
    s.x=x;
    s.y=y;

  s.on('pressmove',  function(ev) {
    var localpos = stage.globalToLocal(ev.stageX, ev.stageY)
    s.x = localpos.x;
    s.y = localpos.y;
    stage.update();
    });

    stage.addChild(s);
    stage.update();
}

// create a rectangle 'background' Shape object to cover the stage (to allow for capturing mouse drags on anything except other shapes). 
bg = new createjs.Shape();
bg.graphics.beginFill("LightGray").drawRect(10, 10, stage.canvas.width - 20, stage.canvas.height - 20); //deliberately smaller for debugging purposes (easier to see if it moves). 
bg.x = 0;
bg.y = 0;
stage.addChild(bg);
stage.update();


//create a rectangle frame to represent the position of the stage. 
stageborder = new createjs.Shape();
stageborder.graphics.beginStroke("Black").drawRect(0, 0, stage.canvas.width, stage.canvas.height);
stageborder.x = 0;
stageborder.y = 0;
stage.addChild(stageborder);
stage.update();


// MOUSEWHEEL ZOOM LISTENER - anywhere on canvas.
var factor
canvas.addEventListener("wheel", function(e){
    if(Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)))>0){
        factor = 1.1;
    } else {
        factor = 1/1.1;
  }

  var local = stage.globalToLocal(stage.mouseX, stage.mouseY);
  stage.regX=local.x;
  stage.regY=local.y;
    stage.x=stage.mouseX;
    stage.y=stage.mouseY;   

    stage.scaleX = stage.scaleX * factor;
    stage.scaleY = stage.scaleY * factor;

  //re-size the 'background' shape to be the same as the canvas size.
  bg.graphics.command.w = bg.graphics.command.w / factor;
  bg.graphics.command.h = bg.graphics.command.h / factor;

  // re-position the 'background' shape to it's original position of (0,0) in the global space.  
  var localzero = stage.globalToLocal(0, 0);

  bg.x = localzero.x;
  bg.y = localzero.y;

    stage.update();
});

// listener to add circles to the canvas. 
canvas.addEventListener('dblclick', function(){
var localpos = stage.globalToLocal(stage.mouseX, stage.mouseY);
    addCircle(10, localpos.x, localpos.y);
});

bg.addEventListener("mousedown", function(ev1){
  // purpose of this listener is to be able to capture drag events on the 'background' to pan the whole stage. 
  // it needs to be a separate 'shape' object (rather than the stage itself), so that it doesn't fire when other shape objects are drag-moved around on the stage. 

  // get the initial positions of the stage, background, and mousedown. 
  var mousedownPos0 = {'x': ev1.stageX, 'y': ev1.stageY};
  var stagePos0 = {'x': stage.x, 'y': stage.y};
  var bgPos0 = {'x': bg.x, 'y': bg.y};

  bg.addEventListener('pressmove', function(ev2){
    //logic is to pan the stage, which will automatically pan all of it's children (shapes). 
    // except we want the 'background' shape to stay where it is, so we need to offset it in the opposite direction to the stage movement so that it stays where it is. 
    stageDelta = {'x': ev2.stageX - mousedownPos0.x, 'y': ev2.stageY - mousedownPos0.y};

    //adjust the stage position
    stage.x = stagePos0.x + stageDelta.x;
    stage.y = stagePos0.y + stageDelta.y;

    // return the 'background' shape to global(0,0), so that it doesn't move with the stage. 
    var localzero = stage.globalToLocal(0,0);
    bg.x = localzero.x;
    bg.y = localzero.y;

    stage.update();


  });

});

Серая рамка - моя фоновая фигура. Я намеренно сделал его немного меньше, чем холст, чтобы я мог видеть, где это (полезно для отладки). Дважды щелкните в любом месте на холсте, чтобы добавить красные круги. Если вы перетаскиваете круг, он только перемещает этот круг.

Если вы перетаскиваете серую область «фона» между кругами, она перемещает всю стадию (и, следовательно, все дочерние фигуры, принадлежащие этой сцене). Поскольку серый фон также является потомком сцены, он хочет двигаться вместе с ним. Поэтому я включил некоторый код, чтобы всегда возвращать эту серую коробку туда, где она началась. Черная рамка представляет положение «сцены», я просто добавил ее, чтобы помочь визуализировать, где находится сцена.

Управление масштабированием с помощью колесика мыши основано на ответе на этот вопрос: Мольберт JS - прерывистое панорамирование на увеличенном изображении

Аналогично перетаскиванию при масштабировании I необходимо отрегулировать размер и положение серого поля 'background', чтобы оно отображалось в той же позиции на холсте. Тем не менее, он не остается точно там, где я хочу… он, кажется, ползет вверх к левому верхнему углу холста, когда я уменьшаю масштаб. Я потратил довольно много времени, пытаясь диагностировать это поведение и не могу понять, почему это происходит. Я подозреваю, что это может быть как-то связано с округлением ... но я действительно не уверен.

Кто-нибудь может объяснить, почему моя серая коробка не остается неподвижной, когда я увеличиваю и уменьшаю масштаб?

Альтернативным методом будет удаление фигуры 'background', используемой для захвата событий mousedown, которые не не в «правильной» форме. Вместо этого можно было бы использовать событие mousedown stage, но не позволять ему перемещаться по сцене, если мышь находится над «shape». Будет ли это лучший способ справиться с этим поведением? Любые предложения, как предотвратить перемещение сцены?

Заранее спасибо, Хью.

1 Ответ

0 голосов
/ 21 февраля 2020

Хорошо,

Как обычно, после того, как я наконец обратился за помощью, мне удалось решить проблему.

Проблема была вызвана тем, что форма фона (серый прямоугольник) была на 10 пикселей меньше, чем холст, чтобы я мог видеть его положение более четко (для облегчения отладки). Как иронично, что это смещение стало причиной проблемы. При применении масштабирования смещение в 10 пикселей не преобразовывалось в «локальное» пространство. Сделав графическое изображение серого прямоугольника c в точке (0,0) с шириной и высотой равным ширине холста, проблема исчезла!

Надеюсь, что в какой-то момент это кому-нибудь пригодится ,

Ура, Хью.

...