Взаимодействие. js изменяемый размер пикселя идеально - PullRequest
1 голос
/ 01 апреля 2020

В этом jsFiddle у меня есть SVG-прямоугольник, размер которого можно изменить с помощью interact.js. Также имеется сетка 10 на 10 пикселей, а функция .resizable включает в себя оснастку 10 на 10 пикселей. Цель состоит в том, чтобы изменить размер прямоугольника и сделать так, чтобы края точно совпадали с сеткой.

В большинстве случаев это работает нормально, но во многих случаях это не так, как вы можете видеть на рисунке ниже. Может быть, настройку нужно сделать вручную на resizeend? Как решить эту проблему?

enter image description here

1 Ответ

1 голос
/ 01 апреля 2020

Как сказал Эрик:

С этим target.setAttribute(attr/a, Math.round(v/10)*10) кажется, что он работает:

  .on('resizemove', function(event) {
    // Resize the rect, not the group, it will resize automatically
    const target = event.target.querySelector('rect');

    for (const attr of ['width', 'height']) {
      let v = Number(target.getAttribute(attr));
      v += event.deltaRect[attr];
      target.setAttribute(attr, Math.round(v/10)*10);
    }

    for (const attr of ['top', 'left']) {
      const a = attr == 'left' ? 'x' : 'y';
      let v = Number(target.getAttribute(a));
      v += event.deltaRect[attr];
      target.setAttribute(a, Math.round(v/10)*10);
    }

    findLocations(rect, handles);
  });

Полная демонстрация здесь - https://jsfiddle.net/alexander_L/1mzs36qL/3/ и ниже:

const svg = document.getElementById('mysvg');
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");


// draw vertical lines
var gridSize = 10;
for (var i=0;i < 100;i++){
  var line = document.createElementNS("http://www.w3.org/2000/svg", "line");    
  svg.appendChild(line);
  line.setAttribute("x1", (i + 1) * gridSize)
  line.setAttribute("y1", 0)
  line.setAttribute("x2", (i + 1) * gridSize)
  line.setAttribute("y2", 500)
  line.setAttribute("stroke-width", 1)
  line.setAttribute("stroke", 'gray');
}
        
// draw horizontal lines
for (var i=0;i < 100;i++){
  var line = document.createElementNS("http://www.w3.org/2000/svg", "line");    
  svg.appendChild(line);
  line.setAttribute("x1", 0)
  line.setAttribute("y1", (i + 1) * gridSize)
  line.setAttribute("x2", 2000)
  line.setAttribute("y2", (i + 1) * gridSize)
  line.setAttribute("stroke-width", 1)
  line.setAttribute("stroke", 'gray');
}


svg.appendChild(group);
group.appendChild(rect);
group.setAttribute('class', 'resize-me');

rect.setAttribute('x', 100);
rect.setAttribute('y', 100);
rect.setAttribute('width', 100);
rect.setAttribute('height', 100);
rect.setAttribute('stroke-width', 1);
rect.setAttribute('stroke', 'white');
rect.setAttribute('fill', 'grey');

// Create the handles
const handles = [];
for (let i = 0; i < 8; i++) {
  const handle = document.createElementNS("http://www.w3.org/2000/svg", "rect");

  handle.setAttribute('width', 8);
  handle.setAttribute('height', 8);
  handle.setAttribute('stroke-width', 1);
  handle.setAttribute('stroke', 'white');
  handle.setAttribute('fill', 'black');

  handles.push(handle);
  group.appendChild(handle);
}

// Manually assign them their resize duties (R->L, T->B)
handles[0].classList.add('resize-top', 'resize-left');
handles[1].classList.add('resize-top');
handles[2].classList.add('resize-top', 'resize-right');
handles[3].classList.add('resize-left');
handles[4].classList.add('resize-right');
handles[5].classList.add('resize-bottom', 'resize-left');
handles[6].classList.add('resize-bottom');
handles[7].classList.add('resize-bottom', 'resize-right');



// This function takes the rect and the list of handles and positions
// the handles accordingly
const findLocations = (r, h) => {
  const x = Number(r.getAttribute('x'));
  const y = Number(r.getAttribute('y'));
  const width = Number(r.getAttribute('width'));
  const height = Number(r.getAttribute('height'));

  // Important these are in the same order as the classes above
  let locations = [
    [0, 0],
    [width / 2, 0],
    [width, 0],
    [0, height / 2],
    [width, height / 2],
    [0, height],
    [width / 2, height],
    [width, height]
  ];

  // Move each location such that it's relative to the (x,y) of the rect,
  // and also subtract half the width of the handles to make up for their
  // own size.
  locations = locations.map(subarr => [
    subarr[0] + x - 4,
    subarr[1] + y - 4
  ]);

  for (let i = 0; i < locations.length; i++) {
    h[i].setAttribute('x', locations[i][0]);
    h[i].setAttribute('y', locations[i][1]);
  }
}

interact('.resize-me')
  .resizable({
    edges: {
      left: '.resize-left',
      right: '.resize-right',
      bottom: '.resize-bottom',
      top: '.resize-top'
    },
    modifiers: [
      interact.modifiers.snap({
          targets: [
            interact.snappers.grid({
              x: 10,
              y: 10,
            })
          ]
        })
     ]
  })
  .on('resizemove', function(event) {
    // Resize the rect, not the group, it will resize automatically
    const target = event.target.querySelector('rect');

    for (const attr of ['width', 'height']) {
      let v = Number(target.getAttribute(attr));
      v += event.deltaRect[attr];
      target.setAttribute(attr, Math.round(v/10)*10);
    }

    for (const attr of ['top', 'left']) {
      const a = attr == 'left' ? 'x' : 'y';
      let v = Number(target.getAttribute(a));
      v += event.deltaRect[attr];
      target.setAttribute(a, Math.round(v/10)*10);
    }

    findLocations(rect, handles);
  });

findLocations(rect, handles);
svg {
  width: 100%;
  height: 240px;
  background-color: #2e9;
  
  -ms-touch-action: none;
      touch-action: none;
}
body { margin: 0; }
<script src="https://cdn.jsdelivr.net/npm/interactjs@latest/dist/interact.min.js"></script>

<svg id="mysvg"></svg>
...