Raphael path resize and move relative to container

2020-04-20 03:21发布

问题:

I am trying to scale/move an SVG path created with the Raphael api. I want the path to fit neatly within a container, no matter the size of the container. I have searched the reference, the web and I'm still struggling to get this to work.

If anyone can tell me why this isn't working, I would be very happy.

This fiddle shows you what I'm doing: http://jsfiddle.net/tolund/3XPxL/5/

JavaScript:

var draw = function(size) {
    var $container = $("#container").empty();
    $container.css("height",size+"px").css("width",size+"px");

    var paper = Raphael("container");

    var pathStr = "M11.166,23.963L22.359,17.5c1.43-0.824,1.43-2.175,"+
        "0-3L11.166,8.037c-1.429-0.826-2.598-0.15-2.598,"+
        "1.5v12.926C8.568,24.113,9.737,24.789,11.166,23.963z";

    // set the viewbox to same size as container
    paper.setViewBox(0, 0, $container.width(), $container.height(), true);

    // create the path
    var path = paper.path(pathStr)
        .attr({ fill: "#000", "stroke-width": 0, "stroke-linejoin": "round", opacity: 1 });

    // get the path outline box (may be different size than view box.
    var box = path.getBBox();

    // move the path as much to the top/left as possible within container
    path.transform("t" + 0 + "," + 0);

    // get the width/height based on path box relative to paper (same size as container)
    var w = (paper.width) / box.width;
    var h = (paper.height) / box.height;

    // scale the path to the container (use "..." to compound transforms)
    path.transform('...S' + w + ',' + h + ',0,0');
}

$(function() {
    var currentSize = 30;
    draw(currentSize);

    $("#smaller").click(function(){ 
        currentSize = currentSize < 10 ? currentSize : currentSize * 0.5;
        draw(currentSize);
    });
    $("#bigger").click(function(){ 
        currentSize = 300 < currentSize ? currentSize : currentSize * 2; 
        draw(currentSize);
    });
});

HTML:

<button id="smaller">-</button>
<button id="bigger">+</button>

<div id="container" style="border: 1px #ddd solid; margin: 30px">

</div>

Thanks, Torgeir.

回答1:

I think your problem is a fundamental misunderstanding of what the viewbox is useful for. In your code, you're attempting to set the viewbox of the svg element so that it matches the coordinate space of the screen, and then transform the path to match that coordinate space. There's no technical reason you can't do this, but it does effectively take the "Scalable" out of "Scalable Vector Graphics." The entire point of the viewbox is to make the translation between the vector coordinate space and the screen relative.

The best way to solve your problem, then, is not to transform the path to match the SVG element, but to use the viewbox to let SVG's intrinsic scalability do this for you.

First things first: create your path so we have an object to work with. We don't care what the viewbox is at this point.

var pathStr = "...";  // The content of the path and its coordinates are completely immaterial

var path = paper.path(pathStr)
    .attr({ fill: "#000", "stroke-width": 0, "stroke-linejoin": "round", opacity: 1 });

Now all we need to do is use the viewbox to "focus" the SVG on the coordinate space we're interested in.

var box = path.getBBox();    
var margin = Math.max( box.width, box.height ) * 0.1;  //  because white space always looks nice ;-)
paper.setViewBox(box.x - margin, box.y - margin, box.width + margin * 2, box.height + margin * 2);   

And that's it. The SVG (regardless of its size) will translate from the internal coordinates specified in the viewbox to its physical coordinates on screen.

Here's a fork of your fiddle as proof-of-concept.