使用vanilla JavaScript

时间:2016-07-19 07:23:49

标签: javascript svg

这不是另一个问题的重复。 我发现this使用XML讨论了关于中心的轮换问题,尝试使用类似rotate(45, 60, 60)的vanilla 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);
}
&#13;
&#13;
&#13;

更新

我发现问题是使用rect计算self.getBoundingClientRect()的中心点,总是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 };

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

2 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:0)

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