HTML SVG повторно использовать группу <g> с <use> и изменять атрибуты внутренних элементов индивидуально для каждого экземпляра
Поэтому я хотел бы повторно использовать сгруппированную фигуру svg и изменить один атрибут одного из элементов внутри группы индивидуально для каждого экземпляра. В следующем упрощенном примере создается второй круг с прямоугольником внутри. Теперь я хочу изменить атрибут «width» прямоугольника «my-rect» индивидуально для каждой формы с помощью javascript. Использование идентификатора 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="#my-group" x="340" y="110"/>
</svg>
Ответы
С небольшими хитростями это возможно. Вы должны воспользоваться наследованием CSS, чтобы получить какое-либо значение свойства внутри теневого элемента. В этом случае для масштабирования и позиционирования прямоугольника будут использоваться пользовательские переменные.
Для этого необходимо немного переписать разметку. Сначала вы пишете свою группу внутри <defs>
элемента, делая его шаблоном для повторного использования, но не визуализируемым сам по себе. Во-вторых, прямоугольник помещается внутри вложенного <svg overflow="visible">
элемента. Задавая этому элементу координаты x / y и оставив их равными 0 для <rect>
элемента, легче отслеживать, где окажется левая сторона прямоугольника после операции преобразования.
Теперь изменение ширины прямоугольника достигается с помощью 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="#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>
Шон сказал:
Если пользовательские элементы веб-компонентов будут расширены до пространства имен 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 - современное решение для многих хаков oldskool SVG
Обновлять
Если OP хочет один SVG с кругами, мы можем использовать shadowDOM и тот факт, что элементы внутри lightDOM не отображаются. Мы даже можем использовать неопределенные <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
Это невозможно. use
Элемент создает closed shadow root, что означает его содержание является недоступным для JS. Хотя вы можете установить атрибуты экземпляров повторно используемого элемента по отдельности (если они не были установлены в исходном элементе), вы не сможете повлиять на элементы, которые являются дочерними элементами повторно используемого элемента.
Альтернативный подход - повторно использовать элементы прямоугольника и круга напрямую и поместить их в новую группу.
Этот 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