javascript - 使用 SVG 字符串构建嵌套 SVG 的 LitElement 渲染问题

标签 javascript svg lit-element lit-html

我有一个 SVG 字符串集合,所有字符串都具有不同的 viewBox 属性。我试图将所有这些 SVG 嵌套在父 SVG 中,以便我可以动态设置它们的 xy 属性。下面是我提出的解决方案的一个最小示例。它最初不会渲染,但是如果在浏览器开发工具中我向 HTML 添加类似空行的内容,这似乎会导致重新渲染,并且圆圈会按预期渲染。所以 HTML 生成的结构是正确的,在我的代码“完成”之前,LitElement/lit-html 渲染似乎存在问题。

我怀疑问题归结为我使用 lit-html 模板的方式,而 LitElement 和 SVG 方面是不相关的。

<my-example></my-example>
import { LitElement, svg } from "https://unpkg.com/lit-element?module";

const circles = {
  big: `<svg viewBox="0 0 100 100" width="300px" height="300px">
          <circle cx="50" cy="50" r="50"/>
        </svg>`,
  small: `<svg viewBox="0 0 200 200" width="300px" height="300px">
            <circle cx="100" cy="100" r="60"/>
          </svg>`
};

class MyExample extends LitElement {
  render() {
    return svg`
      <div class="ingredients">
        <svg height="50vh" width="100%" viewBox="0 0 100 100">
          ${Object.keys(circles).map((circle, i) => {
            // create template from svg string
            const template = document.createElement("template");
            template.innerHTML = circles[circle];
            const clone = template.content.cloneNode(true);
            const svgNode = clone.querySelector("svg");
            const viewBoxAtt = clone
              .querySelector("svg")
              .getAttribute("viewBox");

            // create template from svg's contents (circle element)
            const template2 = document.createElement("template");
            template2.innerHTML = svgNode.innerHTML;
            const clone2 = template2.content.cloneNode(true);

            // place circle element in a new svg with dynamically set attributes
            return svg`<svg viewBox=${viewBoxAtt} x=${i * 20} y=${i *
              20} height="30" width="30">${svg`${clone2}`}</svg>`;
          })}
        </svg>
      </div>
    `;
  }
}

window.customElements.define("my-example", MyExample);

最佳答案

这实际上取决于你的圆圈来自哪里......假设所有圆圈的宽度和高度都是“300px”,你可以简单地将你的 SVG 包装到另一个 SVG 中,该 SVG 会在循环中分配位置和大小。然后只需使用 unsafeHTML 添加您的 SVG(假设您信任 SVG 字符串的来源)

import { LitElement, svg } from 'https://unpkg.com/lit-element?module'
import { unsafeHTML } from 'https://unpkg.com/lit-html/directives/unsafe-html.js?module'

const circles = {
  big: `<svg viewBox="0 0 100 100" width="300px" height="300px">
         <circle cx="50" cy="50" r="50"/>
       </svg>`,
  small: `<svg viewBox="0 0 200 200" width="300px" height="300px">
           <circle cx="100" cy="100" r="60"/>
         </svg>`
 }

class MyExample extends LitElement {
  doSomething(evt){
    const index = evt.target.getAttribute("data-index")
    // do something with circle i
  }
  render () {
    return svg`
      <div class="ingredients">
        <svg height="50vh" width="100%" viewBox="0 0 100 100">
          ${Object.values(circles).map((circle, i) => {
            return svg`<svg @click="${this.doSomething}" data-index="${i}" viewBox="0 0 300 300" x=${i * 20} y=${i * 20} height="30" width="30">
              ${unsafeHTML(circle)}
            </svg>`
          })}
        </svg>
      </div>
    `
  }
}

window.customElements.define('my-example', MyExample)

另一种方法是使用 DOMParser 将字符串解析为 DOM,操作该 DOM,然后将结果转回字符串以在渲染函数中渲染它(即如果您的 SVG 没有设置相同的宽度和高度,并且无法删除宽度和高度属性):

import { LitElement, svg } from 'https://unpkg.com/lit-element?module'
import { unsafeHTML } from 'https://unpkg.com/lit-html/directives/unsafe-html.js?module'

const circles = {
  big: `<svg viewBox="0 0 100 100" width="300px" height="300px">
         <circle cx="50" cy="50" r="50"/>
       </svg>`,
  small: `<svg viewBox="0 0 200 200" width="300px" height="300px">
           <circle cx="100" cy="100" r="60"/>
         </svg>`
}

function setPosition (node, x, y, width, height) {
  node.setAttribute('x', x)
  node.setAttribute('y', y)
  node.setAttribute('width', width)
  node.setAttribute('height', height)
}

class MyExample extends LitElement {
  render () {
    const parser = new DOMParser()
    return svg`
      <div class="ingredients">
        <svg height="50vh" width="100%" viewBox="0 0 100 100">
          ${Object.values(circles).map((circle, i) => {
            const doc = parser.parseFromString(circle, 'image/svg+xml')
            setPosition(doc.documentElement, i * 20, i * 20, 30, 30)
            return unsafeHTML(doc.documentElement.outerHTML)
          })}
        </svg>
      </div>
    `
  }
}

window.customElements.define('my-example', MyExample)

这实际上取决于数据来自哪里以及您可以执行哪些步骤来准备数据...最优雅的解决方案是将 SVG 字符串转换为模板字符串返回函数。那么渲染函数就变得非常简单:

import { LitElement, svg } from 'https://unpkg.com/lit-element?module'

const circles = {
  big: (x, y, w, h) => svg`<svg viewBox="0 0 100 100" x="${x}" y="${y}"" width="${w}" height="${h}">
         <circle cx="50" cy="50" r="50"/>
       </svg>`,
  small: (x, y, w, h) => svg`<svg viewBox="0 0 200 200" x="${x}" y="${y}"" width="${w}" height="${h}">
           <circle cx="100" cy="100" r="60"/>
         </svg>`
}

class MyExample extends LitElement {
  render () {
    const parser = new DOMParser()
    return svg`
      <div class="ingredients">
        <svg height="50vh" width="100%" viewBox="0 0 100 100">
          ${Object.values(circles).map((circle, i) => circle(i * 20, i * 20, 30, 30))}
        </svg>
      </div>
    `
  }
}

window.customElements.define('my-example', MyExample)

遵循您在示例中使用的逻辑(创建新的 svg 元素并从 svg 字符串附加圆圈)仅适用于全新的 unsafeSVG() 指令,该指令是未完成的 lit-html 版本 1.2.0...我正在托管该版本的构建,因此您也可以尝试一下:

import { LitElement, svg } from 'https://cdn.klimapartner.net/modules/lit-element/lit-element.js'
import { unsafeSVG } from 'https://cdn.klimapartner.net/modules/lit-html/directives/unsafe-svg.js'

const circles = {
  big: `<svg viewBox="0 0 100 100" width="300px" height="300px">
         <circle cx="50" cy="50" r="50"/>
       </svg>`,
  small: `<svg viewBox="0 0 200 200" width="300px" height="300px">
           <circle xmlns="http://www.w3.org/2000/svg" cx="100" cy="100" r="60"/>
         </svg>`
}

class MyExample extends LitElement {
  render () {
    const parser = new DOMParser()
    return svg`
      <div class="ingredients">
        <svg height="50vh" width="100%" viewBox="0 0 100 100">
          ${Object.values(circles).map((circle, i) => {
            const doc = parser.parseFromString(circle, 'image/svg+xml')
            return svg`<svg viewBox="${doc.documentElement.getAttribute('viewBox')}" x=${i * 20} y=${i * 20} height="30" width="30">
              ${unsafeSVG(doc.documentElement.innerHTML)}
            </svg>`
          })}
        </svg>
      </div>
    `
  }
}

window.customElements.define('my-example', MyExample)

关于javascript - 使用 SVG 字符串构建嵌套 SVG 的 LitElement 渲染问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60391454/

相关文章:

javascript - 删除子节点

javascript - 使用 TypeScript 创建 SVG 元素

html - 在 HTML 中放置 SVG 内容的最佳方式

javascript - 如何仅在省略号时激活工具提示?

java - AES javascript加密和Java解密

javascript - 使用 JavaScript 在新选项卡中打开 PDF 文件

javascript - lit-element 示例中 "$="或 "?="的用途

javascript - Golang 和 JavaScript 模块

javascript - 将 @query 与 LitElement 和 Typescript 结合使用

javascript - 纯函数能否返回一个在随机时间后解决的 promise ?