首页 > 解决方案 > SVG用脚本改变所有克隆

问题描述

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

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

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

那不是我想要的。我希望“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>

标签: svgscripting

解决方案


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

无论您做什么,都会快速执行的一件事是,与 a 关联的脚本<symbol>将为该符号的所有实例同步执行。同样严格的规则是符号成员元素的样式集将应用于该元素的所有实例。

但是第二条规则有一些漏洞:您不需要在样式属性中设置样式属性,但是 CSS 级联提供了以下机会:a) 为所有适合选择器的元素设置属性,并且 b) 继承该属性从它的父母那里。这里有个窍门:如果你引用一个<symbol>元素<use>,实例会从单个元素继承样式属性<use>

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

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

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

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

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

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

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>


推荐阅读