Making interact.js resizable pixel perfect

有些话、适合烂在心里 提交于 2020-05-09 07:52:07

问题


In this jsFiddle I have an SVG rect that is resizable using interact.js. There's also a 10px by 10px grid and the .resizable function has included a 10px by 10 px snap. The objective is to resize the rect and have the edges snap exactly on the grid.

In most cases it works fine, but many times it is not, as you can see in the picture below. Maybe an adjustment needs to be done manually on resizeend ? How to fix this problem?


回答1:


As Erik said:

With this target.setAttribute(attr/a, Math.round(v/10)*10) it seems to work:

  .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);
  });

Full Demo here - https://jsfiddle.net/alexander_L/1mzs36qL/3/ and below:

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>


来源:https://stackoverflow.com/questions/60973085/making-interact-js-resizable-pixel-perfect

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!