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

WordPress+Elementor中Canvas绘图鼠标偏移问题求助

WordPress + Elementor中Canvas绘图光标偏移问题修复

问题描述

我想在WordPress里通过Elementor的HTML元素实现交互式绘图功能,代码在WordPress外部运行正常,但部署后鼠标绘制位置和光标出现明显偏移。尝试过调整Canvas尺寸、移除边距,问题仍未解决。

核心原因

  1. Canvas实际尺寸与显示尺寸脱节:原代码用window.innerWidth计算Canvas的绘图缓冲区尺寸,但Elementor的嵌套容器会通过CSS缩放Canvas的显示尺寸,导致坐标映射错误。
  2. 偏移计算逻辑错误:canvas.offsetLeft/offsetTop在Elementor的复杂布局中计算不准确,且未考虑窗口滚动的影响,同时e.clientY未减去Canvas的垂直偏移。
  3. 静态偏移值未更新:页面加载后如果元素位置因响应式布局变化,原代码中的静态偏移值不会自动刷新。

修复方案

1. 同步Canvas绘图尺寸与显示尺寸

不要用固定值设置Canvas尺寸,而是实时获取它在页面中的显示尺寸,同步到绘图缓冲区:

function resizeCanvas() {
  const rect = canvas.getBoundingClientRect();
  canvas.width = rect.width;
  canvas.height = rect.height;
}
// 页面加载和窗口大小变化时触发
window.addEventListener('load', resizeCanvas);
window.addEventListener('resize', resizeCanvas);

2. 正确计算鼠标在Canvas内的坐标

用getBoundingClientRect()实时获取Canvas的位置,同时考虑窗口滚动的影响,确保坐标精准映射:

const draw = (e) => {
  if(!isPainting) return;

  ctx.lineWidth = lineWidth;
  ctx.lineCap = 'round';

  const rect = canvas.getBoundingClientRect();
  // 计算相对Canvas的坐标:鼠标位置 - Canvas左上角位置 + 滚动偏移
  const x = e.clientX - rect.left + window.scrollX;
  const y = e.clientY - rect.top + window.scrollY;

  ctx.lineTo(x, y);
  ctx.stroke();
}

canvas.addEventListener('mousedown', (e) => {
  isPainting = true;
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left + window.scrollX;
  const y = e.clientY - rect.top + window.scrollY;
  ctx.beginPath();
  ctx.moveTo(x, y); // 修正:绘制开始时先定位到鼠标起始点
});

3. 调整CSS避免Canvas被意外缩放

给Canvas添加CSS规则,确保显示尺寸与绘图缓冲区一致,避免Elementor默认样式干扰:

#drawing-board {
  display: block; /* 移除inline元素的默认间距 */
  width: 100%;
  height: 100%;
}

完整修正代码

注意:Elementor的HTML元素是嵌入现有页面的,不需要包含html/head/body根标签,直接使用以下代码:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope&display=swap" rel="stylesheet">
<style>
    *,
    *:before,
    *:after {
        box-sizing: border-box;
    }

    .container{
        margin-left: 2em;
    }
    .drawing-board{
        height: 80vh; /* 用vh替代%,避免父容器高度不确定的问题 */
        width: 80%;
    }

    #drawing-board {
        display: block;
        width: 100%;
        height: 100%;
        border: 1px solid #ccc; /* 可选:添加边框方便识别Canvas范围 */
    }

    #toolbar{
        display: flex;
        align-items: center;
        justify-content: space-around;
        margin-top: 1em;
    }

    input[type="color"]{
        -webkit-appearance: none;
        -moz-appearance: none;
        appearance: none;
        width: 70px;
        height: 70px;
        background-color: transparent;
        border: none;
        cursor: pointer;
    }

    input[type="color"]::-webkit-color-swatch{
        border-radius: 15px;
        border: none;
    }

    #lineWidth{
        visibility: hidden;
    }

    #clear{
        font-family: 'Manrope', sans-serif;
        border-radius: 4em;
        background-color: black;
        border: solid black 2px;
        padding: .5em 2em;
        color: white;
        cursor: pointer;
    }

    #clear:hover{
        color: black;
        background-color: transparent;
    }
</style>

<section class="container">
    <div class="drawing-board">
        <canvas id="drawing-board"></canvas>
    </div>
    <div id="toolbar">
        <label for="stroke">Choisissez votre couleur : </label>
        <input id="stroke" name='stroke' type="color" value="#43da86">
        <button id="clear">Effacer</button>
        <input id="lineWidth" name='lineWidth' type="number" value="18">
    </div>
</section>

<script>
    const canvas = document.getElementById('drawing-board');
    const toolbar = document.getElementById('toolbar');
    const ctx = canvas.getContext('2d');

    let isPainting = false;
    let lineWidth = 18;

    // 调整Canvas尺寸
    function resizeCanvas() {
        const rect = canvas.getBoundingClientRect();
        canvas.width = rect.width;
        canvas.height = rect.height;
    }

    window.addEventListener('load', resizeCanvas);
    window.addEventListener('resize', resizeCanvas);

    toolbar.addEventListener('click', e => {
        if (e.target.id === 'clear') {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        }
    });

    toolbar.addEventListener('change', e => {
        if(e.target.id === 'stroke') {
            ctx.strokeStyle = e.target.value;
        }

        if(e.target.id === 'lineWidth') {
            lineWidth = e.target.value;
        }
    });

    const draw = (e) => {
        if(!isPainting) return;

        ctx.lineWidth = lineWidth;
        ctx.lineCap = 'round';

        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left + window.scrollX;
        const y = e.clientY - rect.top + window.scrollY;

        ctx.lineTo(x, y);
        ctx.stroke();
    }

    canvas.addEventListener('mousedown', (e) => {
        isPainting = true;
        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left + window.scrollX;
        const y = e.clientY - rect.top + window.scrollY;
        ctx.beginPath();
        ctx.moveTo(x, y);
    });

    canvas.addEventListener('mouseup', e => {
        isPainting = false;
        ctx.stroke();
        ctx.beginPath();
    });

    canvas.addEventListener('mousemove', draw);
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 17:11:11