首页 > 解决方案 > 使用 d3 围绕自己的中心旋转 svg 路径

问题描述

我有一个用 Inkscape 制作的 SVG 路径(开关图标),我试图以编程方式围绕它自己的中心旋转 90 度。在我的正确网页中,我有许多这些图标都由那里的 ID 引用,因此这需要是我可以应用的通用解决方案。

看起来这个问题是相同的,但 OP 从未跟进过,他们永远无法提供更多代码或小提琴。

我这里有一个小提琴,它显示了我的示例的所有内容。

knob = d3.select("#switch1")
knob.attr('transform', 'rotate(0 0 0)')

是我用于旋转的基本代码。我需要知道如何计算 x 和 y 值,以便可以使任何给定的图标指向示例中的开/关文本。或使用另一种获得相同旋转效果的方法d3

如果我这样做了,knob.attr('transform', 'rotate(90 0 0)')那么图标就会从页面上消失——我以为0 0是围绕它的相对中心旋转?

如果我手动这样做,knob.attr('transform', 'rotate(90 15 15)')我可以将其保留在页面上,但在错误的位置。

SVG 路径由以下部分组成:

       <path
       id="switch1"
       style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.12199998;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="m 35.741778,27.194664 -1.082299,0 0,-8.512001 1.082299,-9e-6 z m -5.563199,-4.274211 a 5.00005,5.000032 0 0 0 9.999999,2e-5 5.00005,5.000032 0 1 0 -9.999999,-2e-5 z"
       inkscape:connector-curvature="0" />

完整的 SVG 标记可以在小提琴上找到。

标签: javascriptd3.jssvginkscape

解决方案


您可以<path>使用getBBox()获得位置:

const centre = knob.node().getBBox();

然后,只需计算其中心:

knob.attr("transform", "rotate(" + angle + ", " + 
    (centre.x + centre.width / 2) + ", " + (centre.y + centre.height / 2) + ")");

angle显然,这是您想要的角度。

这是一个使用 SVG(但更小)的演示,单击 SVG 中的任意位置以旋转路径:

let toggle = 0;
let svg = d3.select("svg")
const knob = d3.select("#switch1")
const centre = knob.node().getBBox();
svg.on("click", function() {
  const angle = (toggle = 1 - toggle) ? 90 : 0;
  knob.attr("transform", "rotate(" + angle + ", " + (centre.x + centre.width / 2) + ", " + (centre.y + centre.height / 2) + ")");
})
svg {
 border: 1px solid gray;
 background-color: lavender;
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="200" height="100" viewBox="0 0 50 50" id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="bar.svg">
  <defs
     id="defs4" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="11.2"
     inkscape:cx="34.921875"
     inkscape:cy="1047.7595"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="true"
     inkscape:window-width="1920"
     inkscape:window-height="1033"
     inkscape:window-x="-4"
     inkscape:window-y="-4"
     inkscape:window-maximized="1">
    <inkscape:grid
       type="xygrid"
       id="grid4157" />
  </sodipodi:namedview>
  <metadata
     id="metadata7">
    <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">
    <path
       id="switch1"
       style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.12199998;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="m 35.741778,27.194664 -1.082299,0 0,-8.512001 1.082299,-9e-6 z m -5.563199,-4.274211 a 5.00005,5.000032 0 0 0 9.999999,2e-5 5.00005,5.000032 0 1 0 -9.999999,-2e-5 z"
       inkscape:connector-curvature="0" />
    <text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="25.714285"
       y="13.612206"
       id="text4135"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4137"
         x="25.714285"
         y="13.612206"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">OFF</tspan></text>
    <text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="44.566826"
       y="26.086744"
       id="text4135-2"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4137-2"
         x="44.566826"
         y="26.086744"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">ON</tspan></text>
  </g>
</svg>

您还可以添加过渡:

let toggle = 0;
let svg = d3.select("svg")
const knob = d3.select("#switch1")
const centre = knob.node().getBBox();
const centreX = centre.x + centre.width / 2;
const centreY = centre.y + centre.height / 2;
svg.on("click", function() {
  const angle = (toggle = 1 - toggle) ? 90 : 0;
  knob.transition()
    .ease(d3.easeLinear)
    .attrTween("transform", function() {
      return d3.interpolateString("rotate(" + (90 - angle) + ", " + centreX + ", " + centreY + ")", "rotate(" + angle + ", " + centreX + ", " + centreY + ")")
    })
})
svg {
  border: 1px solid gray;
  background-color: lavender;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="200" height="100" viewBox="0 0 50 50" id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="bar.svg">
  <defs
     id="defs4" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="11.2"
     inkscape:cx="34.921875"
     inkscape:cy="1047.7595"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="true"
     inkscape:window-width="1920"
     inkscape:window-height="1033"
     inkscape:window-x="-4"
     inkscape:window-y="-4"
     inkscape:window-maximized="1">
    <inkscape:grid
       type="xygrid"
       id="grid4157" />
  </sodipodi:namedview>
  <metadata
     id="metadata7">
    <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">
    <path
       id="switch1"
       style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.12199998;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="m 35.741778,27.194664 -1.082299,0 0,-8.512001 1.082299,-9e-6 z m -5.563199,-4.274211 a 5.00005,5.000032 0 0 0 9.999999,2e-5 5.00005,5.000032 0 1 0 -9.999999,-2e-5 z"
       inkscape:connector-curvature="0" />
    <text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="25.714285"
       y="13.612206"
       id="text4135"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4137"
         x="25.714285"
         y="13.612206"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">OFF</tspan></text>
    <text
       xml:space="preserve"
       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       x="44.566826"
       y="26.086744"
       id="text4135-2"
       sodipodi:linespacing="125%"><tspan
         sodipodi:role="line"
         id="tspan4137-2"
         x="44.566826"
         y="26.086744"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:start;writing-mode:lr-tb;text-anchor:start">ON</tspan></text>
  </g>
</svg>


推荐阅读