如何将svg图形组缩放到所需的大小(不是因子)?

时间:2011-08-06 09:58:13

标签: svg

我有svg图形组(我的意思是路径,recatangles等)。我想将组缩放到一定的大小,例如70,70。我知道svg提供了比例变换,但是比例变换需要我不知道的比例因子,因为组可以有很多形状,并且不容易确定整个组的边界大小。
我玩了svg elemenent的viewBox参数和preserveAspectRatio参数,但是我无法获得所需的结果(比例组为70,70)。
也许是可以用maxtrix变换来缩放组还是以其他方式存在?

2 个答案:

答案 0 :(得分:7)

您可以使用getBBox()功能检索图形的当前大小,然后使用宽度和高度来计算应该应用于图形以使其适合所需大小的比例因子。

在下面的示例中, g1 组中的图形将缩放为适合宽度和高度设置为70的矩形。

<?xml version="1.0" encoding="UTF-8" ?>
<svg version="1.2" viewBox="0 0 480 240" width="480" height="240" xmlns="http://www.w3.org/2000/svg" >
 <g id="g1">
    <rect x="20" y="20" width="100" height="100" fill="red" />
    <rect x="40" y="60" width="100" height="100" fill="blue" />
 </g>
 <script type="application/ecmascript"> 

    var width=70, height=70;
    var node = document.getElementById("g1");
    var bb = node.getBBox();
    var matrix = "matrix("+width / bb.width+", 0, 0, "+height / bb.height+", 0,0)";
    node.setAttribute("transform", matrix);

 </script>
</svg>

答案 1 :(得分:0)

如果已经应用path \ rect转换,您可能会遇到一些使SVG元素适合矩形范围的问题。

下面的代码演示了如何适应将分组,倾斜,缩放和变换为边界的变换后的反应输入。您必须保留现有的转换并应用新的转换。

代码正在执行以下操作:

  • 计算屏幕坐标的比例尺和位置偏移(在这种情况下,在计算所有转换的情况下,屏幕坐标)
  • 创建一个新矩阵,首先应用变换偏移(移至目标的中心)。
  • 应用新的缩放比例,将元素的中心作为转换的起点。
  • 虽然应该在屏幕坐标上进行缩放,但我们应该将当前元素转换(包括所有父转换)转换为屏幕坐标,将所有具有给定比例的转换转换为屏幕坐标。

确切的行是:

var toScreenMatrix = inputElement.getScreenCTM();
// Scale element by a matrix in screen coordinates and convert it back to the element coordinates:
currentMatrix = currentMatrix.multiply(toScreenMatrix.inverse().multiply(scaleAndTransform).multiply(toScreenMatrix));
    

此代码对于所有svg元素都是通用的,因此任何形状都可以适合给定的rect:

    function fitElement(from, to, changePosition) {
        var inputElement = document.getElementById(from);
        var destinationElement = document.getElementById(to);
        // Get center of figure in element coordinates:
        var inputScreenBBox = inputElement.getBoundingClientRect();
        var destinationScreenBBox = destinationElement.getBoundingClientRect();
        var scaleX = destinationScreenBBox.width / inputScreenBBox.width;
        var scaleY = destinationScreenBBox.height / inputScreenBBox.height;

        var inputCenter = getCenter(inputScreenBBox);
        var offsetX = 0;
        var offsetY = 0;
        if (changePosition) {
            var destCenter = getCenter(destinationScreenBBox);
            offsetX = destCenter.x - inputCenter.x;
            offsetY = destCenter.y - inputCenter.y;
        }

        // create scale matrix:
        var scaleMatrix = getScaleMatrix(scaleX, scaleY, inputElement);
        // get element self transformation matrix:
        var currentMatrix = getElementMatrix(inputElement);

        scaleAndTransform = inputElement.ownerSVGElement.createSVGMatrix()
            // multiply is used instead of the scale method while for some reasons matrix scale is giving proportional scaling...
            // From a transforms proper matrix is generated.
            .translate(offsetX, offsetY)
            // Scale in screen coordinates around screen center:
            .translate(inputCenter.x, inputCenter.y)
            .multiply(scaleMatrix)
            .translate(-inputCenter.x, -inputCenter.y)
        
        var toScreenMatrix = inputElement.getScreenCTM();
        // Scale element by a matrix in screen coordinates and convert it back to the element coordinates:
        currentMatrix = currentMatrix.multiply(toScreenMatrix.inverse().multiply(scaleAndTransform).multiply(toScreenMatrix));
        // Apply new created transform:
        var newTransform = inputElement.ownerSVGElement.createSVGTransform();
        newTransform.setMatrix(currentMatrix);
        inputElement.transform.baseVal.initialize(newTransform);

    }
    function getElementMatrix(element) {
        // Get consolidated element matrix:
        var currentMatrix =
            (element.transform.baseVal.consolidate() ||
                element.ownerSVGElement.createSVGTransform()).matrix;
        return currentMatrix;
    }
    function getScaleMatrix(scaleX, scaleY, el) {
        // Return DOM matrix
        var svgTransform = el.ownerSVGElement.createSVGTransform();
        // Transform type is used because of the bug in chrome applying scale to the DOM matrix:
        svgTransform.setScale(scaleX, scaleY);
        var scaleMatrix = svgTransform.matrix;
        return scaleMatrix
    }

    function getCenter(rect) {
        return new DOMPoint((rect.x + rect.width / 2), (rect.y + rect.height / 2));
    }

    fitElement('source', 'destination', true);
<svg width="1380" height="1340" xmlns="http://www.w3.org/2000/svg">
<g transform="skewX(10) translate(-3,4) rotate(30)">
<g transform="skewX(30) translate(-3,4) rotate(30)">
<g transform="skewX(10) translate(-3,4) rotate(10)">
<g transform="translate(350,30) skewX(10) rotate(30)">
<rect id="source" transform="scale(2) rotate(30) skewX(10)" x="20" y="50" width="30" height="30"
fill="red" />
</g>
</g>
</g>
</g>
<rect id="destination" x="30" y="30" width="120" height="100" fill="transparent" stroke="blue" />
</svg>

GitHub gist link