Проблема рендеринга LitElement с использованием строк SVG для создания вложенных SVG - PullRequest
0 голосов
/ 25 февраля 2020

У меня есть коллекция строк SVG с разными атрибутами viewBox. Я пытаюсь вложить все эти SVG в родительский SVG таким образом, чтобы я мог динамически устанавливать их атрибуты x и y. Ниже приведен минимальный пример решения, которое я придумал. Первоначально он не рендерится, однако, если в браузере devtools я добавляю что-то вроде пустой строки в 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);

1 Ответ

1 голос
/ 26 февраля 2020

это действительно зависит от того, откуда берутся ваши круги ... при условии, что все имеют ширину и высоту "300 пикселей", вы можете просто обернуть ваши SVG в другой SVG, который получает позицию и размер, назначенные в l oop. Затем просто добавьте ваши SVG с помощью unsafeHTML (что предполагает, что вы доверяете источнику ваших строк 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)

после логики c (создания нового элемента 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)
...