如何在缩放和旋转后确定Raphael对象的大小?

And*_*iga 2 javascript svg raphael

如果我使用初始大小[w1,h1]创建一个Raphael.js对象的图像,然后使用该transform()方法缩放它,我将如何检索它的新大小[w2,h2]?因为调用image.attr("width")返回w1,初始宽度,而不是转换后的宽度.

我知道你可能会说我应该简单地将w1和h1与缩放因子相乘,但是大多数时候,也会应用旋转变换,这会让事情变得更加麻烦.

简而言之,Raphael.js中是否有一个方法可以检索对象的正确大小,而不管可能应用于它的任何转换?

Tim*_*nen 11

Matt Esch的示例可能仅适用于某些(矩形)元素,但要获取所有可能元素的度量(以及在元素受父组嵌套转换影响的情况下),我们必须使用其他方法.为了得到各种变换的SVG元素的各种度量(宽度,高度,bbox宽度,bbox高度,角坐标,边长等),我做了一个get_metrics() - 函数:

功能齐全的例子:http://output.jsbin.com/zuvuborehe/

下图显示了get_metrics()的一个可能用例:

指标示例

该函数get_metrics()使用纯javascript,没有库.但它可以与像Raphaël这样的库一起使用.它基于native element1.getTransformToElement(element2),可以获得两个元素之间的关系矩阵,在这种情况下是转换元素(例如<path>)和SVG根元素(<svg>).当然你也可以使用其他元素,如图像,多边形,矩形等.顺便说一句,getTransformToElement它非常强大和多功能,它可以用于例如.通过这种方式展平由任何路径命令组成的路径变换(如果它们首先使用Raphaël的path2曲线转换为Cubics,则为偶数弧):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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我用polyfill更新了答案. (2认同)

Mat*_*sch 5

有一种方法可以检索带有和不带变换的元素的边界框.

随着转型:

var bbox = image.getBBox(),
    width = bbox.width,
    height = bbox.height;
Run Code Online (Sandbox Code Playgroud)

没有转变:

var bbox = image.getBBoxWOTransform(),
    width = bbox.width,
    height = bbox.height;
Run Code Online (Sandbox Code Playgroud)

您可以使用辅助方法扩展Raphael.el(如果您小心的话),如果这对您有帮助,可以直接提供宽度和高度.您可以使用边界框方法并返回您感兴趣的部分,但为了更高效,我使用元素上的矩阵属性和属性的位置/宽度/高度仅计算了请求的属性.

(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))
Run Code Online (Sandbox Code Playgroud)

用法:

var x = image.getX();
var y = image.getY();
var width = image.getWidth();
var height = image.getHeight();
Run Code Online (Sandbox Code Playgroud)

在您加入Raphael之后,只需添加脚本即可.请注意,它仅适用于具有宽度,高度,x和y属性的元素,适用于图像.Raphael实际上是根据路径数据计算边界框,它会转换路径中的所有点,并在转换它们后获取最小/最大x/y值.