Use canvas transformations to create a zoom effect

  Read comments... Posted on 18th November 2012

Summary
A demonstration of how you can use the canvas transformation functions to create a zoom effect

[No canvas support]

RGraph used to have a thumbnail zoom effect and this was based on the toDataURL() method to get a data: URL representation of the canvas (ie an image) and then stretch the image position it correctly over the canvas to give the impression of zoom.

This is an alternative way of implementing much the same thing but instead of the toDataURL() method it uses canvas transformations: the clip(), scale() and translate() methods. You can see an example of it in action with the chart to the right.


1. The Original canvas

[No canvas support]

This is the original chart - unmodified. Its a straight-forward Bar chart. The code for creating the chart is quite short - as shown below


<script>
    var obj = new RGraph.Bar({
        id: 'cvs',
        data: [8,3,9,5,6,8],
        options: {
            labels: ['Rich','Pete','Jane','Luis','Fred','Joe']
        }
    }).draw();
</script>

2. Add a semi-transparent cover

[No canvas support]

Also straight-forward is the addition of a semi transparent cover.


<script>
    var obj = new RGraph.Bar({
        id: 'cvs',
        data: [8,3,9,5,6,8],
        options: {
            labels: ['Rich','Pete','Jane','Luis','Fred','Joe']
        }
    }).draw();

    obj.context.fillStyle = 'rgba(255,255,255,0.9)';
    obj.context.fillRect(0,0,600,250);
</script>

3. The mousemove event

This is where the zoom effect is created. A path is created (a circle though you could use a rectangle if you prefer) and the canvas is clip()ped to it - so any further drawing on the canvas will only show up in that circle. This produces the chart at the top of the page. Read the comments in the code for more


<script>
    /**
    * Draw the chart in this function
    */
    function DrawChart ()
    {
        var obj = new RGraph.Bar({
            id: 'cvs',
            data: [8,3,9,5,6,8],
            options: {
                labels: ['Rich','Pete','Jane','Luis','Fred','Joe']
            }
        }).draw();
        
        return obj;
    }
    
    /**
    * Draw the cover that goes over the chart
    */
    function DrawCover (obj)
    {
        obj.context.fillStyle = 'rgba(255,255,255,0.9)';
        obj.context.fillRect(0,0,600,250);
    }
    
    // Load the image that is used as the magnifying glass frame - you will need to put this on your
    // own server
    var img = new Image();
    img.src = '/images/magnifying-glass2.png';
    
    var obj = DrawChart();
    
    // You could omit this so that when you move the mouse away from the canvas it isn't covered (also
    // see below)
    DrawCover(obj);

    obj.canvas.onmousemove = function (e)
    {
        // Set the radius of the zoomed circle
        var radius = 55;
        
        // Get a reference to the canvas context
        var context = e.target.getContext('2d');
    
        // Each time the mouse is moved (and thus this event runs) this clears the canvas so that we
        // can start afresh with a blank canvas
        RGraph.Clear(e.target);

        // Get the coordinates of the mouse position
        var mouseXY = RGraph.getMouseXY(e);
        
        // Draw the chart and draw the cover over it
        var obj = DrawChart();
        DrawCover(obj);
        
        // Save the state so that we can restore() it when we're done
        context.save();

            // Draw a circle and clip to it - fill it white so that the chart in the background doesn't
            // show through
            context.beginPath();
                context.fillStyle = 'white';
                context.arc(mouseXY[0], mouseXY[1], radius, 0, 2 * Math.PI, false);
            context.fill();

            // Clip the drawing are so that any further drawing is restricted to the current path (as
            // just drawn)
            context.clip();
            
            // Draw a circle outside of the clip()ped area with some shadow - the circle will not be
            // visible but its
            // shadow will extend into the clipped area
            context.beginPath();
                context.lineWidth = 10;
                RGraph.setShadow({'context': context}, 'black', 0, 0, 15)
                context.arc(mouseXY[0], mouseXY[1], radius + 5, 0, 2 * Math.PI, false);
            context.stroke();
            
            // Turn the shadow off again
            RGraph.noShadow({'context':context});
    
            // Save the state again and then translate() the canvas and scale() it. The translate method
            // moves the coordinate system (in this case up and to the left) and the scale method makes
            // everything that is subsequently drawn on the canvas bigger (in this case by a factor of two).
            // When finished the canvas is restored - but because canvas transformations are stacked - that
            // only restores to the last save(), which in this case is the one below. Therefore the restore()
            // only gets rid of the translate() and the scale() that are performed below.
            context.save()
                context.translate(-1 * mouseXY[0], -1 * mouseXY[1]);
                context.scale(2,2);
                DrawChart();
            context.restore();
        
        // Now we're finished drawing the canvas can be restore()ed to it's original state. This gets
        // rid of the clip() effect so that any further drawing will not be disturbed. The end result can
        // be seen at the top of the page.
        context.restore();

        // The magnifying glass frame
        context.drawImage(img, mouseXY[0] - 131, mouseXY[1] - 65);
    }
</script>

4. Hiding the zoom when the onmouseout fires

To hide he zoom when the mouse is moved away from the canvas it's a simple matter of adding a mouseout event listener that redraws the chart and the cover.

<script>
    obj.canvas.onmouseout = function (e)
    {
        RGraph.Clear(e.target);
        
        var obj = DrawChart();
        
        // You could omit this so that when you move the mouse away from the canvas it isn't covered
        // (also comment out the DrawCover() call just above the onmousemove event)
        DrawCover(obj);
    }
</script>

Share RGraph
X

Comments