使用 html5 canvas 的更大图像的视口

Viewport from bigger image with html5 canvas

我正在尝试使用较小的 canvas 作为另一个更大的 canvas 的视口。 我真的很喜欢 this 类似问题的解决方案中使用的方法。

他基本上使用 CanvasRenderingContext2D.drawImage() 到 "crop" buffer/offset canvas,然后在较小的 canvas(视口)上显示该部分图像。我正在尝试在此 fiddle: https://jsfiddle.net/avvac0x8/2/ 中实现给定解决方案的更简单版本。但是正如您所看到的,视口与大图像并不完全同步(反之亦然)。

似乎后面提到的解决方案不适用于不同的 canvas 尺寸。所以我需要做到 "canvas-size agnostic".

也许我遗漏了某种缩放计算,但我不知道如何从这里开始,欢迎任何提示。

编辑:

更新了 fiddle 以正常工作:https://jsfiddle.net/avvac0x8/4/。显然,不应缩放原始图像以适应缓冲区 canvas。应该做的是 offset/buffer canvas 应该与原始图像大小相同。

最简单的方法就是再用一个canvas做中间层

这里我会忽略偏移量canvas,因为除非你想显示整个地图,否则不需要它。大概您需要的只是放大的区域。如果您想缩小,只需将完整图像绘制到视口 window(通过向 ctx.drawImage ( img, x, y, viewPort.width, viewPort.height ) 提供宽度和高度参数)。但是,您希望确保您的图像被手动裁剪到合适的尺寸,这样图像就不会出现拉伸,或者确保您的 canvas 视口与您使用的图像具有相同的纵横比。

如果您希望背景的裁剪区域(实际查看区域)与视口 window(缩放 in/out 视图的大小不同(小于或大于),则以下方法有效区域 )。请注意,这与实际背景的大小无关。大概裁剪区域和视口 window 都小于背景图像本身。

例如:

// use these to determine where the clipping region lives
var offsetX = 0,
    offsetY = 0,
    clipWidth = <<yourChosenPixelWidth>>,
    clipHeight = <<yourChosenPixelHeight>>,
    clip = document.createElement ( "canvas" ),
    clipCtx,
    viewPort = document.getElementById ( "main-canvas" ),
    viewCtx = viewPort.getContext ( "2d" ),
    background = new Image (),
    // offsetCanvas = document.getElementById ( "offset-canvas" ),
    imgLoaded = false;

// configure the offset canvas once
background.src = "http://pixeljoint.com/files/icons/full/map__r1470206141.png";
background.onLoad = function() {
    // the fiddle scales the image, here we don't
    //offsetCanvas.width = background.width;
    //offsetCanvas.height = background.height;
    //offsetCtx = offsetCanvas.getContext ( "2d" );
    //offsetCtx.drawImage ( background, 0, 0 );
    imgLoaded = true;
}

clip.width = clipWidth;
clip.height = clipHeight;
clipCtx = clip.getContext ( "2d" );

function updateViewport () {
    if ( imgLoaded ) {
        // copy pixels from the background directly
        // to the middle layer so we have a "clipped"
        // but unscaled image object
        //clipCtx.putImageData ( offsetCtx.getImageData ( offsetX, offsetY, clip.width, clip.height ) );
        clipCtx.drawImage ( background, offsetX, offsetY );

        // this is where rescaling happens
        viewCtx.drawImage ( clip, 0, 0, viewPort.width, viewPort.height );

        // and you're done!
    }
}