Lit-Element中canvas元素窗口resize时无法调整大小问题
问题根因梳理
- CSS规则与需求不匹配:当前
square类设置的尺寸是min(50vw,75vh),和你要求的「占80%视口高度的正方形」不符 - Safari布局更新滞后:resize事件触发时,Safari可能还未完成CSS布局重排,此时读取
clientWidth/clientHeight拿到的还是旧值,导致resolutionMatch判断始终为真,不会更新canvas绘制尺寸 - 空值判断逻辑缺陷:
handleResize中仅判断canvas === null,如果this.canvas未正确赋值为undefined时,不会触发兜底返回,后续逻辑会异常 - (如果使用Lit等Web Components框架)DOM挂载时机问题:
connectedCallback触发时组件内部DOM还未渲染完成,this.canvas/this.canvasAlt可能未正确指向DOM元素
修复方案
1. 修正CSS样式
将square类调整为符合80%视口高度正方形的规则:
.square { width: min(80vh, 100vw); height: min(80vh, 100vw); }
2. 修正空值判断逻辑
将handleResize中的空值判断改为覆盖undefined和null的场景:
if (!canvas) { console.warn('null or undefined canvas'); return; }
3. 适配Safari布局滞后问题
用requestAnimationFrame包裹尺寸读取和更新逻辑,保证读取到的是布局更新后的最新尺寸,同时给resize事件加去抖避免高频触发:
// 新增去抖工具函数 const debounce = (func, wait) => { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); } } async handleResize(canvas) { canvas ??= this.canvas; if (!canvas) { console.warn('null or undefined canvas'); return; } // 等下一帧再读取尺寸,保证Safari布局更新完成 requestAnimationFrame(async () => { const width = canvas.clientWidth; const height = canvas.clientHeight; // 可选:如果需要高清显示,乘以设备像素比 // const dpr = window.devicePixelRatio || 1; // const drawWidth = width * dpr; // const drawHeight = height * dpr; const resolutionMatch = canvas.width === width && canvas.height === height; if (!resolutionMatch) { canvas.width = width; canvas.height = height; // 高清模式下需要缩放上下文 // const ctx = canvas.getContext('2d'); // ctx.scale(dpr, dpr); } const world = this.state || (await this.worlds).currentWorld; draw(this.canvas, world); }) } connectedCallback() { super.connectedCallback(); // 加100ms去抖,避免resize高频触发 this._resizers = debounce(() => { this.handleResize(this.canvas); this.handleResize(this.canvasAlt); }, 100).bind(this); window.addEventListener('resize', this._resizers); } // 如果使用Lit等框架,在firstUpdated生命周期中获取canvas元素引用 firstUpdated() { this.canvas = this.renderRoot.querySelector('#canvas'); this.canvasAlt = this.renderRoot.querySelector('#canvasAlt'); // 初始化时调用一次resize this._resizers(); }
4. 可选优化:调整异步逻辑位置
如果await this.worlds是一次性的初始化逻辑,建议提前在组件初始化阶段完成加载,不要放在每次resize触发的逻辑中,避免异步执行顺序混乱导致的绘制异常。
内容的提问来源于stack exchange,提问作者Ian Campbell
相关产品推荐
相关产品推荐

