【问题标题】:SVG <symbol> with script changes all the <use> clones带有脚本的 SVG <symbol> 更改了所有 <use> 克隆
【发布时间】:2019-05-10 02:47:50
【问题描述】:

我对此相当缺乏经验,所以要温柔;-)。

我正在尝试在 Inkscape 中制作类似动画图标集的东西。 为了向矩形“符号”添加行为,我向其中添加了一些 Javascript。到目前为止,一切都很好。如果我在“使用”标签的帮助下克隆“符号”并将鼠标悬停在矩形上,它会改变颜色。

问题是:如果我使用“use”标签创建第二个克隆,如果我将鼠标悬停在其中一个上,两个副本都会改变颜色。

这不是我想要的。我希望“use1”能够独立于“use2”改变颜色。同时我希望脚本成为“符号”标签的一部分,而不是“使用”标签的一部分。

示例代码(不成功):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="210mm"
   height="297mm"
   viewBox="0 0 210 297"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.1 r15371"
   sodipodi:docname="rectangle.svg">
  <script
     type="text/javascript"
     href="svg.js"
     id="script5609" />
  <defs
     id="defs2">
    <symbol
       id="symbol7630"
       onmouseover="console.log(evt.target)"
       onmouseout="evt.target.style.fill='blue'">
      <rect
         style="fill:#ff0000;stroke:#00fb00;stroke-width:3.16499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
         id="BigRect"
         width="57.452377"
         height="36.285713"
         x="61.988094"
         y="47.535706" />
      <rect
         style="fill:#ff0000;stroke:#00fb00;stroke-width:3.16499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
         id="SmallRect"
         width="21.166666"
         height="35.529762"
         x="143.63095"
         y="45.267857" />
      <script
         type="text/javascript"
         id="script5613"><![CDATA[
         var element = SVG.get('SmallRect')
         element.style('fill', 'yellow')
         element.click(function() {
           this.style('fill', 'green')
         })
         element.mouseover(function() {
           this.style('fill', 'red')
         })
         element.mouseout(function() {
           this.style('fill', 'blue')
         })
         //element.attr('fill', '#c06')
         //element.fill('#c06')
         //element.stroke(
         ]]></script>
    </symbol>
  </defs>
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="0.75722503"
     inkscape:cx="104.33497"
     inkscape:cy="561.25984"
     inkscape:document-units="mm"
     inkscape:current-layer="layer1"
     showgrid="false"
     inkscape:window-width="1920"
     inkscape:window-height="1017"
     inkscape:window-x="-8"
     inkscape:window-y="-8"
     inkscape:window-maximized="1" />
  <metadata
     id="metadata5">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title />
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <!-- Specify the place where the animation library svg.js can be found -->
    <use
       xlink:href="#symbol7630"
       id="use16221"
       transform="translate(-15.72353,1.3976471)"
       x="0"
       y="0"
       width="100%"
       height="100%" />
    <use
       id="use3984"
       xlink:href="#symbol7630"
       x="0"
       y="0"
       width="100%"
       height="100%"
       transform="translate(-20.449326,79.41301)" />
    <use
       id="use4008"
       xlink:href="#symbol7630"
       x="0"
       y="0"
       width="100%"
       height="100%"
       transform="translate(-37.570503,138.11419)" />
  </g>
</svg>

【问题讨论】:

  • 这是意料之中的事。如果您将行为添加到 &lt;symbol&gt;,它仍然是模板的一部分,并将平等地应用于所有 &lt;use&gt; 克隆。要获得每个克隆的个体行为,需要将行为附加到个体克隆。为了帮助您解决问题,我们需要更详细地描述您正在努力实现的目标以及到目前为止您已经尝试过的目标,最好是 minimal reproducible example
  • 你好ccprog,非常感谢你的回答。 “要获得每个克隆的个体行为,需要将行为附加到个体克隆。” --> 这就是我想要实现的目标,但 scipt 必须驻留在“符号”中。我没有样品,因为我不知道该怎么做。我们的想法是拥有一个带有脚本的 SVG 库,它作用于符号的各个实例而不是符号本身。
  • 我添加了到目前为止我所做的代码......它作用于,而不是。这是一个用 Inkscape 制作的 SVG。我稍后添加了
  • 我可以在 中有一个脚本检查 DOM 中的 ,然后将脚本复制到所有 中,但这有点难看......我不这样做;不知道能不能搞定……

标签: svg scripting


【解决方案1】:

您的示例代码并不理想,因为您尝试做的某些事情或某些事情可以仅使用没有 Javascript 的 CSS 来实现,其他可以使用 SMIL 动画更优雅地完成(再次避免使用 Javascript,但目前的代价是一些浏览器兼容性问题)。但是由于您的问题是从尝试编写脚本开始的,所以我会从那里开始。

无论您做什么,有一点很快就会生效,那就是与&lt;symbol&gt; 关联的脚本将同步执行该符号的所有实例。一条同样严格的规则是,为符号成员元素设置的样式将应用于该元素的所有实例。

但是第二条规则有一些漏洞:您不需要在样式属性中设置样式属性,但 CSS 级联提供了以下机会:a) 为所有适合选择器的元素设置属性,以及 b)从其父级继承属性。诀窍在于:如果您使用 &lt;use&gt; 元素引用 &lt;symbol&gt;,则该实例会从单个 &lt;use&gt; 元素继承样式属性。

您必须做的第一件事是从style 属性中删除fill 属性。这样,它的值可以从父&lt;use&gt;继承。然后,选择样式表中的所有&lt;use&gt; 元素并在那里定义fill。我将这种模式用于大矩形。

请注意:如果您定义 &lt;style&gt; 元素,Inkscape 会将其内容分发给目标元素,并将它们添加到本地 style 属性中。这与 CSS 级联的目的背道而驰,并且会破坏我在这里所描述的内容。 Inkscape 是一个不错的设计工具,但在为 Web 编程时不要依赖它!

小矩形提供了一个纯 CSS 解决方案,用于在悬停时更改颜色:如果您将鼠标悬停在 &lt;use&gt; 元素上,它自己的属性会发生变化,并且属性值会向下继承。您可以设置use:hover {fill: red} 规则,但这会使没有更具体规则的所有元素变为红色。相反,我设置了一个property variable --small-rect-fill: red 并引用它来填充fill:var(--small-rect-fill) 的小矩形。您可以根据需要定义任意数量的变量。

对于脚本,您必须遵循相同的基本路径:更改&lt;use&gt; 元素的属性以使其被继承。直接定位内部的符号实例是不可能的(这个“影子树”的成员是只读的)。这里的问题是您需要一个由每个 &lt;use&gt; 元素上的事件触发的脚本,并且可以区分它们。有两种可能的模式可以解决这个问题。优雅的一个,事件委托,我只去hint at 并使用第二个,更简单的一个:定义你的监听器函数一次,然后将它附加到每个目标元素。

作为抽象问题(为了避免一些兼容性问题,事实证明),我不是直接在&lt;use&gt; 元素上设置样式,而是添加/删除一个更改属性使用值的类变量。

我希望这涵盖了您想到的用例。

var elements = document.querySelectorAll('use');

var onclick = function (event) {
    event.target.classList.add('clicked');
};

var onmouseout = function (event) {
    event.target.classList.remove('clicked');
};

elements.forEach(function (el) {
    el.addEventListener('click', onclick);
    el.addEventListener('mouseout', onmouseout);
});
rect {
    stroke: #00fb00;
    stroke-width: 3.165;
}
use {
    fill: red;
    --small-rect-fill: yellow;
}
use:hover {
    --small-rect-fill: red;
}
use.clicked {
    --small-rect-fill: green;
}
<svg
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   viewBox="0 0 210 297">
  <defs>
    <symbol id="symbol7630">
      <rect
         id="BigRect"
         width="57.452377"
         height="36.285713"
         x="61.988094"
         y="47.535706" />
      <rect
         style="fill:var(--small-rect-fill)"
         id="SmallRect"
         width="21.166666"
         height="35.529762"
         x="143.63095"
         y="45.267857" />
    </symbol>
  </defs>
  <g>
    <use
       xlink:href="#symbol7630"
       transform="translate(-15.72353,1.3976471)" />
    <use
       xlink:href="#symbol7630"
       transform="translate(-20.449326,79.41301)" />
    <use
       xlink:href="#symbol7630"
       transform="translate(-37.570503,138.11419)" />
  </g>
</svg>

【讨论】:

  • 嗨 ccprog,非常感谢您的详尽回答。我将不得不咀嚼一段时间,因为这对我来说都很新,但我看到了一些可能性。佩蒂,它不像我想象的那么直截了当。我计划使用 Inkscape 构建人机界面,这就是我需要脚本的原因(SVG 应该反映机器的状态)
  • 我努力让它工作还没有带来任何有用的东西......我想要实现的是一个独立的代码单元和 SVG 都塞进了一个 。它可以代表一个小型步行木偶或类似的东西。然后,如果我想要 100 个步行木偶,我会它们来创建 100 个步行木偶实例、动画图标或其他任何东西......
  • 我尝试构建一个脚本来用 标签中包含的原始 SVG 和 JS 替换 标签,但我发现自己在努力保持所有 id 的唯一性。使用“self.element1”或“self.element2”之类的东西来引用SVG元素会很好,但我在HTML5中找不到“self”的等价物。
猜你喜欢
  • 2016-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-07
  • 2016-07-02
  • 1970-01-01
  • 2023-03-29
  • 2017-02-09
相关资源
最近更新 更多