使用香草JavaScript围绕其中心旋转SVG矩形
rotating SVG rect around its center using vanilla JavaScript
这不是另一个问题的重复。我发现这是在谈论使用XML的中心旋转,并试图使用类似rotate(45, 60, 60)
的普通JavaScript实现同样的功能,但对我来说不起作用
我使用的方法是下面片段中的方法,但发现矩形并没有完全围绕其中心旋转,而且它有点移动,矩形应该在第一次点击时开始旋转,并且应该在第二次点击时停止,这对我来说很好。
任何想法,为什么项目移动,以及我如何修复它。
var NS="http://www.w3.org/2000/svg";
var SVG=function(el){
return document.createElementNS(NS,el);
}
var svg = SVG("svg");
svg.width='100%';
svg.height='100%';
document.body.appendChild(svg);
class myRect {
constructor(x,y,h,w,fill) {
this.SVGObj= SVG('rect'); // document.createElementNS(NS,"rect");
self = this.SVGObj;
self.x.baseVal.value=x;
self.y.baseVal.value=y;
self.width.baseVal.value=w;
self.height.baseVal.value=h;
self.style.fill=fill;
self.onclick="click(evt)";
self.addEventListener("click",this,false);
}
}
Object.defineProperty(myRect.prototype, "node", {
get: function(){ return this.SVGObj;}
});
Object.defineProperty(myRect.prototype, "CenterPoint", {
get: function(){
var self = this.SVGObj;
self.bbox = self.getBoundingClientRect(); // returned only after the item is drawn
self.Pc = {
x: self.bbox.left + self.bbox.width/2,
y: self.bbox.top + self.bbox.height/2
};
return self.Pc;
}
});
myRect.prototype.handleEvent= function(evt){
self = evt.target; // this returns the `rect` element
this.cntr = this.CenterPoint; // backup the origional center point Pc
this.r =5;
switch (evt.type){
case "click":
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true){
self.move = setInterval(()=>this.animate(),100);
}
else{
clearInterval(self.move);
}
break;
default:
break;
}
}
myRect.prototype.step = function(x,y) {
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().translate(x,y));
}
myRect.prototype.rotate = function(r) {
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().rotate(r));
}
myRect.prototype.animate = function() {
self = this.SVGObj;
self.transform.baseVal.appendItem(this.step(this.cntr.x,this.cntr.y));
self.transform.baseVal.appendItem(this.rotate(this.r));
self.transform.baseVal.appendItem(this.step(-this.cntr.x,-this.cntr.y));
};
for (var i = 0; i < 10; i++) {
var x = Math.random() * 100,
y = Math.random() * 300;
var r= new myRect(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16));
svg.appendChild(r.node);
}
更新
我发现问题是使用self.getBoundingClientRect()
计算rect
的中心点,总是有4px extra in each side
,这意味着宽度增加了8px,高度增加了8pt,并且x和y都偏移了4 px,我发现这是一样的,但设置self.setAttribute("display", "block");
或self.style.display = "block";
都不适用。
所以,现在我有两种选择之一:
- 找到每侧额外4px的解决方案(即x和y的4px偏移,宽度和高度总共额外8px)
-
或使用计算中点
self.Pc = { x: self.x.baseVal.value + self.width.baseVal.value/2, y: self.y.baseVal.value + self.height.baseVal.value/2 };
第二个选项(计算中点的另一种方法对我来说很好,因为它是矩形的,但如果使用其他形状,那就不一样了,我会寻找通用的方法来找到中点,无论对象是什么,也就是寻找第一个选项,这就是解决self.getBoundingClientRect()
问题。
开始…
小提琴
这里的一些文档代码:
let SVG = ((root) => {
let ns = root.getAttribute('xmlns');
return {
e (tag) {
return document.createElementNS(ns, tag);
},
add (e) {
return root.appendChild(e)
},
matrix () {
return root.createSVGMatrix();
},
transform () {
return root.createSVGTransformFromMatrix(this.matrix());
}
}
})(document.querySelector('svg.stage'));
class Rectangle {
constructor (x,y,w,h) {
this.node = SVG.add(SVG.e('rect'));
this.node.x.baseVal.value = x;
this.node.y.baseVal.value = y;
this.node.width.baseVal.value = w;
this.node.height.baseVal.value = h;
this.node.transform.baseVal.initialize(SVG.transform());
}
rotate (gamma, x, y) {
let t = this.node.transform.baseVal.getItem(0),
m1 = SVG.matrix().translate(-x, -y),
m2 = SVG.matrix().rotate(gamma),
m3 = SVG.matrix().translate(x, y),
mtr = t.matrix.multiply(m3).multiply(m2).multiply(m1);
this.node.transform.baseVal.getItem(0).setMatrix(mtr);
}
}
谢谢@Philipp,
可以通过以下任一方式解决捕捉SVG中心的问题:
-
使用
.getBoundingClientRect()
和考虑4px的尺寸调整在每侧都是额外的,因此需要调整的结果为:BoxC = self.getBoundingClientRect(); Pc = { x: (BoxC.left - 4) + (BoxC.width - 8)/2, y: (BoxC.top - 4) + (BoxC.height - 8)/2 };
或通过:
-
将
.(x/y).baseVal.value
捕获为:Pc = { x: self.x.baseVal.value + self.width.baseVal.value/2, y: self.y.baseVal.value + self.height.baseVal.value/2 };
下面是完整的运行代码:
let ns="http://www.w3.org/2000/svg";
var root = document.createElementNS(ns, "svg");
root.style.width='100%';
root.style.height='100%';
root.style.backgroundColor = 'green';
document.body.appendChild(root);
//let SVG = function() {}; // let SVG = new Object(); //let SVG = {};
class SVG {};
SVG.matrix = (()=> { return root.createSVGMatrix(); });
SVG.transform = (()=> { return root.createSVGTransformFromMatrix(SVG.matrix()); });
SVG.translate = ((x,y)=> { return SVG.matrix().translate(x,y) });
SVG.rotate = ((r)=> { return SVG.matrix().rotate(r); });
class Rectangle {
constructor (x,y,w,h,fill) {
this.node = document.createElementNS(ns, 'rect');
self = this.node;
self.x.baseVal.value = x;
self.y.baseVal.value = y;
self.width.baseVal.value = w;
self.height.baseVal.value = h;
self.style.fill=fill;
self.transform.baseVal.initialize(SVG.transform()); // to generate transform list
this.transform = self.transform.baseVal.getItem(0), // to be able to read the matrix
this.node.addEventListener("click",this,false);
}
}
Object.defineProperty(Rectangle.prototype, "draw", {
get: function(){ return this.node;}
});
Object.defineProperty(Rectangle.prototype, "CenterPoint", {
get: function(){
var self = this.node;
self.bbox = self.getBoundingClientRect(); // There is 4px shift in each side
self.bboxC = {
x: (self.bbox.left - 4) + (self.bbox.width - 8)/2,
y: (self.bbox.top - 4) + (self.bbox.height - 8)/2
};
// another option is:
self.Pc = {
x: self.x.baseVal.value + self.width.baseVal.value/2,
y: self.y.baseVal.value + self.height.baseVal.value/2
};
return self.bboxC;
// return self.Pc; // will give same output of bboxC
}
});
Rectangle.prototype.animate = function () {
let move01 = SVG.translate(this.CenterPoint.x,this.CenterPoint.y),
move02 = SVG.rotate(10),
move03 = SVG.translate(-this.CenterPoint.x,-this.CenterPoint.y);
movement = this.transform.matrix.multiply(move01).multiply(move02).multiply(move03);
this.transform.setMatrix(movement);
}
Rectangle.prototype.handleEvent= function(evt){
self = evt.target; // this returns the `rect` element
switch (evt.type){
case "click":
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true){
self.move = setInterval(()=>this.animate(),100);
}
else{
clearInterval(self.move);
}
break;
default:
break;
}
}
for (var i = 0; i < 10; i++) {
var x = Math.random() * 100,
y = Math.random() * 300;
var r= new Rectangle(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16));
root.appendChild(r.draw);
}
- 顺时针旋转Svg元件
- 在轴上旋转SVG图像
- 获取以屏幕像素为单位的旋转SVG元素的边界
- SVG具有旋转图像的低性能
- JS with SVG:围绕其中心旋转SVG元素
- 如何在svg中添加围绕整个圆的笔划和旋转圆的动画
- 使用D3.js旋转外部SVG文件
- 拉伸和旋转SVG图像的特定部分
- D3.js-围绕一个点旋转svg文本
- 在 svg (Raphael.js) 上调整大小和旋转会产生跳跃
- 完全旋转 1 次后暂停 SVG 动画
- 在 SVG 动画中围绕舞台原点以外的其他位置旋转路径
- 使用 Snap.svg 沿路径放置和旋转 svg 元素
- 围绕其中心 d3 旋转 svg 路径
- 使用Google Maps API旋转SVG符号以匹配飞机航向
- 旋转SVG文本围绕一个圆圈,然后围绕自己使用d3
- 在JavaScript中旋转SVG组元素的位置,而不旋转整个图像
- 使用香草JavaScript围绕其中心旋转SVG矩形
- JavaScript 围绕特定点 'transform: rotate()' 旋转 SVG 对象
- 360度旋转SVG元素没有任何作用