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

D3.js Canvas设置边距后缩放不同步问题求助

解决Canvas带边距的D3缩放偏移问题

问题出在D3的zoom变换默认基于Canvas全局坐标系,但你的绘图区域是通过context.translate(margin.left, margin.top)偏移后的局部坐标系,两者不匹配导致缩放中心偏移。只需在onZoom函数中调整变换的偏移量,将全局坐标转换为绘图区域的局部坐标即可修复。

修改后的完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        html, body {
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
        }

        canvas {
            width: 100%;
            height: 100%;
            display: block;
        }
    </style>
</head>
<body>
    <canvas id="lineCanvas"></canvas>

    <script>
        const canvas = document.getElementById('lineCanvas');
        const context = canvas.getContext('2d');

        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        const margin = { top: 20, right: 20, bottom: 40, left: 600 };
        const width = canvas.width - margin.left - margin.right;
        const height = canvas.height - margin.top - margin.bottom;

        const data = Array.from({ length: 1000 }, (_, i) => ({
            x: i / 100,
            y: Math.sin(i / 100)
        }));

        const xScale = d3.scaleLinear().domain([0, 10]).range([0, width]);
        const yScale = d3.scaleLinear().domain([-1, 1]).range([height, 0]);

        const zoom = d3.zoom()
            .scaleExtent([0.5, 100])
            .on("zoom", onZoom);

        d3.select(canvas).call(zoom);

        function drawPlot(transform = d3.zoomIdentity) {
            context.clearRect(0, 0, canvas.width, canvas.height);

            context.save();
            context.translate(margin.left, margin.top);

            const zoomedXScale = transform.rescaleX(xScale);
            const zoomedYScale = transform.rescaleY(yScale);

            context.beginPath();
            context.lineWidth = 2;
            context.strokeStyle = "steelblue";

            data.forEach((point, i) => {
                const x = zoomedXScale(point.x);
                const y = zoomedYScale(point.y);

                if (i === 0) {
                    context.moveTo(x, y);
                } else {
                    context.lineTo(x, y);
                }
            });

            context.stroke();
            context.restore();
        }

        function onZoom(event) {
            // 调整变换:将Canvas全局坐标转换为绘图区域局部坐标
            const { x, y, k } = event.transform;
            const adjustedTransform = d3.zoomIdentity
                .translate(x - margin.left, y - margin.top)
                .scale(k);
            drawPlot(adjustedTransform);
        }

        drawPlot();
    </script>
</body>
</html>

关键修改说明

  • 在onZoom函数中,从原始变换提取缩放系数k和全局偏移x、y
  • 创建新的变换对象,将偏移量减去margin.left和margin.top,把变换原点从Canvas左上角移到绘图区域的左上角(即translate(margin.left, margin.top)后的原点)
  • 使用调整后的变换重绘,确保缩放/平移的中心与绘图区域的实际位置对应

后续绘制坐标轴时,同样基于这个调整后的变换和translate后的绘图区域即可,尺度与图形保持一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 17:55:02