Javascript 和 SVG - 移动带有偏移量的元素
Javascript and SVG - move element with offset
在下面的代码中,我对 canvas 进行了缩放和拖动。另外 - 当点击 canvas 我可以添加蓝色圆圈。我也可以四处移动蓝色圆圈 - 但问题是圆心移动到鼠标指针位置,没有考虑从鼠标指针到圆心的偏移量。如何将此偏移量包含在即使 canvas 为 scaled/repositioned 时也能正常工作的计算中?
var svgCanvas = document.getElementById("canvas");
var viewPort = document.getElementById("viewport");
var drag = false;
var dragged = false;
var offset = { x: 0, y: 0 };
var factor = .1;
var matrix = new DOMMatrix();
var whatToMove = function (event) { };
svgCanvas.addEventListener('pointerdown', function (event) {
drag = true;
whatToMove = moveViewPort;
offset = { x: event.clientX, y: event.clientY };
});
document.addEventListener('pointermove', function (event) {
if (event.buttons == 1) {
whatToMove(event);
}
});
var moveViewPort = function (event) {
if (drag) {
var tx = event.clientX - offset.x;
var ty = event.clientY - offset.y;
offset = {
x: event.clientX,
y: event.clientY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(tx, ty));
viewPort.style.transform = matrix.toString();
dragged = true;
}
};
svgCanvas.addEventListener('pointerup', function (event) {
if (!dragged) {
var c = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
var point = new DOMPoint(event.offsetX, event.offsetY);
var tPoint = matrix.inverse().transformPoint(point);
c.cx.baseVal.value = tPoint.x;
c.cy.baseVal.value = tPoint.y;
c.r.baseVal.value = 10;
c.style.fill = "blue";
var cdrag = false;
c.addEventListener('pointerdown', function (event) {
event.stopPropagation();
cdrag = true;
whatToMove = moveCircle;
});
var moveCircle = function (event) {
if (cdrag) {
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
//!!!!!!! HERE NEEDS TO INCLUDE ADDITONAL OFFSET FROM POINTER TO CENTER OF CIRCLE
c.cx.baseVal.value = tPoint_1.x;
c.cy.baseVal.value = tPoint_1.y;
}
};
c.addEventListener('pointerup', function (event) {
event.stopPropagation();
cdrag = false;
whatToMove = function () { };
});
viewPort.appendChild(c);
}
drag = false;
whatToMove = function () { };
dragged = false;
});
document.addEventListener('wheel', function (event) {
event.preventDefault();
var zoom = event.deltaY > 0 ? -1 : 1;
var scale = 1 + factor * zoom;
offset = {
x: event.offsetX,
y: event.offsetY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(offset.x, offset.y)
.scaleSelf(scale, scale)
.translateSelf(-offset.x, -offset.y));
viewPort.style.transform = matrix.toString();
}, { passive:false });
#around{
display: flex;
width: 100%;
height: 400px;
padding: 20px;
border: 1px dashed orange;
}
#canvas{
flex: 1;
height: auto;
}
<div id="around">
<svg id="canvas" style="border: 1px solid blue;">
<g id="viewport">
<rect x="100" y="100" width="400" height="200" fill="red"/>
<circle r="10" cx="600" cy="600" fill="blue"/>
</g>
</svg>
</div>
拖动事件开始时需要保存鼠标光标与圆心的偏移量
您已经设计了一种方法来获取鼠标在正确引用中的位置:
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
使用相同的代码计算初始偏移量并将其存储 (startDragOffset
) on pointerdown:
startDragOffset.x = c.cx.baseVal.value - tPoint_1.x;
startDragOffset.y = c.cy.baseVal.value - tPoint_1.y;
最后将该偏移量添加到 moveCircle
中的鼠标位置:
c.cx.baseVal.value = startDragOffset.x + tPoint_1.x;
c.cy.baseVal.value = startDragOffset.y + tPoint_1.y;
演示:
var svgCanvas = document.getElementById("canvas");
var viewPort = document.getElementById("viewport");
var drag = false;
var dragged = false;
var offset = { x: 0, y: 0 };
var factor = .1;
var matrix = new DOMMatrix();
var whatToMove = function (event) { };
var startDragOffset = { x: 0, y: 0 };
svgCanvas.addEventListener('pointerdown', function (event) {
drag = true;
whatToMove = moveViewPort;
offset = { x: event.clientX, y: event.clientY };
});
document.addEventListener('pointermove', function (event) {
if (event.buttons == 1) {
whatToMove(event);
}
});
var moveViewPort = function (event) {
if (drag) {
var tx = event.clientX - offset.x;
var ty = event.clientY - offset.y;
offset = {
x: event.clientX,
y: event.clientY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(tx, ty));
viewPort.style.transform = matrix.toString();
dragged = true;
}
};
svgCanvas.addEventListener('pointerup', function (event) {
if (!dragged) {
var c = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
var point = new DOMPoint(event.offsetX, event.offsetY);
var tPoint = matrix.inverse().transformPoint(point);
c.cx.baseVal.value = tPoint.x;
c.cy.baseVal.value = tPoint.y;
c.r.baseVal.value = 10;
c.style.fill = "blue";
var cdrag = false;
c.addEventListener('pointerdown', function (event) {
event.stopPropagation();
cdrag = true;
whatToMove = moveCircle;
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
startDragOffset.x = c.cx.baseVal.value - tPoint_1.x;
startDragOffset.y = c.cy.baseVal.value - tPoint_1.y;
});
var moveCircle = function (event) {
if (cdrag) {
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
//!!!!!!! HERE NEEDS TO INCLUDE ADDITONAL OFFSET FROM POINTER TO CENTER OF CIRCLE
c.cx.baseVal.value = startDragOffset.x + tPoint_1.x;
c.cy.baseVal.value = startDragOffset.y + tPoint_1.y;
}
};
c.addEventListener('pointerup', function (event) {
event.stopPropagation();
cdrag = false;
whatToMove = function () { };
});
viewPort.appendChild(c);
}
drag = false;
whatToMove = function () { };
dragged = false;
});
document.addEventListener('wheel', function (event) {
var zoom = event.deltaY > 0 ? -1 : 1;
var scale = 1 + factor * zoom;
offset = {
x: event.offsetX,
y: event.offsetY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(offset.x, offset.y)
.scaleSelf(scale, scale)
.translateSelf(-offset.x, -offset.y));
viewPort.style.transform = matrix.toString();
});
#around{
display: flex;
width: 100%;
height: 400px;
padding: 20px;
border: 1px dashed orange;
}
#canvas{
flex: 1;
height: auto;
}
<div id="around">
<svg id="canvas" style="border: 1px solid blue;">
<g id="viewport">
<rect x="100" y="100" width="400" height="200" fill="red"/>
<circle r="10" cx="600" cy="600" fill="blue"/>
</g>
</svg>
</div>
当然,你想做一些调整,比如去掉重复的鼠标位置转换代码。
在下面的代码中,我对 canvas 进行了缩放和拖动。另外 - 当点击 canvas 我可以添加蓝色圆圈。我也可以四处移动蓝色圆圈 - 但问题是圆心移动到鼠标指针位置,没有考虑从鼠标指针到圆心的偏移量。如何将此偏移量包含在即使 canvas 为 scaled/repositioned 时也能正常工作的计算中?
var svgCanvas = document.getElementById("canvas");
var viewPort = document.getElementById("viewport");
var drag = false;
var dragged = false;
var offset = { x: 0, y: 0 };
var factor = .1;
var matrix = new DOMMatrix();
var whatToMove = function (event) { };
svgCanvas.addEventListener('pointerdown', function (event) {
drag = true;
whatToMove = moveViewPort;
offset = { x: event.clientX, y: event.clientY };
});
document.addEventListener('pointermove', function (event) {
if (event.buttons == 1) {
whatToMove(event);
}
});
var moveViewPort = function (event) {
if (drag) {
var tx = event.clientX - offset.x;
var ty = event.clientY - offset.y;
offset = {
x: event.clientX,
y: event.clientY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(tx, ty));
viewPort.style.transform = matrix.toString();
dragged = true;
}
};
svgCanvas.addEventListener('pointerup', function (event) {
if (!dragged) {
var c = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
var point = new DOMPoint(event.offsetX, event.offsetY);
var tPoint = matrix.inverse().transformPoint(point);
c.cx.baseVal.value = tPoint.x;
c.cy.baseVal.value = tPoint.y;
c.r.baseVal.value = 10;
c.style.fill = "blue";
var cdrag = false;
c.addEventListener('pointerdown', function (event) {
event.stopPropagation();
cdrag = true;
whatToMove = moveCircle;
});
var moveCircle = function (event) {
if (cdrag) {
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
//!!!!!!! HERE NEEDS TO INCLUDE ADDITONAL OFFSET FROM POINTER TO CENTER OF CIRCLE
c.cx.baseVal.value = tPoint_1.x;
c.cy.baseVal.value = tPoint_1.y;
}
};
c.addEventListener('pointerup', function (event) {
event.stopPropagation();
cdrag = false;
whatToMove = function () { };
});
viewPort.appendChild(c);
}
drag = false;
whatToMove = function () { };
dragged = false;
});
document.addEventListener('wheel', function (event) {
event.preventDefault();
var zoom = event.deltaY > 0 ? -1 : 1;
var scale = 1 + factor * zoom;
offset = {
x: event.offsetX,
y: event.offsetY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(offset.x, offset.y)
.scaleSelf(scale, scale)
.translateSelf(-offset.x, -offset.y));
viewPort.style.transform = matrix.toString();
}, { passive:false });
#around{
display: flex;
width: 100%;
height: 400px;
padding: 20px;
border: 1px dashed orange;
}
#canvas{
flex: 1;
height: auto;
}
<div id="around">
<svg id="canvas" style="border: 1px solid blue;">
<g id="viewport">
<rect x="100" y="100" width="400" height="200" fill="red"/>
<circle r="10" cx="600" cy="600" fill="blue"/>
</g>
</svg>
</div>
拖动事件开始时需要保存鼠标光标与圆心的偏移量
您已经设计了一种方法来获取鼠标在正确引用中的位置:
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
使用相同的代码计算初始偏移量并将其存储 (startDragOffset
) on pointerdown:
startDragOffset.x = c.cx.baseVal.value - tPoint_1.x;
startDragOffset.y = c.cy.baseVal.value - tPoint_1.y;
最后将该偏移量添加到 moveCircle
中的鼠标位置:
c.cx.baseVal.value = startDragOffset.x + tPoint_1.x;
c.cy.baseVal.value = startDragOffset.y + tPoint_1.y;
演示:
var svgCanvas = document.getElementById("canvas");
var viewPort = document.getElementById("viewport");
var drag = false;
var dragged = false;
var offset = { x: 0, y: 0 };
var factor = .1;
var matrix = new DOMMatrix();
var whatToMove = function (event) { };
var startDragOffset = { x: 0, y: 0 };
svgCanvas.addEventListener('pointerdown', function (event) {
drag = true;
whatToMove = moveViewPort;
offset = { x: event.clientX, y: event.clientY };
});
document.addEventListener('pointermove', function (event) {
if (event.buttons == 1) {
whatToMove(event);
}
});
var moveViewPort = function (event) {
if (drag) {
var tx = event.clientX - offset.x;
var ty = event.clientY - offset.y;
offset = {
x: event.clientX,
y: event.clientY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(tx, ty));
viewPort.style.transform = matrix.toString();
dragged = true;
}
};
svgCanvas.addEventListener('pointerup', function (event) {
if (!dragged) {
var c = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
var point = new DOMPoint(event.offsetX, event.offsetY);
var tPoint = matrix.inverse().transformPoint(point);
c.cx.baseVal.value = tPoint.x;
c.cy.baseVal.value = tPoint.y;
c.r.baseVal.value = 10;
c.style.fill = "blue";
var cdrag = false;
c.addEventListener('pointerdown', function (event) {
event.stopPropagation();
cdrag = true;
whatToMove = moveCircle;
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
startDragOffset.x = c.cx.baseVal.value - tPoint_1.x;
startDragOffset.y = c.cy.baseVal.value - tPoint_1.y;
});
var moveCircle = function (event) {
if (cdrag) {
var rect = svgCanvas.getBoundingClientRect();
var x = event.clientX - rect.left;
var y = event.clientY - rect.top;
var point_1 = new DOMPoint(x, y);
var tPoint_1 = matrix.inverse().transformPoint(point_1);
//!!!!!!! HERE NEEDS TO INCLUDE ADDITONAL OFFSET FROM POINTER TO CENTER OF CIRCLE
c.cx.baseVal.value = startDragOffset.x + tPoint_1.x;
c.cy.baseVal.value = startDragOffset.y + tPoint_1.y;
}
};
c.addEventListener('pointerup', function (event) {
event.stopPropagation();
cdrag = false;
whatToMove = function () { };
});
viewPort.appendChild(c);
}
drag = false;
whatToMove = function () { };
dragged = false;
});
document.addEventListener('wheel', function (event) {
var zoom = event.deltaY > 0 ? -1 : 1;
var scale = 1 + factor * zoom;
offset = {
x: event.offsetX,
y: event.offsetY
};
matrix.preMultiplySelf(new DOMMatrix()
.translateSelf(offset.x, offset.y)
.scaleSelf(scale, scale)
.translateSelf(-offset.x, -offset.y));
viewPort.style.transform = matrix.toString();
});
#around{
display: flex;
width: 100%;
height: 400px;
padding: 20px;
border: 1px dashed orange;
}
#canvas{
flex: 1;
height: auto;
}
<div id="around">
<svg id="canvas" style="border: 1px solid blue;">
<g id="viewport">
<rect x="100" y="100" width="400" height="200" fill="red"/>
<circle r="10" cx="600" cy="600" fill="blue"/>
</g>
</svg>
</div>
当然,你想做一些调整,比如去掉重复的鼠标位置转换代码。