我尝试创建一个小插件,通过拖动每个现有
rect
和 rect
右上角的一点 circle
来缩放 SVG 元素。虽然这对于红色元素效果很好,但对于蓝色元素却无法正确执行。
所需的行为是使用左下点作为原点缩放元素,同时保持其比例。然而在蓝色元素上它也会旋转它。
蓝色元素已经旋转:
matrix(1 -0.5 0.5 1 0 0)
虽然红色元素不是:
matrix(1 0 0 1 0 0)
我的公式:
new m(a) = prev m(a) * largest movement of dx or dy as factor
new m(d) = new m(a) //to keep proportions
new m(e) = (prev m(a) - new m(a)) * prev(x) + prev m(e) //vertical position
new m(f) = (prev m(d) - new m(d)) * (prev(y) + prev(height)) + prev m(f) //horizontal position
我认为蓝色元素上已经存在的旋转导致了我的问题,但我无法理解为什么。由于我只是通过比例(a,d)的差异来调整平移(e,f),所以无论现有的旋转如何,这都不起作用吗?
例如,我可以将蓝色元素包装在另一个
g
中,并在其上添加更改,而不需要旋转:
<g transform="matrix(1.2 0 0 1.2 -115 -25)">
<rect x="380" y="320" width="40" height="40" fill="blue" transform="matrix(1 -0.5 0.5 1 0 0)"></rect>
</g>
const
_Drawing = document.querySelector('svg'),
_Parent = _Drawing.querySelector('.VWS_DragAndDrop');
let
_currentElement = null; //current "g.Dragging"
//add mousedown-event for dragging
_Drawing.onmousedown = function(event){
if(event.target.tagName === 'rect' && event.target.parentNode.parentNode === _Parent){
//set "g.Dragging" as dragging element
_currentElement = event.target.parentNode;
const
tBBoxE = _currentElement.getBBox(),
tBBoxR = _currentElement.referenceElement.getBBox(),
tMatrix = _currentElement.transform.baseVal.getItem(0).matrix;
//values on start to calculate the adjustments on move
_currentElement.referenceOptions = {
startX: event.clientX,
startY: event.clientY,
Bounds: {height: tBBoxR.height, w: tBBoxE.width * tMatrix.a, h: tBBoxE.height * tMatrix.d, x: tBBoxR.x, y: tBBoxR.y},
Matrix: {a: tMatrix.a, b: tMatrix.b, c: tMatrix.c, d: tMatrix.d, e: tMatrix.e, f: tMatrix.f}
}
}
else{
//reset dragging element
_currentElement = null
}
};
//add mousemove for dragging
_Drawing.onmousemove = function(event){
if(_currentElement){
const
tOriginalBBox = _currentElement.referenceOptions.Bounds,
tOriginalMatrix = _currentElement.referenceOptions.Matrix,
tCurrentMatrix = _currentElement.transform.baseVal.getItem(0).matrix,
tNewMatrix = _currentElement.ownerSVGElement.createSVGMatrix(),
tCTM = _currentElement.ownerSVGElement.getScreenCTM()
tC1 = {x: (_currentElement.referenceOptions.startX - tCTM.e) / tCTM.a, y: (_currentElement.referenceOptions.startY - tCTM.f) / tCTM.d},
tC2 = {x: (event.clientX - tCTM.e) / tCTM.a, y: (event.clientY - tCTM.f) / tCTM.d},
tDX = (tC2.x-tC1.x),
tDY = (tC2.y-tC1.y);
//current matrix as default
tNewMatrix.a = tCurrentMatrix.a;
tNewMatrix.b = tCurrentMatrix.b;
tNewMatrix.c = tCurrentMatrix.c;
tNewMatrix.d = tCurrentMatrix.d;
tNewMatrix.e = tCurrentMatrix.e;
tNewMatrix.f = tCurrentMatrix.f;
//adjust matrix around p(bottom-left) while keeping proportions
tNewMatrix.a = Math.max(tOriginalMatrix.a/100*((tOriginalBBox.w+tDX)*100/tOriginalBBox.w), tOriginalMatrix.d/100*((tOriginalBBox.h-tDY)*100/tOriginalBBox.h));
tNewMatrix.d = tNewMatrix.a;
tNewMatrix.e = (tOriginalMatrix.a - tNewMatrix.a) * tOriginalBBox.x + tOriginalMatrix.e; //vertical position
tNewMatrix.f = (tOriginalMatrix.d - tNewMatrix.d) * (tOriginalBBox.y + tOriginalBBox.height) + tOriginalMatrix.f; //horizontal position
//set new matrix to reference-element
_currentElement.referenceElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)
//set new matrix to dragging-element
_currentElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)
}
};
//add mouseup for dragging
_Drawing.onmouseup = _Drawing.onmouseleave = function(event){
_currentElement = null
};
//adding dragging boxes to each "rect" and "circle"
document.querySelectorAll('rect, circle').forEach(function(element){
const tElement = element;
//create a "g.Dragging" inside "g.VWS_DragAndDrop"
const tG = _Parent.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'g'));
tG.className = 'Dragging';
tG.setAttributeNS(null, 'transform', 'matrix(1 0 0 1 0 0)')
tG.referenceElement = tElement;
//apply the element's matrix to the new "g.Dragging"
tG.transform.baseVal.getItem(0).setMatrix(tElement.transform.baseVal.getItem(0).matrix)
//create a "rect" to scale the reference-element while dragging that "rect"
const
tBBox = tElement.getBBox(),
tLength = Math.floor(Math.min(tBBox.width, tBBox.height) / 10);
const tRect = tG.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'rect'));
tRect.setAttributeNS(null, 'x', tBBox.x + tBBox.width - tLength);
tRect.setAttributeNS(null, 'y', tBBox.y - tLength*3);
tRect.setAttributeNS(null, 'width', tLength * 4);
tRect.setAttributeNS(null, 'height', tLength * 4)
})
<svg width="800" height="800" style="background-color: aqua">
<style>
.VWS_DragAndDrop rect{
cursor: move;
fill: grey;
stroke: black
}
</style>
<circle cx="150" cy="150" r="15" fill="red" transform="matrix(1 0 0 1 0 0)" />
<rect x="300" y="300" width="40" height="40" fill="red" transform="matrix(1 0 0 1 5 5)" />
<rect x="380" y="320" width="40" height="40" fill="blue" transform="matrix(1 -0.5 0.5 1 0 0)" />
<g class="VWS_DragAndDrop" />
</svg>
我不确定你尝试什么存档,但矩阵根本不旋转
如果你想旋转元素使用旋转 旋转需要 3 个参数: 度数 - 旋转 x - 位置原点 y - 位置原点
const
_Drawing = document.querySelector('svg'),
_Parent = _Drawing.querySelector('.VWS_DragAndDrop');
let
_currentElement = null; //current "g.Dragging"
//add mousedown-event for dragging
_Drawing.onmousedown = function(event){
if(event.target.tagName === 'rect' && event.target.parentNode.parentNode === _Parent){
//set "g.Dragging" as dragging element
_currentElement = event.target.parentNode;
const
tBBoxE = _currentElement.getBBox(),
tBBoxR = _currentElement.referenceElement.getBBox(),
tMatrix = _currentElement.transform.baseVal.getItem(0).matrix;
//values on start to calculate the adjustments on move
_currentElement.referenceOptions = {
startX: event.clientX,
startY: event.clientY,
Bounds: {height: tBBoxR.height, w: tBBoxE.width * tMatrix.a, h: tBBoxE.height * tMatrix.d, x: tBBoxR.x, y: tBBoxR.y},
Matrix: {a: tMatrix.a, b: tMatrix.b, c: tMatrix.c, d: tMatrix.d, e: tMatrix.e, f: tMatrix.f}
}
}
else{
//reset dragging element
_currentElement = null
}
};
//add mousemove for dragging
_Drawing.onmousemove = function(event){
if(_currentElement){
const
tOriginalBBox = _currentElement.referenceOptions.Bounds,
tOriginalMatrix = _currentElement.referenceOptions.Matrix,
tCurrentMatrix = _currentElement.transform.baseVal.getItem(0).matrix,
tNewMatrix = _currentElement.ownerSVGElement.createSVGMatrix(),
tCTM = _currentElement.ownerSVGElement.getScreenCTM()
tC1 = {x: (_currentElement.referenceOptions.startX - tCTM.e) / tCTM.a, y: (_currentElement.referenceOptions.startY - tCTM.f) / tCTM.d},
tC2 = {x: (event.clientX - tCTM.e) / tCTM.a, y: (event.clientY - tCTM.f) / tCTM.d},
tDX = (tC2.x-tC1.x),
tDY = (tC2.y-tC1.y);
//current matrix as default
tNewMatrix.a = tCurrentMatrix.a;
tNewMatrix.b = tCurrentMatrix.b;
tNewMatrix.c = tCurrentMatrix.c;
tNewMatrix.d = tCurrentMatrix.d;
tNewMatrix.e = tCurrentMatrix.e;
tNewMatrix.f = tCurrentMatrix.f;
//adjust matrix around p(bottom-left) while keeping proportions
tNewMatrix.a = Math.max(tOriginalMatrix.a/100*((tOriginalBBox.w+tDX)*100/tOriginalBBox.w), tOriginalMatrix.d/100*((tOriginalBBox.h-tDY)*100/tOriginalBBox.h));
tNewMatrix.d = tNewMatrix.a;
tNewMatrix.e = (tOriginalMatrix.a - tNewMatrix.a) * tOriginalBBox.x + tOriginalMatrix.e; //vertical position
tNewMatrix.f = (tOriginalMatrix.d - tNewMatrix.d) * (tOriginalBBox.y + tOriginalBBox.height) + tOriginalMatrix.f; //horizontal position
//set new matrix to reference-element
_currentElement.referenceElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)
//set new matrix to dragging-element
_currentElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)
}
};
//add mouseup for dragging
_Drawing.onmouseup = _Drawing.onmouseleave = function(event){
_currentElement = null
};
//adding dragging boxes to each "rect" and "circle"
document.querySelectorAll('rect, circle').forEach(function(element){
const tElement = element;
//create a "g.Dragging" inside "g.VWS_DragAndDrop"
const tG = _Parent.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'g'));
tG.className = 'Dragging';
tG.setAttributeNS(null, 'transform', 'matrix(1 0 0 1 0 0)')
tG.referenceElement = tElement;
//apply the element's matrix to the new "g.Dragging"
tG.transform.baseVal.getItem(0).setMatrix(tElement.transform.baseVal.getItem(0).matrix)
//create a "rect" to scale the reference-element while dragging that "rect"
const
tBBox = tElement.getBBox(),
tLength = Math.floor(Math.min(tBBox.width, tBBox.height) / 10);
const tRect = tG.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'rect'));
tRect.setAttributeNS(null, 'x', tBBox.x + tBBox.width - tLength);
tRect.setAttributeNS(null, 'y', tBBox.y - tLength*3);
tRect.setAttributeNS(null, 'width', tLength * 4);
tRect.setAttributeNS(null, 'height', tLength * 4)
})
<svg width="800" height="800" style="background-color: aqua">
<style>
.VWS_DragAndDrop rect{
cursor: move;
fill: grey;
stroke: black
}
</style>
<circle cx="150" cy="150" r="15" fill="red" transform="matrix(1 0 0 1 0 0)" />
<rect x="300" y="300" width="40" height="40" fill="red" transform="matrix(1 0 0 1 5 5)" />
<rect x="380" y="320" width="40" height="40" fill="blue" transform="matrix(1 0 0 1 0 0) rotate(45 400 340)" />
<g class="VWS_DragAndDrop" />
</svg>