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

使用Web Components实现可缩放Canvas时高度无限增长问题排查

问题分析与解决方案

核心问题根源

你遇到的无限高度增长和ResizeObserver loop completed with undelivered notifications报错,本质是布局循环:在ResizeObserver回调中修改canvas的height属性时,若没有正确区分canvas的「绘图缓冲区尺寸」和「渲染尺寸」,会触发父div的尺寸变化,进而再次触发ResizeObserver回调,形成无限循环。max-height只是强行终止了循环,但循环逻辑本身依然存在。

初始设置的不合理点

  1. 混淆了canvas的两个关键尺寸:
    • canvas.width/height属性:控制绘图缓冲区的像素尺寸,不影响页面布局
    • CSS的width/height:控制canvas在页面上的渲染显示尺寸,会影响父元素布局
  2. 外层div无初始最小高度约束,初始高度为0,成为循环触发的起点
  3. 回调中修改的属性直接引发父元素布局变化,导致循环

简化且可靠的实现方案

1. 基础思路

  • 用CSS让canvas占满父容器,避免修改布局属性触发循环
  • 仅在ResizeObserver回调中更新canvas的绘图缓冲区尺寸(保证清晰度)
  • 给外层容器设置初始最小高度,避免初始尺寸为0

2. 示例代码

class ScalableCanvas extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        .canvas-container {
          width: 100%;
          min-height: 200px; /* 初始最小高度,避免0值 */
          max-height: 800px; /* 你的高度限制 */
        }
        canvas {
          width: 100%;
          height: 100%;
          display: block; /* 消除inline元素默认间隙 */
        }
      </style>
      <div class="canvas-container">
        <canvas></canvas>
      </div>
    `;
    this.container = this.shadowRoot.querySelector('.canvas-container');
    this.canvas = this.shadowRoot.querySelector('canvas');
    this.observer = new ResizeObserver(this.handleResize.bind(this));
  }

  connectedCallback() {
    this.observer.observe(this.container);
    this.handleResize(); // 初始渲染一次
  }

  disconnectedCallback() {
    this.observer.disconnect();
  }

  handleResize() {
    const dpr = window.devicePixelRatio || 1;
    // 获取容器的实际渲染尺寸
    const { width, height } = this.container.getBoundingClientRect();
    // 更新绘图缓冲区尺寸,乘以设备像素比保证清晰度
    this.canvas.width = Math.floor(width * dpr);
    this.canvas.height = Math.floor(height * dpr);
    
    // 这里添加你的绘图逻辑
    const ctx = this.canvas.getContext('2d');
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    // 示例:绘制填充矩形
    ctx.fillStyle = '#2c3e50';
    ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
  }
}

customElements.define('scalable-canvas', ScalableCanvas);

关于你之前的尝试

用this.canvas.height = this.parentElement.clientHeight效果更糟,是因为你直接把父元素的渲染高度赋值给了canvas的绘图缓冲区尺寸,但没有通过CSS让canvas占满父容器,导致canvas的渲染尺寸被绘图缓冲区尺寸撑开,进一步放大了布局循环的问题。

报错说明

ResizeObserver loop completed with undelivered notifications是浏览器的警告,提示回调操作触发了新的resize事件,导致部分通知未被处理。修复布局循环后,这个警告会自动消失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:42:18