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

Canvas绘制组件滚动时消失问题排查求助

问题描述

使用JS在Canvas绘制网格状自定义组件,初始加载时显示正常,但向下滚动页面后,Canvas上的方形边框和圆形会消失,仅文字标签保留。

问题原因

  1. 初始代码将Canvas宽高设为window.innerWidth和window.innerHeight,仅覆盖当前窗口可视区域,而组件总高度远大于窗口高度,滚动后超出初始窗口范围的图形会被Canvas裁剪。
  2. 文字标签是通过createElement生成的DOM元素,使用绝对定位挂载在Canvas父节点下,不受Canvas尺寸限制,因此滚动后仍能正常显示。

修复方案

调整Canvas尺寸为所有组件的总宽高,确保能容纳全部绘制内容:

  1. 计算网格的总宽度和总高度:
    • 总宽度 = 列数 × 组件横向间距(componentWidth * 1.5)
    • 总高度 = 行数 × 组件纵向间距(componentHeight * 1.5)
  2. 将Canvas的宽高设置为计算出的总尺寸,替换原有的窗口尺寸赋值。

修改后的完整代码:

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 计算组件网格的总尺寸
const componentWidth = 100;
const componentHeight = 100;
const cols = 10;
const rows = 8;
const totalWidth = cols * componentWidth * 1.5;
const totalHeight = rows * componentHeight * 1.5;

// 设置Canvas为总尺寸,容纳所有组件
canvas.width = totalWidth;
canvas.height = totalHeight;

function drawComponent(x, y, square, circleRadius) {
    ctx.strokeRect(square[0] + x, square[1] + y, square[2] - square[0], square[3] - square[1]);
    const w = circleRadius*2
    const circles = [
        [square[0] - w + x, square[1] - w + y, square[0] + w + x, square[1] + w + y],
        [square[2] - w + x, square[1] - w + y, square[2] + w + x, square[1] + w + y],
        [square[0] - w + x, square[3] - w + y, square[0] + w + x, square[3] + w + y],
        [square[2] - w + x, square[3] - w + y, square[2] + w + x, square[3] + w + y]
    ];
    circles.forEach(circle => {
        ctx.beginPath();
        ctx.arc((circle[0] + circle[2]) / 2, (circle[1] + circle[3]) / 2, circleRadius, 0, Math.PI * 2);
        ctx.strokeStyle = 'black';
        ctx.fillStyle = 'white';
        ctx.fill();
        ctx.lineWidth = 1;
        ctx.stroke();
    });
    const [squareCenterX, squareCenterY] = calculateCenter([square[0] + x, square[1] + y, square[2] + x, square[3] + y]);
    createLabel('Headline', squareCenterX, squareCenterY - circleRadius * 1.5, 'bold', '20px', 'black');
    createLabel('Text', squareCenterX, squareCenterY + circleRadius * 1.5 + 10, 'normal', '16px', 'gray');
}

function createLabel(text, left, top, fontWeight, fontSize, color) {
    const label = document.createElement('label');
    label.textContent = text;
    label.style.position = 'absolute';
    canvas.parentNode.appendChild(label);
    label.style.left = left - label.offsetWidth / 2 + 'px';
    label.style.top = top - label.offsetHeight / 2 + 'px';
    label.style.fontWeight = fontWeight;
    label.style.fontSize = fontSize;
    label.style.textAlign = 'center';
    label.style.color = color;
}

function calculateCenter(rect) {
    const x1 = rect[0];
    const y1 = rect[1];
    const x2 = rect[2];
    const y2 = rect[3];
    return [(x1 + x2) / 2, (y1 + y2) / 2];
}

const circleRadius = componentWidth * 0.1;
for (let row = 0; row < rows; row++) {
    for (let col = 0; col < cols; col++) {
        const x = col * componentWidth * 1.5; 
        const y = row * componentHeight * 1.5; 
        const square = [50, 50, 150, 150];
        drawComponent(x, y, square, circleRadius);
    }
}

额外优化建议

  • 如果需要Canvas跟随页面滚动,上述修改即可解决问题;若要让Canvas固定在视口,可给Canvas添加CSS样式position: fixed; top: 0; left: 0;,同时监听scroll事件,根据滚动偏移量重新绘制组件。
  • 避免重复创建DOM标签,可将文字直接绘制在Canvas上,减少DOM节点数量:
    // 替换createLabel函数,直接在Canvas绘制文字
    function drawLabel(text, x, y, fontWeight, fontSize, color) {
        ctx.font = `${fontWeight} ${fontSize} sans-serif`;
        ctx.fillStyle = color;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(text, x, y);
    }
    
    然后在drawComponent中调用drawLabel替代createLabel,确保文字与Canvas图形同步显示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 17:07:51