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

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

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="/id#my-group" x="340" y="110"/>
</svg>


person Holger Rattenscheid    schedule 20.08.2020    source sumber


Jawaban (4)


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

Markupnya harus ditulis ulang sedikit untuk ini. Pertama, Anda menulis grup Anda di dalam elemen <defs>, menjadikannya templat untuk digunakan kembali, tetapi tidak dirender dengan sendirinya. Kedua, persegi panjang ditempatkan di dalam elemen <svg overflow="visible"> yang disarangkan. Memberi elemen ini koordinat x/y dan membiarkannya pada 0 untuk elemen <rect> akan memudahkan pelacakan di mana sisi kiri persegi panjang akan berakhir setelah operasi transformasi.

Sekarang perubahan lebar persegi dicapai dengan transformasi scaleX() ditambah translate() untuk posisinya. Ini harus dalam sintaks transformasi CSS. Menggunakan atribut transform tidak akan berfungsi (belum). Oleh karena itu, kita juga memerlukan properti transform-origin, yang disetel ke sisi kiri elemen <svg> yang melingkupinya.

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

#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="/id#my-group" x="110" y="110" style="--scale:1"/>
  <use xlink:href="/id#my-group" x="340" y="110" style="--scale:2;--posX:20px; --posY:-10px"/>
</svg>

person ccprog    schedule 20.08.2020
comment
Bagus! Saya belajar sesuatu yang baru. Anda juga dapat mengganti <svg x="-50" y="-50" overflow="visible"> itu dengan <g transform="translate(-50 -50)"> jika Anda tidak menyukai SVG yang bertumpuk. - person Sean; 22.08.2020
comment
Terima kasih, apakah ini juga bisa digunakan dengan ketinggian? Pendekatan naif saya menggunakan ScaleY tidak berhasil. - person Holger Rattenscheid; 25.08.2020
comment
Saya menggunakan contoh Anda dan hanya mengganti X dengan Y. Setelah komentar Anda, saya mencoba kedua variasi yang Anda sebutkan, tetapi keduanya menghasilkan hasil yang sama: Persegi panjang bergerak ke atas atau ke bawah sambil mengubah ketinggiannya (diedit) - person Holger Rattenscheid; 25.08.2020
comment
Saya menemukan kesalahan saya: transform-origin: left meninggalkan asal y di center, jadi harus transform-origin: left top agar berfungsi. - person ccprog; 25.08.2020

Sean berkata:

Jika Elemen Kustom Komponen Web diperluas ke namespace SVG,
penggunaan kembali yang lebih kompleks akan dimungkinkan

Memang benar, Anda belum bisa membuat elemen SVG khusus.

Namun Anda dapat membuat Elemen Khusus 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 Kustom untuk SVG adalah solusi modern untuk banyak peretasan SVG lama

Memperbarui

Jika OP menginginkan satu SVG dengan lingkaran, kita dapat menggunakan shadowDOM dan fakta bahwa elemen di dalam lightDOM tidak ditampilkan. Kita bahkan dapat menggunakan elemen undefinisi <rect> (dalam namespace HTML) sehingga kita 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

person Danny '365CSI' Engelman    schedule 20.08.2020
comment
Anda tidak akan dapat menggunakan elemen khusus tersebut di dalam SVG, seperti permintaan pertanyaan. - person Sean; 22.08.2020
comment
Benar. Elemen Khusus tidak berfungsi dalam Namespace SVG, seperti yang Anda katakan. OP menginginkan lingkaran dengan persegi dinamis. Jika dia menginginkan 3 lingkaran dalam satu SVG, dia harus mengadaptasi Elemen Kustom untuk menghasilkan satu SVG dengan 3 lingkaran. - person Danny '365CSI' Engelman; 22.08.2020
comment
Terima kasih itu juga tampak menjanjikan, tetapi bagian perubahan lebar dengan javascript (setelah dirender) tidak ada di sini. Setidaknya saya tidak tahu bagaimana cara kerjanya dengan pendekatan ini. Oleh karena itu saya menerima jawaban ccprog. - person Holger Rattenscheid; 25.08.2020

Ini tidak mungkin. Elemen use membuat closed shadow root, yang berarti isinya adalah tidak dapat diakses oleh JS. Meskipun Anda dapat menyetel atribut instance dari elemen yang digunakan kembali satu per satu (jika atribut tersebut belum disetel pada elemen asli), Anda tidak akan dapat memengaruhi elemen yang merupakan turunan dari elemen yang digunakan kembali.

Pendekatan alternatifnya adalah dengan menggunakan kembali elemen persegi panjang dan lingkaran secara langsung, dan menempatkannya dalam grup baru.

person Sean    schedule 20.08.2020
comment
Jika Elemen Khusus Komponen Web diperluas ke namespace SVG, penggunaan kembali yang lebih kompleks akan dimungkinkan: github. com/w3c/webcomponents/issues/634 - person Sean; 20.08.2020

Metode getAttribute tidak berhasil untuk saya tetapi ini berhasil:

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

person Amir Fo    schedule 27.09.2020