Domanda

I want to visualize a huge diagram that is drawn in a HTML5 canvas. As depicted below, let’s imagine the world map, it’s impossible to visualize it all at the same time with a “decent” detail. Therefore, in my canvas I would like to be able to pan over it using the mouse to see the other countries that are not visible.

Does anyone know how to implement this sort of panning in a HTML5 canvas? Another feature would be the zoom in and out.

canvas diagram I've seen a few examples but I couldn't get them working nor they seam to address my question.

Thanks in advance!

È stato utile?

Soluzione

To achieve a panning functionality with a peep-hole it's simply a matter of two draw operations, one full and one clipped.

To get this result you can do the following (see full code here):

Setup variables:

var ctx = canvas.getContext('2d'),

    ix = 0, iy = 0,           /// image position
    offsetX = 0, offsetY = 0, /// current offsets
    deltaX, deltaY,           /// deltas from mouse down
    mouseDown = false,        /// in mouse drag
    img = null,               /// background
    rect,                     /// rect position
    rectW = 200, rectH = 150; /// size of highlight area

Set up the main functions that you use to set size according to window size (including on resize):

/// calc canvas w/h in relation to window as well as
/// setting rectangle in center with the pre-defined
/// width and height
function setSize() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    rect = [canvas.width * 0.5 - rectW * 0.5,
            canvas.height * 0.5 - rectH * 0.5,
            rectW, rectH]
    update();
}

/// window resize so recalc canvas and rect
window.onresize = setSize;

The main function in this is the draw function. Here we draw the image on the position calculated by mouse moving (see next section).

  • First step to get that washed-out look is to set alpha down to about 0.2 (you could also draw a transparent rectangle on top but this is more efficient).
  • Then draw the complete image.
  • Reset alpha
  • Draw the peep-hole using clipping with corrected offsets for the source.

-

/// main draw
function update() {
    if (img === null) return;

    /// limit x/y as drawImage cannot draw with negative
    /// offsets for clipping
    if (ix + offsetX > rect[0]) ix = rect[0] - offsetX;
    if (iy + offsetY > rect[1]) iy = rect[1] - offsetY;

    /// clear background to clear off garbage
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    /// make everything transparent
    ctx.globalAlpha = 0.2;

    /// draw complete background
    ctx.drawImage(img, ix + offsetX, iy + offsetY);

    /// reset alpha as we need opacity for next draw
    ctx.globalAlpha = 1;

    /// draw a clipped version of the background and
    /// adjust for offset and image position
    ctx.drawImage(img, -ix - offsetX + rect[0],  /// sx
                       -iy - offsetY + rect[1],  /// sy
                       rect[2], rect[3],         /// sw/h

                       /// destination
                       rect[0], rect[1], rect[2], rect[3]);

    /// make a nice sharp border by offsetting it half pixel
    ctx.strokeRect(rect[0] + 0.5, rect[1] + 0.5, rect[2], rect[3]);
}

Now it's a matter of handling mouse down, move and up and calculate the offsets -

In the mouse down we store current mouse positions that we'll use for calculating deltas on mouse move:

canvas.onmousedown = function(e) {

    /// don't do anything until we have an image
    if (img === null) return;

    /// correct mouse pos
    var coords = getPos(e),
        x = coords[0],
        y = coords[1];

    /// store current position to calc deltas
    deltaX = x;
    deltaY = y;

    /// here we go..
    mouseDown = true;
}

Here we use the deltas to avoid image jumping setting the corner to mouse position. The deltas are transferred as offsets to the update function:

canvas.onmousemove = function(e) {

    /// in a drag?
    if (mouseDown === true) {

        var coords = getPos(e),
            x = coords[0],
            y = coords[1];

        /// offset = current - original position
        offsetX = x - deltaX;
        offsetY = y - deltaY; 

        /// redraw what we have so far
        update();
    }
}

And finally on mouse up we make the offsets a permanent part of the image position:

document.onmouseup = function(e) {

    /// was in a drag?
    if (mouseDown === true) {
        /// not any more!!!
        mouseDown = false;

        /// make image pos. permanent
        ix += offsetX;
        iy += offsetY;

        /// so we need to reset offsets as well
        offsetX = offsetY = 0;
    }
}

For zooming the canvas I believe this is already answered in this post - you should be able to merge this with the answer given here:
Zoom Canvas to Mouse Cursor

Altri suggerimenti

To do something like you have requested, it is just a case of having 2 canvases, each with different z-index. one canvas smaller than the other and position set to the x and y of the mouse.

Then you just display on the small canvas the correct image based on the position of the x and y on the small canvas in relation to the larger canvas.

However your question is asking for a specific solution, which unless someone has done and they are willing to just dump their code, you're going to find it hard to get a complete answer. I hope it goes well though.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top