Scaling SVG (Raphael.js) like an SWF
Asked Answered
P

5

20

I started using Raphael.js a few days ago and I'm really enjoying it. The only thing I haven't been able to figure out is how to get the "paper" or svg/vml tag to fill the browser window like an swf. See this example.

Note the way the above example resizes with the browser window

I was able to get the "paper" to resize with the browser window, but no luck getting all the vector graphics to adjust their size. Any feedback would be greatly appreciated.

EDIT

I tried a bunch of different routes with this. viewBox worked great but its SVG only. I just figured out how to do it using Raphael sets and a little code on the window.onresize event. I'll post my findings later tonight or tomorrow. I'd still really like to see other solutions to the question if there are any.

Papert answered 1/12, 2010 at 9:0 Comment(0)
P
37

It took me awhile but I finally came up with a solution to this problem. I've wrapped the solution in a small js file that can be used with Raphael. You can get the js file along with some simple documentation here. See it in action.

How it works:

  1. use viewBox for svg
  2. wrap all vml nodes in a group node
  3. wrap the Raphael constructor up so that the vml group node is passed to the Raphael constructor
  4. alter a few css properties when the paper is resized to deal with centering, clipping and maintaining the correct aspect ratio.

Any feedback would be greatly appreciated.

Papert answered 4/12, 2010 at 19:4 Comment(1)
can you only have one instance of ScaleRaphael?? ... I am putting .svg files into thumbnails ... the original .svg's are about 400x400, but the thumbs are 75x75 ... I have been using a unique instance of Raphael( container_id ) for each thumb. ... BUT it seems that when I switch to new ScaleRaphael( container_id , 75, 75), all the thumbs are getting lumped into one visual element.Tanya
E
7

Well hello Zévan,

I found a nice answer, made by a guy called Zevan. However, I found the code overcomplicated for my liking (Required jquery, did wierd stuff like "$(this)" etc).

So I simplified it. It's now short enough to fit into stackoverflow ;):

var paper;

window.ScaleRaphael = function(container, width, height) {
    var wrapper = document.getElementById(container);
    wrapper.style.width = width + "px";
    wrapper.style.height = height + "px";
    wrapper.style.overflow = "hidden";

    wrapper.innerHTML = "<div id='svggroup'><\/div>";
    var nestedWrapper = document.getElementById("svggroup");

    paper = new Raphael(nestedWrapper, width, height);
    paper.w = width;
    paper.h = height;
    paper.canvas.setAttribute("viewBox", "0 0 "+width+" "+height);
    paper.changeSize = function() {
        var w = window.innerWidth
        var h = window.innerHeight
        var ratioW = w / width;
        var ratioH = h / height;
        var scale = ratioW < ratioH ? ratioW : ratioH;

        var newHeight = Math.floor(height * scale);
        var newWidth = Math.floor(width * scale);

        wrapper.style.width = newWidth + "px";
        wrapper.style.height = newHeight + "px";
        paper.setSize(newWidth, newHeight);
    }
    window.onresize = function() {
        paper.changeSize();
    }

    paper.changeSize();

    return paper;
}

The only drawback from your version is that it requires SVG, it doesn't do VML. Is this a problem?

I'm using it with a simplified version of your demo page:

<!DOCTYPE html> 
<html lang="en"> 
<head>
<title>ScaleRaphaël Demo 1</title>
<meta charset="utf-8"> 

<script type="text/javascript" src="raphael.js"></script>
<script type="text/javascript" src="scale.raphael.js"></script>
<script type="text/javascript">

    window.onload = function() {
        var paper = new ScaleRaphael("wrap", 600, 400);

        // draw some random vectors:
        var path = "M " + paper.w / 2 + " " + paper.h / 2;
        for (var i = 0; i < 100; i++){
            var x = Math.random() * paper.w;
            var y = Math.random() * paper.h;
            paper.circle(x, y,
                Math.random() * 60 + 2).
                attr("fill", "rgb("+Math.random() * 255+",0,0)").
                attr("opacity", 0.5);
            path += "L " + x + " " + y + " ";
        }

        paper.path(path).attr("stroke","#ffffff").attr("stroke-opacity", 0.2);

        paper.text(200,100,"Resize the window").attr("font","30px Arial").attr("fill","#ffffff");
    }

      </script>
<style type="text/css">
    body, html {
        margin: 0;
        padding: 0;
        overflow: hidden;
    }

    #wrap{
        background-color: orange;
    }
</style>

</head>
<body>

Ence answered 13/6, 2011 at 20:38 Comment(2)
i know you didn't do vml support, is that why your resize function isn't set up for IE? For me a huge part of Raphael is the VML support... and so the window resize method needs to work in IEPapert
Superb answer and code. THis just saved me an enormous amount of trial and error. Thanks!Brocket
G
2

You could loop over all paths and scale() them acording to the new scale of the paper after resizing it.

Gastro answered 1/12, 2010 at 9:10 Comment(3)
turns out that there is no need to loop if you use a set and do myset.scale(2,2,0,0)Papert
@Zeven Interesting! I didn't look into sets when I used raphaël.Gastro
I spoke to soon though. Unfortunately if you scale a set of objects the coordinate system isn't preserved which can really mess up any animation that might be going on. Meaning the cx and cy change depending on scale. I'm pretty sure this could be avoided if Raphael were using the transform() SVG stuff. Not sure why scale is done manually rather than with built in SVG functionality. Maybe it has something to do with having the code be easily compatable with VML. I'm going to keep looking into it. Worst case I'll write some kind of plug-in.Papert
B
2

You could add a 'viewBox' attribute on the svg root element to define the coordinate system, the shapes will then scale to whatever the size of the container is. Not sure how to deal with the VML side of things though. I'd suggest reporting an issue for it, https://github.com/DmitryBaranovskiy/raphael/issues.

Briticism answered 1/12, 2010 at 12:5 Comment(3)
Cool I'll give this a try - and I'll look into the VML side of things.Papert
This really works great. The equivalent in VML seems to be to wrap all the objects in a group node. I did this and it actually worked nicely. Only problem is that Raphael wasn't able to fill or style the objects anymore. I guess because of the change to the VML structure.Papert
I guess in IE the width and height of the paper could be set to an expression which calculates the dimensions of the parent element.Forebrain
K
2

This might just be too old of a thread, but Raphael 2.0 at least has a setViewBox method -- http://raphaeljs.com/reference.html#Paper.setViewBox

You'll still need to set the actual canvas size to 100%, so the container scales to the window, but calling setViewBox with appropriate w/h in your window.resize callback should do the trick.

Khaddar answered 21/1, 2013 at 3:50 Comment(1)
here's an equally old random example of using setViewBox to "zoom" on mousewheel - jsfiddle.net/9zu4U/2Khaddar

© 2022 - 2024 — McMap. All rights reserved.