Konva js: создание перетаскиваемой области с некоторыми ограничениями для одного из дочерних элементов. - PullRequest
3 голосов
/ 23 января 2020

Я создаю временную шкалу с помощью Konva, и вся временная шкала (сцена) перетаскивается по всем направлениям, но у меня есть ось со всеми годами временной шкалы (группа Konva), которую я хочу ограничить, чтобы она была только движется по горизонтали.

Я не могу использовать dragBoundFun c, поскольку это ограничит движение на всех узлах временной шкалы.

Я пытался изменить положение элемент, использующий событие dragmove:

stage.on("dragmove", function(evt) {
  xaxis.y(0);
});

Но при перетаскивании сцены ось все еще перемещается во всех направлениях.

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

Ответы [ 4 ]

2 голосов
/ 24 января 2020

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

stage.on("dragmove", function(evt) {
  // read absolute position
  const oldAbs = xaxis.absolutePosition();

  // set new absolute position, but make sure x = 0
  xaxis.absolutePosition({
   x: oldAbs.x,
   y: 0
  });
});
1 голос
/ 27 января 2020

Просто для удовольствия, урезанная версия моих ответов, показывающая функцию ondrag () без всех излишков временной шкалы.

var stage;

function setup() {

  // Set up a stage and a shape
  stage = new Konva.Stage({
    container: 'konva-stage',
    width: 600,
    height: 300
  });

  // layer1.
  var layer1 = new Konva.Layer({
    draggable: false
  })

  stage.add(layer1);

  var ln1 = new Konva.Line({
    points: [10, 0, 10, 20, 10, 10, 0, 10, 20, 10],
    stroke: 'cyan',
    strokeWidth: 4
  });
  layer1.add(ln1);



  var layer2 = new Konva.Layer({
    draggable: true,
  });
  stage.add(layer2);


  var ln2 = new Konva.Line({
    points: [10, 0, 10, 20, 10, 10, 0, 10, 20, 10],
    stroke: 'magenta',
    strokeWidth: 4
  });
  layer2.add(ln2);

  // position the crosses on the canvas
  ln1.position({
    x: 100,
    y: 80
  });
  ln2.position({
    x: 100,
    y: 40
  });

  // ! position of layer1 is moved in x axis in sync with layer2 via dragMove event of layer2.
  layer2.on('dragmove', function() {
    var pos = layer2.position();
    var bgPos = layer1.position();
    layer1.position({
      x: pos.x,
      y: bgPos.y
    }); // <--- move  layer1 in sync with layer2.
    stage.draw()
  });

  stage.draw()
}


setup()
  .konva-stage {
  width: 100%;
  height: 100%;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.0.13/konva.js"></script>
<p>Drag the upper cross - only one moves vertically whilst the other is contrained in the y-axis. Both move in sync on the x-axis</p>
<div id="konva-stage"></div>
1 голос
/ 24 января 2020

Вот немного более функциональная версия, которая позволяет перетаскивать слой события по вертикали, сохраняя ось временной шкалы видимой для справки. При этом используются два слоя - один служит фоном, содержащим временную линию и сетку, а второй показывает события.

Ключевой метод здесь - использование прослушивателя событий dragMove на перетаскиваемом слое событий для перемещения фонового слоя в syn c по горизонтали, но НЕ по вертикали. Между тем, слой событий также ограничен функцией dragBound, чтобы остановить глупый UX.

Улучшение будет состоять в том, чтобы добавить отсечение к слою событий, чтобы при перетаскивании вниз он не заслонял временную шкалу.

var stageWidth = 800,
  stageHeight = 300,
  timeFrom = 1960,
  timeTo = 2060,
  timeRange = timeTo - timeFrom,
  timeLineWidth = 1000,
  timeSteps = 20, // over 100 yrs = 5 year intervals
  timeInt = timeRange / timeSteps,
  timeLineStep = timeLineWidth / timeSteps,
  yearWidth = timeLineWidth / timeRange,
  plotHeight = 500,
  events = [{
      date: 1964,
      desc: 'Born',
      dist: 10
    },
    {
      date: 1966,
      desc: 'England win world cup - still celebrating !',
      dist: 20
    },    
    {
      date: 1968,
      desc: 'Infant school',
      dist: 30
    },
    {
      date: 1975,
      desc: 'Secondary school',
      dist: 50
    },
    {
      date: 1981,
      desc: 'Sixth form',
      dist: 7
    },
    {
      date: 1983,
      desc: 'University',
      dist: 30
    },
    {
      date: 1986,
      desc: 'Degree, entered IT career',
      dist: 50
    },
    {
      date: 1990,
      desc: 'Marriage #1',
      dist: 0
    },
    {
      date: 1996,
      desc: 'Divorce #1',
      dist: 0
    },
    {
      date: 1998,
      desc: 'Marriage #2 & Son born',
      dist: 90
    },
    {
      date: 2000,
      desc: 'World did not end',
      dist: 20
    },    
    {
      date: 2025,
      desc: 'Retired ?',
      dist: 0
    },
    {
      date: 2044,
      desc: 'Enters Duncodin - retirement home for IT workers',
      dist: 0
    },
    {
      date: 2054,
      desc: 'Star dust',
      dist: 0
    }
  ]


function setup() {

  // Set up a stage and a shape
  stage = new Konva.Stage({
    container: 'konva-stage',
    width: stageWidth,
    height: stageHeight
  });

  // bgLayer is the background with the grid, timeline and date text.
  var bgLayer = new Konva.Layer({
    draggable: false
  })

  stage.add(bgLayer);

  for (var i = 0, max = timeSteps; i < max; i = i + 1) {
    bgLayer.add(new Konva.Line({
      points: [(i * timeLineStep) + 0.5, 0, (i * timeLineStep) + .5, plotHeight],
      stroke: 'cyan',
      strokeWidth: 1
    }))

    bgLayer.add(new Konva.Text({
      x: (i * timeLineStep) + 4,
      y: 260,
      text: timeFrom + (timeInt * i),
      fontSize: 12,
      fontFamily: 'Calibri',
      fill: 'magenta',
      rotation: 90,
      listening: false
    }));

  }

  for (var i = 0, max = plotHeight; i < max; i = i + timeLineStep) {
    bgLayer.add(new Konva.Line({
      points: [0, i + 0.5, timeLineWidth, i + .5],
      stroke: 'cyan',
      strokeWidth: 1
    }))
  }

  // add timeline
  var timeLine = new Konva.Rect({
    x: 0,
    y: 245,
    height: 1,
    width: timeLineWidth,
    fill: 'magenta',
    listening: false
  });
  bgLayer.add(timeLine)

  // eventLayer contains only the event link line and text.
  var eventLayer = new Konva.Layer({
    draggable: true,

    // the dragBoundFunc returns an object as {x: val, y: val} in which the x is constricted to stop
    // the user dragging out of sight, and the y is not allowed to change.
    // ! position of bgLayer is moved in x axis in sync with eventLayer via dragMove event 
    dragBoundFunc: function(pos) {
      return {

        x: function() {
          var retX = pos.x;
          if (retX > 20) { // if the left exceeds 20px from left edge of stage
            retX = 20;
          } else if (retX < (stageWidth - (timeLineWidth + 50))) { // if the right exceeds 50 px from right edge of stage
            retX = stageWidth - (timeLineWidth + 50);
          }
          return retX;
        }(),

        y: function() {
          var retY = pos.y;
          if (retY < 0) {
            retY = 0;
          } else if (retY > 200) {
            retY = 200;
          }
          return retY;
        }()

      };
    }
  });
  stage.add(eventLayer);

  // ! position of bgLayer is moved in x axis in sync with eventLayer via dragMove event of eventLayer.
  eventLayer.on('dragmove', function() {
    var pos = eventLayer.position();
    var bgPos = bgLayer.position();
    bgLayer.position({
      x: pos.x,
      y: bgPos.y
    }); // <--- move the bgLayer in sync with the event eventLayer.
    stage.draw()
  });

  for (var i = 0, max = events.length; i < max; i = i + 1) {
    var event = events[i];
    var link = new Konva.Rect({
      x: yearWidth * (event.date - timeFrom),
      y: 200 - event.dist,
      width: 1,
      height: 55 + event.dist,
      fill: 'magenta',
      listening: false
    });
    eventLayer.add(link)

    var eventLabel = new Konva.Text({
      x: yearWidth * (event.date - timeFrom) - 5,
      y: 190 - event.dist,
      text: event.date + ' - ' + event.desc,
      fontSize: 16,
      fontFamily: 'Calibri',
      fill: 'magenta',
      rotation: -90,
      listening: false
    });
    eventLayer.add(eventLabel);

    var dragRect = new Konva.Rect({
      x: 0,
      y: 0,
      width: timeLineWidth,
      height: 500,
      opacity: 0,
      fill: 'cyan',
      listening: true
    });
    eventLayer.add(dragRect);

    dragRect.moveToTop()
  }

  stage.draw()

}

var stage, eventLayer;

setup()
.konva-stage {
  width: 100%;
  height: 100%;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.0.13/konva.js"></script>
<p>Drag the timeline left & right AND up & down...</p>
<div id="konva-stage"></div>
0 голосов
/ 23 января 2020

Не совсем понятно, о чем вы спрашиваете, но я предположил, что вы хотите ограничить перетаскивание шкалы времени, чтобы она давала хороший UX. Смотрите рабочий фрагмент ниже. Большая часть кода является настройкой временной шкалы. Важная часть:

  1. Включите прямоугольник, охватывающий всю временную шкалу, которая имеет нулевую непрозрачность и прослушивает события мыши.

  2. Предоставить слою dragBoundFun c, который возвращает объект как {x: val, y: val}, в котором значение x ограничено, чтобы пользователь не мог перетащить его из поля зрения горизонтально и у не разрешено менять. Если вы думаете о прямоугольнике и сцене как о прямоугольниках, то математику нетрудно понять. Если ваша шкала времени вертикальная, поменяйте местами поведение x & y.

var stageWidth = 800,
  timeFrom = 1960,
  timeTo = 2060,
  range = timeTo - timeFrom,
  timeLineWidth = 1000;
yearWidth = timeLineWidth / range,
  events = [{
      date: 1964,
      desc: 'Born'
    },
    {
      date: 1968,
      desc: 'Infant school'
    },
    {
      date: 1975,
      desc: 'Secondary school'
    },
    {
      date: 1981,
      desc: 'Sixth form'
    },
    {
      date: 1983,
      desc: 'University'
    },
    {
      date: 1986,
      desc: 'Degree, entered IT career'
    },
    {
      date: 1990,
      desc: 'Marriage #1'
    },
    {
      date: 1998,
      desc: 'Marriage #2'
    },
    {
      date: 1999,
      desc: 'Son born'
    },
    {
      date: 2025,
      desc: 'Retired ?'
    },
    {
      date: 2044,
      desc: 'Enters Duncodin - retirement home for IT workers'
    },
    {
      date: 2054,
      desc: 'Star dust'
    }
  ]


function setup() {

  // Set up a stage and a shape
  stage = new Konva.Stage({
    container: 'konva-stage',
    width: stageWidth,
    height: 500
  });


  layer = new Konva.Layer({
    draggable: true,
    
    // the dragBoundFunc returns an object as {x: val, y: val} in which the x is constricted to stop
    // the user dragging out of sight, and the y is not allowed to change.
    dragBoundFunc: function(pos) {
      return {
        x: function() {
          retX = pos.x;
          if (retX > 20) {
            retX = 20;
          } else if (retX < (stageWidth - (timeLineWidth + 50))) {
            retX = stageWidth - (timeLineWidth + 50);
          }
          return retX;
        }(),
        y: this.absolutePosition().y
      };
    }
  });
  stage.add(layer);

  // add timeline
  var timeLine = new Konva.Rect({
    x: 0,
    y: 245,
    height: 10,
    width: timeLineWidth,
    fill: 'magenta',
    listening: false
  });
  layer.add(timeLine)

  for (var i = 0, max = events.length; i < max; i = i + 1) {

    var event = events[i];
    var link = new Konva.Rect({
      x: yearWidth * (event.date - timeFrom),
      y: 200,
      width: 5,
      height: 55,
      fill: 'magenta',
      listening: false
    });
    layer.add(link)

    var timeLabel = new Konva.Text({
      x: yearWidth * (event.date - timeFrom) + 10,
      y: 265,
      text: event.date,
      fontSize: 16,
      fontFamily: 'Calibri',
      fill: 'magenta',
      rotation: 90,
      listening: false
    });
    layer.add(timeLabel);

    var eventLabel = new Konva.Text({
      x: yearWidth * (event.date - timeFrom) - 5,
      y: 190,
      text: event.desc,
      fontSize: 16,
      fontFamily: 'Calibri',
      fill: 'magenta',
      rotation: -90,
      listening: false
    });
    layer.add(eventLabel);

    var dragRect = new Konva.Rect({
      x: 0,
      y: 0,
      width: timeLineWidth,
      height: 500,
      opacity: 0,
      fill: 'cyan',
      listening: true
    });
    layer.add(dragRect);

    dragRect.moveToTop()
  }

  stage.draw()

}

var stage, layer;

setup()
.konva-stage {
  width: 100%;
  height: 100%;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/4.0.13/konva.js"></script>
<p>Drag the timeline...</p>
<div id="konva-stage"></div>
...