How to determine size of Raphael object after scaling & rotating it?

前端 未结 2 907
灰色年华
灰色年华 2020-12-03 20:33

If I were to create an image as a Raphael.js object with the initial size [w1,h1], then scale it using the transform() method, how would I afte

相关标签:
2条回答
  • 2020-12-03 20:45

    The Matt Esch's example may work with only some (rectangular) elements, but to get metrics of all possible elements (and also in cases where element is affected by nested transformations of parent groups) we have to use an other approach. To get various metrics (width, height, bbox width, bbox height, corner coordinates, side lengths etc.) of whichever transformed SVG element I made a get_metrics()-function:

    Full functional example: http://output.jsbin.com/zuvuborehe/

    The following image shows one possible use case of get_metrics():

    Metrics example

    The function get_metrics() uses pure javascript, no libraries. But it can OC be used with libraries, like Raphaël. It is based on native element1.getTransformToElement(element2) which can get relation matrix between two elements, which are in this case the transformed element (eg. <path>) and SVG root element (<svg>). Of course you can use other elements also, like image, polygon, rectangle etc. By the way, the getTransformToElement is very powerful and versatile, it can be used eg. to flatten transforms of path consisting of whichever path commands ( even arcs if they are first converted to Cubics using Raphaël's path2curve) this way: 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((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
            return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / 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 = [[b.x, b.y], [b.x + b.width, b.y], [b.x + b.width, b.y + b.height], [b.x, b.y + 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;
    }
    
    0 讨论(0)
  • 2020-12-03 20:51

    There is a method for retrieving the bounding box of an element with and without transformation.

    With transformation:

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

    Without transformation:

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

    You can extend Raphael.el with helper methods (if you are careful) that provide the width and height directly if this helps you. You could just use the bounding box method and return the portion that you're interested in, but to be a bit more efficient I have calculated only the requested property using the matrix property on the elements and the position/width/height from attributes.

    (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 = [
                    m.x(posX, posY),
                    m.x(maxX, posY),
                    m.x(maxX, maxY),
                    m.x(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 = [
                    m.y(posX, posY),
                    m.y(maxX, posY),
                    m.y(maxX, maxY),
                    m.y(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))
    

    With usage:

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

    Just include the script after you include Raphael. Note that it only works for elements with a width, height, x and y attributes, which is suitable for images. Raphael actually computes the bounding box from the path data, which transforms all of the points in the path and gets the min/max x/y values after transforming them.

    0 讨论(0)
提交回复
热议问题