HTML SVG นำกลุ่ม ‹g› มาใช้ซ้ำด้วย ‹use› และเปลี่ยนคุณสมบัติขององค์ประกอบภายในแยกกันสำหรับแต่ละอินสแตนซ์

ดังนั้นฉันจึงต้องการนำรูปร่าง svg ที่จัดกลุ่มมาใช้ซ้ำ และเปลี่ยนแอตทริบิวต์หนึ่งขององค์ประกอบภายในกลุ่มแยกกันสำหรับแต่ละอินสแตนซ์ ตัวอย่างแบบง่ายต่อไปนี้จะสร้างวงกลมที่สองโดยมีสี่เหลี่ยมผืนผ้าอยู่ข้างใน ตอนนี้ฉันต้องการเปลี่ยนแอตทริบิวต์ width ของสี่เหลี่ยมผืนผ้า my-rect แยกกันสำหรับแต่ละรูปร่างด้วย javascript การใช้ id my-rect จะเปลี่ยนความกว้างของสี่เหลี่ยมทั้งสอง แต่ฉันต้องการเปลี่ยนเพียงอันเดียว

เป้าหมายของฉัน (หากแนวทางของฉันไร้สาระ): ฉันต้องวาดรูปร่างเหล่านี้หลาย ๆ รูป และสิ่งเดียวที่แตกต่างคือตำแหน่งและความกว้างของสี่เหลี่ยมผืนผ้า

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


person Holger Rattenscheid    schedule 20.08.2020    source แหล่งที่มา


คำตอบ (4)


ด้วยกลอุบายเล็กน้อยก็เป็นไปได้ คุณต้องใช้ประโยชน์จากการสืบทอด CSS เพื่อรับค่าคุณสมบัติบางอย่างภายในองค์ประกอบเงา ในกรณีนี้ จะเป็นตัวแปรที่กำหนดเองซึ่งจะใช้ในการปรับขนาดและตำแหน่งสี่เหลี่ยมผืนผ้า

มาร์กอัปจะต้องเขียนใหม่เล็กน้อยสำหรับสิ่งนี้ ขั้นแรก คุณเขียนกลุ่มของคุณภายในองค์ประกอบ <defs> ทำให้เป็นเทมเพลตสำหรับใช้ซ้ำ แต่ไม่ได้แสดงผลด้วยตัวเอง ประการที่สอง วางสี่เหลี่ยมผืนผ้าไว้ภายในองค์ประกอบ <svg overflow="visible"> ที่ซ้อนกัน การให้พิกัด x/y แก่องค์ประกอบนี้และปล่อยให้องค์ประกอบ <rect> อยู่ที่ 0 ทำให้ง่ายต่อการติดตามว่าด้านซ้ายของสี่เหลี่ยมผืนผ้าจะไปสิ้นสุดที่ใดหลังจากการดำเนินการเปลี่ยนรูป

ตอนนี้ การเปลี่ยนแปลงความกว้างของสี่เหลี่ยมผืนผ้าสามารถทำได้ด้วยการแปลง scaleX() บวก translate() สำหรับตำแหน่ง นี้ต้องอยู่ในไวยากรณ์การแปลง 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="/th#my-group" x="110" y="110" style="--scale:1"/>
  <use xlink:href="/th#my-group" x="340" y="110" style="--scale:2;--posX:20px; --posY:-10px"/>
</svg>

person ccprog    schedule 20.08.2020
comment
ดี! ฉันได้เรียนรู้สิ่งใหม่ คุณยังสามารถแทนที่ <svg x="-50" y="-50" overflow="visible"> ด้วย <g transform="translate(-50 -50)"> ได้ หากคุณไม่ชอบการซ้อน SVG - person Sean; 22.08.2020
comment
ขอบคุณครับ ใช้กับส่วนสูงได้ไหมครับ วิธีการไร้เดียงสาของฉันโดยใช้ ScaleY ไม่ได้ผล - person Holger Rattenscheid; 25.08.2020
comment
ฉันใช้ตัวอย่างของคุณและเพิ่งสลับ X ด้วย Y หลังจากความคิดเห็นของคุณ ฉันลองทั้งสองรูปแบบที่คุณพูดถึง แต่ทั้งสองอย่างให้ผลลัพธ์เดียวกัน: สี่เหลี่ยมผืนผ้าเลื่อนขึ้นหรือลงในขณะที่เปลี่ยนความสูง (แก้ไข) - person Holger Rattenscheid; 25.08.2020
comment
ฉันพบข้อผิดพลาด: transform-origin: left ปล่อยให้จุดกำเนิด y อยู่ที่ center ดังนั้นจึงต้องเป็น transform-origin: left top จึงจะใช้งานได้ - person ccprog; 25.08.2020

ฌอน กล่าวว่า:

หากองค์ประกอบที่กำหนดเองของ Web Components ได้รับการขยายไปยังเนมสเปซ 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 เป็นโซลูชันที่ทันสมัยสำหรับการแฮ็ก SVG ของ oldskool จำนวนมาก

อัปเดต

หาก OP ต้องการ หนึ่ง SVG ที่มีวงกลม เราสามารถใช้ shadowDOM และความจริงที่ว่าองค์ประกอบภายใน lightDOM จะไม่แสดง เรายังสามารถใช้องค์ประกอบ unknown <rect> (ในเนมสเปซ HTML) เพื่อให้เราสามารถแทรกองค์ประกอบเหล่านั้นลงในสตริง 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

person Danny '365CSI' Engelman    schedule 20.08.2020
comment
คุณจะไม่สามารถใช้องค์ประกอบที่กำหนดเองนั้นภายใน SVG ได้ เช่นเดียวกับคำขอคำถาม - person Sean; 22.08.2020
comment
ถูกต้อง. องค์ประกอบที่กำหนดเองไม่ทำงานภายใน SVG Namespace อย่างที่คุณพูด OP ต้องการวงกลมที่มีเส้นตรงแบบไดนามิก หากเขาต้องการวงกลม 3 วงใน หนึ่ง SVG เขาจะต้องปรับองค์ประกอบที่กำหนดเองเพื่อส่งออก SVG หนึ่ง ที่มีวงกลม 3 วง - person Danny '365CSI' Engelman; 22.08.2020
comment
ขอบคุณที่ดูมีแนวโน้มเช่นกัน แต่ส่วนความกว้างของการเปลี่ยนแปลงด้วยจาวาสคริปต์ (หลังจากแสดงผลแล้ว) หายไปที่นี่ อย่างน้อยฉันก็ไม่รู้ว่ามันจะทำงานอย่างไรกับแนวทางนี้ ดังนั้นฉันจึงยอมรับคำตอบของ ccprog - person Holger Rattenscheid; 25.08.2020

สิ่งนี้เป็นไปไม่ได้ องค์ประกอบ use สร้าง closed shadow root ซึ่งหมายความว่าเนื้อหาประกอบด้วย ไม่สามารถเข้าถึง JS แม้ว่าคุณจะสามารถตั้งค่าแอตทริบิวต์ของอินสแตนซ์ขององค์ประกอบที่นำมาใช้ซ้ำได้เป็นรายบุคคล (หากไม่ได้ตั้งค่าไว้ในองค์ประกอบดั้งเดิม) คุณจะไม่สามารถส่งผลกระทบต่อองค์ประกอบที่เป็นลูกขององค์ประกอบที่นำมาใช้ซ้ำได้

อีกวิธีหนึ่งคือนำองค์ประกอบสี่เหลี่ยมและวงกลมกลับมาใช้ใหม่โดยตรง และวางไว้ในกลุ่มใหม่

person Sean    schedule 20.08.2020
comment
หากองค์ประกอบที่กำหนดเองของ Web Components ได้รับการขยายเป็นเนมสเปซ SVG จะสามารถใช้ซ้ำที่ซับซ้อนมากขึ้นได้: github com/w3c/webcomponents/issues/634 - person Sean; 20.08.2020

วิธี 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

person Amir Fo    schedule 27.09.2020