可调整大小的画布,Web 组件可无限增长

问题描述 投票:0回答:1

我正在尝试使用 Web 组件创建一个可调整大小的

<canvas>
,但是,出于某种原因,以我正在做的方式(使用
ResizeObserver
),初始高度在外部
<div>
上从零增长到无限(在下面的示例中,我使用 CSS'
max-height
限制了高度)。

我的初始设置可以吗?有更简单的方法吗?

我确实尝试使用

this.canvas.height = this.parentElement.clientHeight
代替
this.canvas.height = this.height
,但结果更奇怪。

const hookbanContainerTemplate = document.createElement("template")
hookbanContainerTemplate.innerHTML = "<slot></slot>"

class HookbanContainer extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: "open" })
    shadowRoot.appendChild(hookbanContainerTemplate.content.cloneNode(true))
  }
}

class HookbanBackground extends HTMLElement {
  static get observedAttributes() {
    return ["width", "height"]
  }

  constructor(width = 100, height = 100) {
    super()
    this.width = width
    this.height = height
  }

  connectedCallback() {
    this.canvas = document.createElement("canvas")
    const shadowRoot = this.attachShadow({ mode: "open" })
    shadowRoot.appendChild(this.canvas)

    this.drawBackground()
  }

  drawBackground() {
    this.canvas.width = this.width
    this.canvas.height = this.height

    const ctx = this.canvas.getContext("2d")
    if (!ctx) return

    ctx.fillStyle = "blue"
    ctx.fillRect(
      0,
      0,
      this.canvas.width,
      this.canvas.height
    )
  }

  attributeChangedCallback(
    name,
    _oldValue,
    newValue
  ) {
    switch (name) {
      case "width":
        this.width = parseFloat(newValue)
        break
      case "height":
        this.height = parseFloat(newValue)
        break
      default:
    }

    this.drawBackground()
  }
}

customElements.define("hookban-container", HookbanContainer)
customElements.define("hookban-background", HookbanBackground)
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Hookban</title>

    <style>
      body {
        padding: 10px;
      }

      #demo-container {
        resize: both;
        overflow: auto;

        height: fit-content;
        width: fit-content;
        max-height: 300px;
        max-width: 300px;
        padding: 10px;
        border: 1px solid black;
      }
    </style>
  </head>
  <body>
    <div id="demo-container">
      <hookban-container>
        <hookban-background />
      </hookban-container>
    </div>

    <script>
      const observer = new ResizeObserver((mutations) => {
        const { height: newHeight, width: newWidth } = mutations[0].contentRect

        const hookbanBackground = document.querySelector("hookban-background")

        hookbanBackground.setAttribute("height", newHeight)
        hookbanBackground.setAttribute("width", newWidth)
      })

      const hookbanContainer = document.querySelector("div")
      observer.observe(hookbanContainer)
    </script>
  </body>
</html>

(我不知道为什么该片段会给出错误消息

ResizeObserver loop completed with undelivered notifications
,这在我本地不会发生。)

这个问题中的代码正在这个项目中使用。

javascript html typescript html5-canvas web-component
1个回答
1
投票

我重新开始;你想要一个可调整大小的

canvas

我创建了一个独立的 Web 组件

<full-size-canvas>

关键问题是 ResizeObserver 在大小更改时触发;从而形成无限循环。

您必须根据您的需求重构它。
需要注意的一种行为:如果您观察到属性,要初始化属性

attributeChangedCallback
将在connectedCallback之前
触发;您可以检查 
this.isConnected
 是否已触发 
connectedCallback

<full-size-canvas></full-size-canvas> <script> customElements.define("full-size-canvas", class extends HTMLElement { connectedCallback() { let width = 200, height = 80, canvas; const createElement = (tag, props = {}) => Object.assign(document.createElement(tag), props); const updateCanvas = (color = "blue") => { const ctx = canvas.getContext("2d"); canvas.width = width; canvas.height = height; ctx.fillStyle = color; ctx.fillRect(0, 0, width, height); } Object.assign(this.style, { display : "inline-block", resize : "both", border : "1px solid black", overflow : "auto" }); this.attachShadow({ mode: "open"}) .append( createElement("style", { innerHTML: `canvas {vertical-align:top}`, }), canvas = createElement("canvas") ); updateCanvas("grey"); let observer = new ResizeObserver((mutations) => { ({ width, height } = mutations[0].contentRect); if ((canvas.width !== width || canvas.height !== height)) { observer.unobserve(this); // do not observe on repaint updateCanvas(); // Re-enable the observer after the current call stack is clear // also prevents Observer undelivered notifications warning setTimeout(() => observer.observe(this)); } }); observer.observe(this); }// connectedCCallback }) </script>

© www.soinside.com 2019 - 2024. All rights reserved.