【问题标题】:Change canvas size per zoom using Fabric.js使用 Fabric.js 更改每次缩放的画布大小
【发布时间】:2020-05-06 07:07:21
【问题描述】:

我正在构建一个文档查看器,文档的每一页都变成了一张图片,所有页面都在一个 html 画布上一个接一个地组合在一起。

我正在使用 FabricJS 用图像填充画布。 当我在画布内缩放图像时,它们会完美地放大和缩小,但画布本身的大小保持不变。

我希望改变画布大小,以便在需要时隐藏和显示滚动条。

这是构建和填充画布的代码:

buildCanvas: function (images, length, maximumWidth, totalHeight) {
    var height = 10;

    var canvasContainer = document.getElementById("canvasContainer");
    var c = document.createElement("canvas");
    c.width = canvasContainer.offsetWidth - 20;
    c.height = totalHeight;
    engine.canvasObject = new fabric.Canvas(c);

    for (var index = 0; index < length; index++) {
        var imgInstance = new fabric.Image(images[index], {
            left: ((canvasContainer.offsetWidth / 2) - (maximumWidth / 2)),
            top: height
        });
        var ratio = 1;
        if (images[index].width > engine.maxWidth) {
            ratio = engine.maxWidth / images[index].width;
            imgInstance.scale(ratio);
            maximumWidth = engine.maxWidth;
        }
        engine.canvasObject.add(imgInstance);
        height += (images[index].height * ratio) + 15;
    }

    canvasContainer.appendChild(c);
},

这是我用来放大和缩小的代码:

zoom: function (ratio) {
    var canvasContainer = document.getElementById("canvasContainer");
    var middleX = canvasContainer.offsetWidth / 2;
    var middleY = 0;

    var zoom = engine.canvasObject.getZoom();
    engine.canvasObject.setHeight(engine.canvasObject.getHeight() * ratio);
    //engine.canvasObject.setWidth(engine.canvasObject.getWidth() * (zoom + (ratio - 1)));
    engine.canvasObject.zoomToPoint({ x: middleX, y: middleY }, zoom + (ratio - 1));
}

按照此行中的说明更改画布的高度:

engine.canvasObject.setHeight(engine.canvasObject.getHeight() * ratio);

似乎可以完成这项工作,但我找不到更改宽度的公式。 不管我怎么尝试当我在几次点击后缩小页面时,由于画布宽度减小变化太大,页面开始被“剪切”(主要在右侧)。

我想我正在寻找的是一种了解画布内实际绘制图像的高度和宽度的方法。

我尝试使用 getImageData 方法和 toDataUrl 方法来获取它,但它们对我没有好处。

感谢您的帮助

更新额外信息:

  1. zoomToPoint - 根据所有方向的中心点对画布数据进行缩放。在我的代码中,我将 middleY 设置为零,将 middleX 设置为画布容器大小的一半,因此缩放将围绕该点调整数据大小,而不会导致图像移动的效果。
  2. 是的,我希望画布能够根据缩放比例进行缩放,以便用户能够在需要时滚动,或者在不需要时隐藏滚动条。
  3. 是的,画布的大小正确(+/- 2-3 像素)并且没有进行四舍五入。

【问题讨论】:

  • 放大和缩小的比例是如何计算的?
  • @Serenity 很久以前的广告了,我真的不记得是怎么解决的了——从那以后我搬了一家公司,所以我也没有最终的解决方案。

标签: javascript html canvas html5-canvas fabricjs


【解决方案1】:

一些假设,因为我没有足够的信息。

  • engine.canvasObject.zoomToPoint 函数执行统一缩放
  • widthheight 必须在调用 zoomToPoint 后缩放画布以适合其内容。
  • 当调用函数缩放时,画布是当前缩放的正确大小。 getHeight返回画布宽度和高度时没有四舍五入,并由setHeight设置

那么画布分辨率就可以缩放(zoom + (ratio - 1)) / zoom例如...

const currentZoom = engine.canvasObject.getZoom();
const newZoom = currentZoom + (ratio - 1);

const height = engine.canvasObject.getHeight();
const width = engine.canvasObject.getWidth();

const zoomBy = newZoom / currentZoom;  // the amount to scale canvas 

engine.canvasObject.setHeight(height * zoomBy);
engine.canvasObject.setWidth(width * zoomBy);

engine.canvasObject.zoomToPoint({ x: middleX, y: middleY }, newZoom);

这可以放大或缩小。

注意如果zoom === -(ratio - 1),缩放量(zoom + (ratio - 1)) 可能会失败,因为结果缩放将为零。

注意如果当前的缩放比例很小,而新的缩放比例很大,画布尺寸可能会变得太大。为了安全起见,您应该检查新的缩放值,以防止可能导致页面崩溃或速度非常慢的非常大的画布。

舍入问题

如果画布大小被四舍五入(或取整)为整数,那么随着时间的推移,这个错误会越来越大。仅当您多次缩放画布时才会出现此问题。

为避免此错误,您需要有一个参考比例。特定比例的画布大小。

例如

const canvasScaleReference = {
    zoom: 2,      // At this zoom the canvas 
    width: 500,   // is 500 
    height: 1000, // by 1000
};

这意味着在缩放 1 时,画布是 250 x 500

因此缩放和调整画布大小的功能是

const zoom = engine.canvasObject.getZoom() + (ratio - 1);

const height = canvasScaleReference.height / canvasScaleReference.zoom * zoom;
const width = canvasScaleReference.width / canvasScaleReference.zoom * zoom;

engine.canvasObject.setHeight(height);
engine.canvasObject.setWidth(width);

engine.canvasObject.zoomToPoint({ x: middleX, y: middleY }, zoom);

因为这里使用的是固定的引用,所以舍入误差不能累积。

【讨论】:

  • 我更新了我的问题,回复了您的假设。我在上面尝试了您的解决方案,但效果更糟。如果您愿意,这里是完整 JS 代码的链接:dropbox.com/s/qo9ycgwuuojy0x6/scripts.js?dl=0
  • 从外观上看,画布大小与比例不匹配,c.width = canvasContainer.offsetWidth - 20; 行应该不是c.width = maximumWidth;还应在缩放之前减去高度(我假设是边框)加上 15 并在缩放之后加回。例如(c.height - 15) * scale + 15 宽度相同(如果有边框)
  • maximumWidth 与 canvasContainer.offsetWidth 相同 - 我想它们可以切换得更清晰。每个图像和总画布大小都计算为 15px(这意味着在文档之间留出 15px 的边距)。
猜你喜欢
  • 2012-09-25
  • 2015-09-15
  • 2015-04-02
  • 2016-04-10
  • 2013-09-28
  • 2015-06-20
  • 2014-05-17
  • 2015-12-26
  • 1970-01-01
相关资源
最近更新 更多