
canvas 元素默认仅覆盖视口大小,当绘制内容超出其宽高范围时,滚动后将被裁剪不可见;需根据实际渲染区域动态计算并设置 canvas.width/height。
canvas 元素默认仅覆盖视口大小,当绘制内容超出其宽高范围时,滚动后将被裁剪不可见;需根据实际渲染区域动态计算并设置 canvas.width/height。
问题根源在于:您将
✅ 正确做法是:让 Canvas 尺寸匹配整个内容区域,而非仅适配视口。以下是优化后的完整实现:
<canvas id="myCanvas" style="display: block; width: 100%; height: auto;"></canvas>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// ✅ 提前定义布局参数(可配置化)
const componentWidth = 100;
const componentHeight = 100;
const spacingX = componentWidth * 1.5; // 水平间距
const spacingY = componentHeight * 1.5; // 垂直间距
const cols = 10;
const rows = 8;
// ✅ 动态计算 Canvas 总尺寸(关键修复!)
canvas.width = Math.ceil(spacingX * cols);
canvas.height = Math.ceil(spacingY * rows);
// ✅ 绘制逻辑保持不变(但确保所有坐标在 canvas 范围内)
const circleRadius = componentWidth * 0.1;
const square = [50, 50, 150, 150]; // 相对组件自身的局部坐标
function drawComponent(x, y, square, circleRadius) {
// 绘制正方形边框(注意:坐标已相对于 canvas 原点)
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]
];
ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';
ctx.lineWidth = 1;
circles.forEach(circle => {
const centerX = (circle[0] + circle[2]) / 2;
const centerY = (circle[1] + circle[3]) / 2;
ctx.beginPath();
ctx.arc(centerX, centerY, circleRadius, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
});
// ✅ 使用 canvas 坐标计算中心点(避免依赖 label offset 计算偏差)
const [centerX, centerY] = calculateCenter([
square[0] + x,
square[1] + y,
square[2] + x,
square[3] + y
]);
createLabel('Headline', centerX, centerY - circleRadius * 1.5, 'bold', '20px', 'black');
createLabel('Text', centerX, centerY + 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';
label.style.left = `${left}px`;
label.style.top = `${top}px`;
label.style.transform = 'translate(-50%, -50%)'; // ✅ 更精准居中(替代 offsetWidth/Height)
label.style.fontWeight = fontWeight;
label.style.fontSize = fontSize;
label.style.textAlign = 'center';
label.style.color = color;
label.style.pointerEvents = 'none'; // 防止遮挡 canvas 交互
canvas.parentNode.appendChild(label);
}
function calculateCenter(rect) {
return [(rect[0] + rect[2]) / 2, (rect[1] + rect[3]) / 2];
}
// ✅ 批量渲染
for (let row = 0; row <p>⚠️ <strong>重要注意事项</strong>:</p>
- 不要混用 CSS width/height 与 Canvas 内置 width/height 属性:CSS 缩放会导致像素模糊,务必通过 JS 直接设置 canvas.width/canvas.height(单位:CSS 像素)。
- 若后续需响应式(如窗口缩放),应监听 resize 并重新计算尺寸 + 清空重绘(调用 ctx.clearRect(0,0,canvas.width,canvas.height))。
- 标签元素(
- 如需高性能大量组件,建议改用纯 Canvas 绘制文本(ctx.fillText()),避免创建大量 DOM 节点。
通过以上调整,Canvas 将完整承载全部 80 个组件,滚动时图形不再消失,真正实现「所绘即所见」。









