Svelte自定义组件内Canvas绘图不生效问题如何解决?
问题原因
核心问题出在Svelte的响应式更新规则和canvas的特性上:
- Svelte只有在对变量执行赋值操作时才会触发响应式更新,你修改的是普通对象
canvas的内部属性,没有触发赋值操作,因此<canvas>元素的width、height属性始终是初始化时的1000x1000,不会同步为你设置的body尺寸。 - 当canvas的画布尺寸和实际展示尺寸不一致时,绘制的内容会被拉伸/挤压,甚至可能因为canvas元素超出视口导致你看不到左上角绘制的小矩形。
- 你将代码移到App.svelte能正常运行,是因为顶层组件的更新触发逻辑和子组件存在差异,刚好触发了宽高的同步,这属于不可靠的巧合,不是正确的用法。
解决方法
方法1:直接操作canvas元素的宽高属性(推荐)
不需要通过中间对象传递宽高,直接操作绑定的canvas DOM元素属性,符合canvas的原生使用习惯,逻辑最简洁:
<script> import { onMount } from 'svelte'; let canvasEl; const drawRect = (context) => { context.beginPath(); context.fillStyle = '#000000'; context.rect(0, 0, 40, 10); context.fill(); }; onMount(() => { const context = canvasEl.getContext('2d'); // 直接修改元素的宽高属性,修改后画布尺寸会立即更新 canvasEl.width = document.body.clientWidth; canvasEl.height = document.body.clientHeight; drawRect(context); }); </script> <canvas bind:this={canvasEl} />
方法2:主动触发响应式更新
如果你需要保留中间的canvas配置对象用于其他场景,可以修改完属性后主动触发响应式更新,注意要等DOM更新完成后再执行绘制,避免宽高变更清空画布内容:
<script> import { onMount, tick } from 'svelte'; let canvasEl; let canvas = { width: 1000, height: 1000 }; const drawRect = (context) => { context.beginPath(); context.fillStyle = '#000000'; context.rect(0, 0, 40, 10); context.fill(); }; onMount(async () => { canvas.width = document.body.clientWidth; canvas.height = document.body.clientHeight; // 主动赋值触发响应式更新 canvas = canvas; // 等待DOM更新完成 await tick(); const context = canvasEl.getContext('2d'); drawRect(context); }); </script> <canvas bind:this={canvasEl} width={canvas.width} height={canvas.height} />
内容的提问来源于stack exchange,提问作者kota-yata
相关产品推荐
相关产品推荐

