Arrastrar y soltar en SVG (2)

facebook-svg gplus-svg twitter-svg

Esta es una continuación de Arrastrar y soltar en SVG. En aquel ejemplo he dibujado un rectángulo SVG y después, utilizando eventos del ratón lo he arrastrado y soltado por ahí. A continuación quiero hacer lo mismo, pero esta vez quiero arrastrar varios trazados. Para esto voy a utilizar el código del ejemplo anterior que voy a modificar en parte, solo allá donde fuera necesario. Ya que quiero trabajar con varios objetos, voy a declarar unas nuevas variables
var oTrazados que es un array, el array de los objetos (trazados) que quiero dibujar en el lienzo SVG y
var svgRaton que representa la posición del ratón encima del lienzo SVG

var oTrazados = []; 
var svgRaton = {x:0, y:0};

Además voy a declarar la variable: var trazados que contiene el valor de los atributos de cada elemento ( att ) y el nombre de etiqueta ( nombreEtiqueta ).


var trazados = [
  {att:{cx:225, cy:175, rx:70,ry:50,fill:"#2a80b9"},
  nombreEtiqueta:"ellipse"},
  {att:{cx:280,cy:250,r:60,fill:"#16a086"},
  nombreEtiqueta:"circle"},
  {att:{x:70,y:70,width:125,height:90,fill:"#e77e23"},
  nombreEtiqueta:"rect"},
  {att:{points:"134,128 209,288 59,288",fill:"#f1c40f"},
  nombreEtiqueta:"polygon"},
  {att:{d:"M135,220C135,175 170,125 205,140C240,155 210,225 190,225C180,225 180,205 160,205C150,205 147,225 142,225C140,225 135,225 135,220Z", fill:"#8f44ad"},
  nombreEtiqueta:"path"}
];

La función Trazado()

La función Rect() del ejemplo anterior pasa a llamarse Trazado() y tiene unas nuevas propiedades:
this.nombreEtiqueta Si el trazado que quiero dibujar es un rectángulo this.nombreEtiqueta = "rect"; y si es un circulo this.nombreEtiqueta = "circle" etc. . .
this.rectRaton: representa la posición del ratón encima del rectángulo
this.distInicial: representa la posición inicial del rectángulo

El método dibujar() queda casi sin cambiar. Digo "casi" porque ahora para crear un nuevo elemento SVG utilizo la propiedad nombreEtiqueta en lugar de "rect":

this.dibujar = function(elementoPadre) {
      var elmt = document.createElementNS(SVG_NS, this.nombreEtiqueta);
      for (var name in this.att) {
        if (this.att.hasOwnProperty(name)) {
          elmt.setAttributeNS(null, name, this.att[name]);
        }
      }
      elementoPadre.appendChild(elmt);
      this.elemento = elmt;
}

El método actualizar() queda igual:


this.actualizar = function(x,y){
    this.elemento.setAttributeNS(null, "transform", "translate("+x+","+y+")");
}

Y ya la tenemos:


function Trazado() {
  this.att = {};
  this.elemento;
  
  this.rectRaton = {x:0, y:0};// la posición del ratón encima del trazado
  this.distInicial = {x:0, y:0};// la posición inicial del trazado
  
  this.nombreEtiqueta = "";
  
  this.dibujar = function(elementoPadre) {
    var elmt = document.createElementNS(SVG_NS, this.nombreEtiqueta);
    for (var name in this.att) {
      if (this.att.hasOwnProperty(name)) {
        elmt.setAttributeNS(null, name, this.att[name]);
      }
    }
    elementoPadre.appendChild(elmt);
    this.elemento = elmt;
  }
  
  this.actualizar = function(x,y){
    this.elemento.setAttributeNS(null, "transform", "translate("+x+","+y+")");
  }  
}//function Trazado()

Crear los nuevos trazados

Ahora que ya lo tenemos todo vamos a crear uno a uno los trazados.


for( var i = 0; i < trazados.length; i++ ){
  //crea un nuevo objeto Trazado
  var oTrazado = new Trazado();
  // establece los atributos del nuevo trazado
  oTrazado.att = trazados[i].att;
  // y el nombre de la etiqueta: rect, ellipse....
  oTrazado.nombreEtiqueta = trazados[i].nombreEtiqueta;  
  // dibuja el nuevo trazado
  oTrazado.dibujar(svg);
  // lo guarda en el array de los trazados: oTrazados
  oTrazados.push(oTrazado);
}

Los eventos del ratón

En lo que concierne los eventos del ratón utilizados, estos son los mismos, y hacen lo mismo que en el ejemplo anterior. La única diferencia es que ahora en lugar de un solo rectángulo tenemos un array de trazados.

var t = svg.querySelectorAll("*");

Así que necesitamos utilizar un bucle for, y esto puede ser un poco tramposo si se trata de eventos. Básicamente para que esto funcione tenemos que poner los eventos dentro de una función anónima auto-ejecutable ( self-invoking function ) que toma como argumento al index n.

for(var n = 0; n < t.length; n++){
    (function(n) {
    // aquí van los eventos
    }(n));
}

for(var n = 0; n < t.length; n++){
    (function(n) {
     var num = 0;// un contador
     t[n].addEventListener("mousedown", function(evt){
       num++
       svgRaton = oMousePos(svg, evt); 
       oTrazados[n].rectRaton = oMousePos(this, evt);
       
       if(num == 1){// calculado solo una vez, en el primer clic encima del trazado
       // recupera la posición inicial del trazado
       oTrazados[n].distInicial.x = svgRaton.x - oTrazados[n].rectRaton.x;
       oTrazados[n].distInicial.y = svgRaton.y - oTrazados[n].rectRaton.y;
       }
       arrastrar = true;
     }, false);
      
     t[n].addEventListener("mousemove", function(evt) {
       if (arrastrar) {
        svgRaton = oMousePos(svg, evt);
         var x = svgRaton.x - oTrazados[n].rectRaton.x - oTrazados[n].distInicial.x;
         var y = svgRaton.y - oTrazados[n].rectRaton.y - oTrazados[n].distInicial.y;
        oTrazados[n].actualizar( x, y);  
       }
       }, false);
       
       t[n].addEventListener("mouseup", function(evt) {
       arrastrar = false;
       }, false);

       t[n].addEventListener("mouseout", function(evt) {
       arrastrar = false;
       }, false); 
          
    }(n));
}

Vea este ejemplo en codepen.io

Dale al botón fork, manoséalo y cambie todo lo que se le ocurra.

See the Pen SVG drag shapeS translate* by Gabi (@enxaneta) on CodePen.