HTML SVGはグループ<g>を<use>で再利用し、インスタンスごとに内部要素の属性を個別に変更します

Aug 21 2020

そのため、グループ化されたsvgシェイプを再利用し、インスタンスごとにグループ内の要素の1つの属性を個別に変更したいと思います。次の簡略化された例では、内部に長方形を持つ2番目の円を作成します。ここで、javascriptを使用して、形状ごとに「my-rect」長方形の「width」属性を個別に変更したいと思います。id "my-rect"を使用すると、両方の長方形の幅が変更されますが、1つだけを変更したいと思います。

私の目標(私のアプローチが意味をなさない場合):これらの形状を複数描画する必要があり、異なるのは長方形の位置と幅だけです。

<svg height="1000" width="1000">
  <a transform="translate(110,110)">
    <g id="my-group">
      <g>
        <circle r="100" fill="#0000BF" stroke="black" stroke-width="2" fill-opacity="0.8"></circle>
      </g>
      <g>
        <rect id="my-rect" y="-50" height="100" x="-50" width="50">
        </rect>
      </g>
    </g>
  </a>
  <use xlink:href="#my-group" x="340" y="110"/>
</svg>

回答

4 ccprog Aug 20 2020 at 23:43

少し工夫すれば、それは可能です。シャドウ要素内のプロパティ値を取得するには、CSS継承を利用する必要があります。この場合、長方形の拡大縮小と配置に使用されるのはカスタム変数になります。

このため、マークアップを少し書き直す必要があります。まず、<defs>要素内にグループを記述し、それを再利用用のテンプレートにしますが、それ自体ではレンダリングしません。次に、長方形はネストされた<svg overflow="visible">要素の内側に配置されます。この要素にx / y座標を指定し、<rect>要素の座標を0のままにすると、変換操作後に長方形の左側がどこに到達するかを追跡しやすくなります。

これで、長方形の幅の変更は、位置のscaleX()変換とatranslate()を使用して実現されます。これはCSS変換構文である必要がありますtransform属性の使用は(まだ)機能しません。したがって、transform-origin囲んでいる<svg>要素の左側に設定されたプロパティも必要です。

スケーリングの具体的な値を記述する代わりに、値はデフォルト値1の変数として表されますvar(--scale, 1)。位置の値についても同じです。変数の値は、style<use>要素の属性に個別に設定されますstyle="--scale:2;--posX:20px; --posY:-10px"px単位を書く必要があることに注意してください!

#my-rect {
    transform-origin: left top;
    transform: translate(var(--posX, 0), var(--posY, 0)) scaleX(var(--scale, 1));
}
<svg height="1000" width="1000">
  <defs>
    <g id="my-group">
      <g>
        <circle r="100" fill="#0000BF" stroke="black" stroke-width="2" fill-opacity="0.8"></circle>
      </g>
      <svg x="-50" y="-50" overflow="visible">
        <rect id="my-rect" height="100" width="50">
        </rect>
      </svg>
    </g>
  </defs>
  <use xlink:href="#my-group" x="110" y="110" style="--scale:1"/>
  <use xlink:href="#my-group" x="340" y="110" style="--scale:2;--posX:20px; --posY:-10px"/>
</svg>

5 Danny'365CSI'Engelman Aug 21 2020 at 03:37

ショーンは言った:

Webコンポーネントのカスタム要素がSVG名前空間に拡張されると、
より複雑な再利用が可能になります

確かに、カスタムSVG要素を(まだ)作成することはできません。

ただし、SVGを生成するカスタム要素を作成することはできます。

customElements.define("rect-in-circle", class extends HTMLElement{
  connectedCallback(){
    const a = x => this.getAttribute(x);
    this.innerHTML=`<svg viewBox="-100 -100 100 100">`+
                   `<g transform="translate(-50 -50)">`+
                     `<circle r="50" fill="#123456AB"/>`+
                     `<rect y="${a("y")}" height="${a("height")}"`+
                           `x="${a("x")}" width="${a("width" )}"/>`+
                   `</g></svg>`
  }
});
svg{
  width:100px;
  height:100px;
  background:grey;
  fill:green;
}
<rect-in-circle x=-10 y=-10 width=20 height=20></rect-in-circle>
<rect-in-circle x=-40 y=-20 width=10 height=40></rect-in-circle>
<rect-in-circle x= 10 y= 20 width=30 height= 5></rect-in-circle>

SVGのカスタム要素は、多くのoldskoolSVGハックに対する最新のソリューションです。

更新

OPで円のあるSVGが1つ必要な場合は、shadowDOMを使用でき、lightDOM内の要素は表示されません。(HTML名前空間で)未定義の <rect>要素を使用することもできるので、SVG文字列に簡単に挿入できます。

<script>
  customElements.define("rects-in-circles", class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => {
        const rects = [...this.children];
        const width = rects.length * 100;
        this.attachShadow({
          mode: "open"
        }).innerHTML = `<svg viewBox='0 0 ${width} 100'>` + rects.map((rect, idx) => `<g transform='translate(${50+100*idx} 50)'>` +
          `<circle r='50' fill='green'/>` +
          rect.outerHTML +
          `</g>`) + "</svg>";

      })
    }
  });

</script>

<rects-in-circles>
  <rect x=-10 y=-10 width=20 height=20></rect>
  <rect x=-40 y=-20 width=10 height=40></rect>
  <rect x=10 y=20 width=30 height=5></rect>
  <rect x=-40 y=-40 width=50 height=50></rect>
</rects-in-circles>

(my)関連するStackOverflowの回答:カスタム要素とSVG

2 Sean Aug 20 2020 at 22:28

これは不可能です。use要素が作成されますclosed shadow root、その内容はJSにアクセスできないことを意味します。再利用された要素のインスタンスの属性を個別に設定できますが(元の要素に設定されていない場合)、再利用された要素の子である要素に影響を与えることはできません。

別のアプローチは、長方形と円の要素を直接再利用し、それらを新しいグループに配置することです。

AmirFo Sep 27 2020 at 17:27

このgetAttribute方法は私にはうまくいきませんでしたが、これはうまくいきました:

customElements.define("source-link", class extends HTMLElement {
    static get observedAttributes() {
        return ['href'];
    }

    get href() {
        return this.getAttribute('href');
    }

    set href(val) {
        if (val) {
            this.setAttribute('href', val);
        } else {
            this.removeAttribute('href');
        }
    }
    connectedCallback(){
        this.innerHTML= '<a href="' + this.href + '" target="_blank" title="source"><svg fill="#37f" viewBox="0 0 512 512" width="16"><g><path d="M488.727,0H302.545c-12.853,0-23.273,10.42-23.273,23.273c0,12.853,10.42,23.273,23.273,23.273h129.997L192.999,286.09    c-9.089,9.089-9.089,23.823,0,32.912c4.543,4.544,10.499,6.816,16.455,6.816c5.956,0,11.913-2.271,16.457-6.817L465.455,79.458    v129.997c0,12.853,10.42,23.273,23.273,23.273c12.853,0,23.273-10.42,23.273-23.273V23.273C512,10.42,501.58,0,488.727,0z"/></g><g><path d="M395.636,232.727c-12.853,0-23.273,10.42-23.273,23.273v209.455H46.545V139.636H256c12.853,0,23.273-10.42,23.273-23.273    S268.853,93.091,256,93.091H23.273C10.42,93.091,0,103.511,0,116.364v372.364C0,501.58,10.42,512,23.273,512h372.364    c12.853,0,23.273-10.42,23.273-23.273V256C418.909,243.147,408.489,232.727,395.636,232.727z"/></g></svg></a>';
    }
});

使用法

<source-link href="//google.com"></source-link>

出典:D