【问题标题】:rotating SVG rect around its center using vanilla JavaScript使用 vanilla JavaScript 围绕其中心旋转 SVG 矩形
【发布时间】:2016-07-19 07:23:49
【问题描述】:

这不是另一个问题的重复。 我发现 this 谈论使用 XML 的中心旋转,尝试使用像 rotate(45, 60, 60) 这样的 vanilla JavaScript 来实现相同的功能,但对我不起作用。

与我一起使用的方法是下面 sn-p 中的方法,但发现矩形没有完全围绕其中心旋转,并且它正在移动一点,矩形应该在第一次点击时开始旋转,并且应该停止第二次点击,我觉得很好。

任何想法,为什么项目正在移动,以及如何修复它。

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,高度增加8px,以及x 和y 被移动了 4 px,我发现 this 在谈论同样的事情,但设置 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 };

第二个选项(计算中点的另一种方法对我来说很好,因为它是矩形,但如果使用其他形状,则方法不一样,我会寻找找到中点的通用方法-point 无论对象是什么,即寻找第一个选项,它正在解决self.getBoundingClientRect() 问题。

【问题讨论】:

  • 这是一个经常被回答的事情……诀窍:平移对象,使旋转/缩放的中心位于原点,旋转,平移回来。所有这三个步骤都可以组合在一个变换矩阵中。
  • 对了……写es6类的时候,不用这个Object.defineProperty(myRect.prototype …,因为可以直接创建类的成员方法,翻译成这个prototype的东西是什么……
  • @philipp 我指的是您提供的链接,在我的问题本身中,它不是重复的,而且这种方式对我不起作用:(关于Object.defineProperty...,我使用它是因为我更喜欢将我的所有声明都放在主 class { } 之外
  • @philipp 问题出在第三步,即当我翻译回来时,它不会回去!

标签: javascript svg


【解决方案1】:

我们开始……

FIDDLE

这里有一些文档代码:

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 :) 我发现我的代码中的错误是使用self.getBoundingClientRect() 计算rect 的中心点,每边总是多出4px,这意味着多出8px在宽度和高度中额外增加 8px,以及 x 和 y 都移动了 4 px,当我以与您相同的方式重新计算中心点时,我的代码运行良好,我看到了 stackoverflow.com/a/8600771/2441637 但设置 @987654327 @ 或 self.style.display = "block"; 还没有解决我的问题。
  • 我根据您的回答更新了您的答案以包含我调整后的代码,一旦您批准编辑,我会将您的答案标记为正确且完整的答案,谢谢。
  • 看不到任何变化……
  • @phiiipp 这真的很奇怪,该编辑已被 5 个人拒绝,原因是:“此编辑旨在解决帖子的作者,作为编辑毫无意义。有更好的答案?请写一个新答案。”,不知道你能不能看到这个:stackoverflow.com/review/suggested-edits/13059954
  • @HasanAYousef 我看了你的编辑,老实说我理解拒绝,因为其中有更多的代码,这是动画和其他交互所需要的,但不是问题的核心。我认为好的问题和答案集中在一个/主要问题上,即如何围绕任意点旋转 svg 元素,周期。其他所有内容都是针对您的应用程序的装饰和特定的,因此对于将来寻找解决此问题的解决方案的读者来说,这不是核心兴趣。
【解决方案2】:

感谢@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);
}

【讨论】:

    猜你喜欢
    • 2015-03-29
    • 1970-01-01
    • 1970-01-01
    • 2012-04-08
    • 2013-02-14
    • 2018-08-24
    • 1970-01-01
    • 2017-09-21
    • 1970-01-01
    相关资源
    最近更新 更多