Comment déterminer la taille de l'objet Raphael après la mise à l'échelle et la rotation?

Si je devais créer une image comme un objet Raphael.js avec la taille initiale [w1, h1] , puis l'échelle en utilisant la méthode transform() , comment puis-je récupérer sa nouvelle taille [w2, h2] ? Parce que l' image.attr("width") renvoie w1, la largeur initiale, et non la largeur après la transformation.

Je sais que vous pourriez dire que je devrais simplement multiplier w1 et h1 par les facteurs de mise à l'échelle, mais la plupart du temps, une transformation de rotation est également appliquée, ce qui rend les choses un peu plus encombrantes.

Donc, en bref, y a-t-il une méthode dans Raphael.js qui récupère la taille correcte d'un objet, quelles que soient les transformations qui lui ont été appliquées?

Il existe une méthode pour récupérer la zone de délimitation d'un élément avec et sans transformation.

Avec transformation:

 var bbox = image.getBBox(), width = bbox.width, height = bbox.height; 

Sans transformation:

 var bbox = image.getBBoxWOTransform(), width = bbox.width, height = bbox.height; 

Vous pouvez étendre Raphael.el avec des méthodes auxiliaires (si vous faites attention) qui fournissent la largeur et la hauteur directement si cela vous aide. Vous pouvez simplement utiliser la méthode de la zone de délimitation et renvoyer la partie qui vous intéresse, mais pour être un peu plus efficace, j'ai calculé uniquement la propriété demandée en utilisant la propriété matricielle sur les éléments et la position / largeur / hauteur des attributs.

 (function (r) { function getX() { var posX = this.attr("x") || 0, posY = this.attr("y") || 0; return this.matrix.x(posX, posY); } function getY() { var posX = this.attr("x") || 0, posY = this.attr("y") || 0; return this.matrix.y(posX, posY); } function getWidth() { var posX = this.attr("x") || 0, posY = this.attr("y") || 0, maxX = posX + (this.attr("width") || 0), maxY = posY + (this.attr("height") || 0), m = this.matrix, x = [ mx(posX, posY), mx(maxX, posY), mx(maxX, maxY), mx(posX, maxY) ]; return Math.max.apply(Math, x) - Math.min.apply(Math, x); } function getHeight() { var posX = this.attr("x") || 0, posY = this.attr("y") || 0, maxX = posX + (this.attr("width") || 0), maxY = posY + (this.attr("height") || 0), m = this.matrix, y = [ my(posX, posY), my(maxX, posY), my(maxX, maxY), my(posX, maxY) ]; return Math.max.apply(Math, y) - Math.min.apply(Math, y); } r.getX = getX; r.getY = getY; r.getWidth = getWidth; r.getHeight = getHeight; }(Raphael.el)) 

Avec l'utilisation:

 var x = image.getX(); var y = image.getY(); var width = image.getWidth(); var height = image.getHeight(); 

Ajoutez simplement le script après avoir inclus Raphael. Notez qu'il ne fonctionne que pour les éléments avec des attributs largeur, hauteur, x et y, qui conviennent aux images. Raphael calcule effectivement la boîte de délimitation à partir des données du chemin, qui transforme tous les points du chemin et obtient les valeurs min / max x / y après les transformer.

L'exemple de Matt Esch peut fonctionner avec seulement quelques éléments (rectangulaires), mais pour obtenir des métriques de tous les éléments possibles (et aussi dans les cas où l'élément est affecté par les transformations imbriquées des groupes parents), nous devons utiliser une autre approche. Pour obtenir diverses métriques (largeur, hauteur, largeur de bbox, hauteur de bbox, coordonnées d'angle, longueurs latérales, etc.) de l'élément SVG transformé, j'ai fait une fonction get_metrics ():

Exemple fonctionnel complet: http://output.jsbin.com/zuvuborehe/

L'image suivante montre un cas d'utilisation possible de get_metrics ():

Exemple de mesure

La fonction get_metrics() utilise javascript pur, pas de bibliothèques. Mais il peut être utilisé OC avec des bibliothèques, comme Raphaël. Il est basé sur l' element1.getTransformToElement(element2) qui peut obtenir une matrice de relation entre deux éléments, qui sont dans ce cas l'élément transformé (p. Ex. <path> ) et l'élément racine SVG ( <svg> ). Bien sûr, vous pouvez utiliser d'autres éléments également, comme l'image, le polygone, le rectangle etc. En passant, le getTransformToElement est très puissant et polyvalent, il peut être utilisé par exemple. Pour aplatir des transformées de chemin comprenant les commandes de chemin (même les arcs s'ils sont converti en Cubics à l'aide du chemin de Raphaël2curve) de cette façon: http://jsbin.com/atidoh/9 .

 SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) { return elem.getScreenCTM().inverse().multiply(this.getScreenCTM()); }; function get_metrics(el) { function pointToLineDist(A, B, P) { var nL = Math.sqrt((Bx - Ax) * (Bx - Ax) + (By - Ay) * (By - Ay)); return Math.abs((Px - Ax) * (By - Ay) - (Py - Ay) * (Bx - Ax)) / nL; } function dist(point1, point2) { var xs = 0, ys = 0; xs = point2.x - point1.x; xs = xs * xs; ys = point2.y - point1.y; ys = ys * ys; return Math.sqrt(xs + ys); } var b = el.getBBox(), objDOM = el, svgDOM = objDOM.ownerSVGElement; // Get the local to global matrix var matrix = svgDOM.getTransformToElement(objDOM).inverse(), oldp = [[bx, by], [bx + b.width, by], [bx + b.width, by + b.height], [bx, by + b.height]], pt, newp = [], obj = {}, i, pos = Number.POSITIVE_INFINITY, neg = Number.NEGATIVE_INFINITY, minX = pos, minY = pos, maxX = neg, maxY = neg; for (i = 0; i < 4; i++) { pt = svgDOM.createSVGPoint(); pt.x = oldp[i][0]; pt.y = oldp[i][1]; newp[i] = pt.matrixTransform(matrix); if (newp[i].x < minX) minX = newp[i].x; if (newp[i].y < minY) minY = newp[i].y; if (newp[i].x > maxX) maxX = newp[i].x; if (newp[i].y > maxY) maxY = newp[i].y; } // The next refers to the transformed object itself, not bbox // newp[0] - newp[3] are the transformed object's corner // points in clockwise order starting from top left corner obj.newp = newp; // array of corner points obj.width = pointToLineDist(newp[1], newp[2], newp[0]) || 0; obj.height = pointToLineDist(newp[2], newp[3], newp[0]) || 0; obj.toplen = dist(newp[0], newp[1]); obj.rightlen = dist(newp[1], newp[2]); obj.bottomlen = dist(newp[2], newp[3]); obj.leftlen = dist(newp[3], newp[0]); // The next refers to the transformed object's bounding box obj.BBx = minX; obj.BBy = minY; obj.BBx2 = maxX; obj.BBy2 = maxY; obj.BBwidth = maxX - minX; obj.BBheight = maxY - minY; return obj; }