HTML SVG menggunakan kembali grup <g> dengan <use> dan mengubah atribut elemen dalam satu per satu untuk setiap instance

Aug 21 2020

Jadi saya ingin menggunakan kembali bentuk svg yang dikelompokkan dan mengubah satu atribut dari salah satu elemen di dalam grup secara individual untuk setiap contoh. Contoh sederhana berikut membuat lingkaran kedua dengan persegi panjang di dalamnya. Sekarang saya ingin mengubah atribut "lebar" dari persegi panjang "persegi-saya" satu per satu untuk setiap bentuk dengan javascript. Menggunakan id "my-rect" akan mengubah lebar kedua persegi panjang, tapi saya hanya ingin mengubah satu.

Tujuan saya (jika pendekatan saya tidak masuk akal): Saya harus menggambar beberapa bentuk ini dan satu-satunya hal yang berbeda adalah posisi dan lebar persegi panjang.

<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>

Jawaban

4 ccprog Aug 20 2020 at 23:43

Dengan sedikit tipu daya, itu mungkin. Anda harus memanfaatkan warisan CSS untuk mendapatkan beberapa nilai properti di dalam elemen bayangan. Dalam kasus ini, itu akan menjadi variabel khusus yang akan digunakan untuk menskalakan dan memosisikan persegi panjang.

Markup harus ditulis ulang sedikit untuk ini. Pertama, Anda menulis grup Anda di dalam sebuah <defs>elemen, menjadikannya template untuk digunakan kembali, tetapi tidak dirender dengan sendirinya. Kedua, persegi panjang ditempatkan di dalam <svg overflow="visible">elemen bersarang . Memberi elemen ini koordinat x / y dan membiarkannya di 0 untuk <rect>elemen membuatnya lebih mudah untuk melacak di mana sisi kiri persegi panjang akan berakhir setelah operasi transformasi.

Sekarang perubahan lebar persegi dicapai dengan scaleX()transformasi plus a translate()untuk posisinya. Ini harus dalam sintaks transformasi CSS . Menggunakan transformatribut tidak akan berhasil (belum). Oleh karena itu, kita juga membutuhkan transform-originproperti, diatur ke sisi kiri <svg>elemen penutup .

Alih-alih menulis nilai konkret untuk penskalaan, nilai tersebut dinyatakan sebagai variabel dengan nilai default 1 var(--scale, 1):; sama untuk nilai posisi. Nilai untuk variabel diatur dalam styleatribut untuk setiap <use>elemen secara terpisah: style="--scale:2;--posX:20px; --posY:-10px". Perhatikan kebutuhan pxunit penulisan !

#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

Sean berkata:

Jika Elemen Kustom Komponen Web diperluas ke namespace SVG,
penggunaan ulang yang lebih rumit dapat dilakukan

Yang benar, Anda belum bisa membuat elemen SVG kustom .

Tapi Anda bisa membuat Elemen Kustom yang menghasilkan 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>

Elemen Khusus untuk SVG adalah solusi modern untuk banyak peretasan SVG oldskool

Memperbarui

Jika OP menginginkan satu SVG dengan lingkaran, kita dapat menggunakan shadowDOM dan fakta bahwa elemen di dalam lightDOM tidak ditampilkan. Kami bahkan dapat menggunakan elemen yang tidak ditentukan <rect> (dalam namespace HTML) sehingga kami dapat dengan mudah memasukkannya ke dalam string 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)Jawaban StackOverflow terkait: Elemen Kustom dan SVG

2 Sean Aug 20 2020 at 22:28

Ini tidak mungkin. The useelemen menciptakan closed shadow root, yang berarti isinya tidak dapat diakses untuk JS. Meskipun Anda dapat menyetel atribut instance dari elemen yang digunakan kembali secara individual (jika belum ditetapkan pada elemen asli), Anda tidak akan dapat memengaruhi elemen yang merupakan turunan dari elemen yang digunakan kembali.

Pendekatan alternatif akan menggunakan kembali elemen persegi panjang dan lingkaran secara langsung, dan menempatkannya dalam grup baru.

AmirFo Sep 27 2020 at 17:27

The getAttributeMetode tidak bekerja untuk saya tapi ini bekerja:

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>';
    }
});

Penggunaan :

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

Sumber: D