<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/[email protected]/konva.min.js"></script> <meta charset="utf-8" /> <title>Konva Snapping of shapes Demo</title> <style> body { margin: 0; padding: 0; overflow: hidden; background-color: #f0f0f0; } </style> </head> <body> <div id="container"></div> <script> var width = window.innerWidth; var height = window.innerHeight; var GUIDELINE_OFFSET = 5;
var stage = new Konva.Stage({ container: 'container', width: width, height: height, });
var layer = new Konva.Layer(); stage.add(layer);
for (var i = 0; i < 5; i++) { layer.add( new Konva.Rect({ x: Math.random() * stage.width(), y: Math.random() * stage.height(), width: 50 + Math.random() * 50, height: 50 + Math.random() * 50, fill: Konva.Util.getRandomColor(), rotation: Math.random() * 360, draggable: true, name: 'object', }) ); }
function getLineGuideStops(skipShape) { var vertical = [0, stage.width() / 2, stage.width()]; var horizontal = [0, stage.height() / 2, stage.height()];
stage.find('.object').forEach((guideItem) => { if (guideItem === skipShape) { return; } var box = guideItem.getClientRect(); vertical.push([box.x, box.x + box.width, box.x + box.width / 2]); horizontal.push([box.y, box.y + box.height, box.y + box.height / 2]); }); return { vertical: vertical.flat(), horizontal: horizontal.flat(), }; }
function getObjectSnappingEdges(node) { var box = node.getClientRect(); var absPos = node.absolutePosition();
return { vertical: [ { guide: Math.round(box.x), offset: Math.round(absPos.x - box.x), snap: 'start', }, { guide: Math.round(box.x + box.width / 2), offset: Math.round(absPos.x - box.x - box.width / 2), snap: 'center', }, { guide: Math.round(box.x + box.width), offset: Math.round(absPos.x - box.x - box.width), snap: 'end', }, ], horizontal: [ { guide: Math.round(box.y), offset: Math.round(absPos.y - box.y), snap: 'start', }, { guide: Math.round(box.y + box.height / 2), offset: Math.round(absPos.y - box.y - box.height / 2), snap: 'center', }, { guide: Math.round(box.y + box.height), offset: Math.round(absPos.y - box.y - box.height), snap: 'end', }, ], }; }
function getGuides(lineGuideStops, itemBounds) { var resultV = []; var resultH = [];
lineGuideStops.vertical.forEach((lineGuide) => { itemBounds.vertical.forEach((itemBound) => { var diff = Math.abs(lineGuide - itemBound.guide); if (diff < GUIDELINE_OFFSET) { resultV.push({ lineGuide: lineGuide, diff: diff, snap: itemBound.snap, offset: itemBound.offset, }); } }); });
lineGuideStops.horizontal.forEach((lineGuide) => { itemBounds.horizontal.forEach((itemBound) => { var diff = Math.abs(lineGuide - itemBound.guide); if (diff < GUIDELINE_OFFSET) { resultH.push({ lineGuide: lineGuide, diff: diff, snap: itemBound.snap, offset: itemBound.offset, }); } }); });
var guides = [];
var minV = resultV.sort((a, b) => a.diff - b.diff)[0]; var minH = resultH.sort((a, b) => a.diff - b.diff)[0]; if (minV) { guides.push({ lineGuide: minV.lineGuide, offset: minV.offset, orientation: 'V', snap: minV.snap, }); } if (minH) { guides.push({ lineGuide: minH.lineGuide, offset: minH.offset, orientation: 'H', snap: minH.snap, }); } return guides; }
function drawGuides(guides) { guides.forEach((lg) => { if (lg.orientation === 'H') { var line = new Konva.Line({ points: [-6000, 0, 6000, 0], stroke: 'rgb(0, 161, 255)', strokeWidth: 1, name: 'guid-line', dash: [4, 6], }); layer.add(line); line.absolutePosition({ x: 0, y: lg.lineGuide, }); } else if (lg.orientation === 'V') { var line = new Konva.Line({ points: [0, -6000, 0, 6000], stroke: 'rgb(0, 161, 255)', strokeWidth: 1, name: 'guid-line', dash: [4, 6], }); layer.add(line); line.absolutePosition({ x: lg.lineGuide, y: 0, }); } }); }
layer.on('dragmove', function (e) { layer.find('.guid-line').forEach((l) => l.destroy());
var lineGuideStops = getLineGuideStops(e.target); var itemBounds = getObjectSnappingEdges(e.target);
var guides = getGuides(lineGuideStops, itemBounds);
if (!guides.length) { return; }
drawGuides(guides);
var absPos = e.target.absolutePosition(); guides.forEach((lg) => { switch (lg.orientation) { case 'V': { absPos.x = lg.lineGuide + lg.offset; break; } case 'H': { absPos.y = lg.lineGuide + lg.offset; break; } } }); e.target.absolutePosition(absPos); });
layer.on('dragend', function (e) { layer.find('.guid-line').forEach((l) => l.destroy()); }); </script> </body> </html>
|