You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Fabric.js中如何实现无缩放的副画布显示?

解决Fabric.js双画布:主画布缩放绘图,副画布显示全局无缩放视图

嘿,我懂你的困扰了——你想要一个能缩放绘图的主画布,同时第二个画布始终显示它的完整无缩放全局视图,但现在副画布跟着主画布一起缩放了,对吧?问题出在你直接复制主画布的渲染像素数据,而主画布缩放后,渲染出来的内容已经是放大/缩小后的状态了。咱们用Fabric.js本身的对象机制来解决,会更靠谱!

问题根源

你之前用ctx1.getImageData()获取的是主画布经过缩放变换后的像素,所以副画布自然会显示缩放后的画面。正确的思路应该是让两个画布共享相同的对象集合,但副画布始终以1:1的比例渲染所有内容。

完整解决方案代码

<!DOCTYPE html>
<html>
<head>
    <style>
        canvas { border: 1px solid black; }
        #static { position: relative; top: -300px; left: 500px; }
    </style>
</head>
<body>
    This is interactive canvas
    <canvas id="scale" width="300" height="300"></canvas>
    <canvas id="static" width="300" height="300"></canvas>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"></script>
    <script>
        var c1 = document.getElementById("scale");
        var c2 = document.getElementById("static");
        
        // 主画布(支持缩放、绘图)
        var mainCanvas = new fabric.Canvas(c1, { isDrawingMode: true });
        // 副画布(仅显示全局无缩放视图,禁用交互)
        var staticCanvas = new fabric.Canvas(c2, { 
            selection: false,
            enableRetinaScaling: false
        });

        // 同步背景图到副画布
        fabric.Image.fromURL(
            "https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
            img => {
                img.scaleToWidth(mainCanvas.width);
                mainCanvas.setBackgroundImage(img);
                // 副画布也设置相同的背景图
                staticCanvas.setBackgroundImage(img.clone());
                mainCanvas.requestRenderAll();
                staticCanvas.requestRenderAll();
            },
            { crossOrigin: "Anonymous" }
        );

        // 主画布缩放逻辑(保持不变)
        mainCanvas.on('mouse:wheel', function(opt) {
            var delta = opt.e.deltaY;
            var pointer = mainCanvas.getPointer(opt.e);
            var zoom = mainCanvas.getZoom();
            zoom = zoom + delta/200;
            if (zoom > 20) zoom = 20;
            if (zoom < 0.01) zoom = 0.01;
            mainCanvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
            opt.e.preventDefault();
            opt.e.stopPropagation();
        });

        // 同步主画布的对象变化到副画布
        function syncCanvasObjects() {
            // 清空副画布现有对象
            staticCanvas.clear();
            // 复制主画布的所有对象到副画布
            mainCanvas.getObjects().forEach(obj => {
                staticCanvas.add(obj.clone());
            });
            // 副画布始终保持1倍缩放,并且视图居中
            staticCanvas.setZoom(1);
            staticCanvas.renderAll();
        }

        // 监听主画布的关键事件,实时同步
        mainCanvas.on('object:added', syncCanvasObjects);
        mainCanvas.on('object:modified', syncCanvasObjects);
        mainCanvas.on('object:removed', syncCanvasObjects);
        mainCanvas.on('after:render', syncCanvasObjects);
    </script>
</body>
</html>

关键改动说明

  • 创建独立的副画布实例:不用直接操作context,而是用Fabric.js的Canvas类创建副画布,禁用交互避免误操作。
  • 对象同步机制:监听主画布的object:added/modified/removed事件,每次对象变化时,把主画布的所有对象克隆到副画布。
  • 强制副画布缩放比例:副画布始终设置zoom=1,确保显示的是原始尺寸的全局视图。
  • 替换定时复制为事件驱动:相比setInterval,事件驱动更高效,能精准捕捉画布变化时机。

这样一来,不管你在主画布怎么缩放、绘图,副画布都会实时显示完整的无缩放全局视图啦!

内容的提问来源于stack exchange,提问作者vinkovsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 12:04:09