【问题标题】:Rotate an svg path around its own centre using d3使用 d3 围绕自己的中心旋转 svg 路径
【发布时间】:2019-07-23 17:18:32
【问题描述】:

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

看起来this 的问题是相同的,但 OP 从未跟进过,他们再也无法提供任何代码或小提琴。

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

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 标记可以在小提琴上找到。

【问题讨论】:

    标签: javascript d3.js svg inkscape


    【解决方案1】:

    你可以通过getBBox()获得&lt;path&gt;的位置:

    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>

    【讨论】:

      【解决方案2】:

      虽然 Gerardo 的 answer 显示了显而易见且应用最广泛的解决方案,但还有其他方法可以做到这一点。这个答案有一些实验性的接触(Gerardo 报告它在 Safari 上被破坏),因为它列出了两种非现成的方法。将其视为探索可能性并添加到知识库中,而不是呈现惯用的方式。


      您可以使用transform-origin CSS 属性来控制元素转换的原点,而不是获取边界框并做一些琐碎的数学运算。因为 Firefox 似乎以不同的方式处理这个问题,所以您还应该设置 transform-box:fill-box。然后将旋转的原点设置为50% 50% 将按照您最初预期的方式围绕其中心旋转元素。旁注:围绕中心旋转是 HTML 的默认行为,而 SVG 默认情况下围绕坐标原点旋转。

      这是 Gerardo 演示的更精简版本:

      let toggle = 0;
      const svg = d3.select("svg")
      const knob = d3.select("#switch1")
      svg.on("click", function() {
        const angle = (toggle = 1 - toggle) ? 90 : 0;
        knob.attr("transform", "rotate(" + angle + ")");
      })
      path {
        fill: none;
        stroke: #000000;
        stroke-width: 1.12199998;
        transform-box: fill-box;
        transform-origin: 50% 50%
      }
      
      text {
        font-size: 10px;
        font-family: Sans;
        fill: #000000;
      }
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
      
        <svg width="200" height="100" viewBox="0 0 50 50">
          <g>
            <path id="switch1"
               d="m 35,27 -1,0 0,-9 1,0 z m -5.7,-4 a 5,5 0 0 0 10,0 5,5 0 1 0 -10,0 z" />
            <text x="26" y="14">OFF</text>
            <text x="45" y="26">ON</text>
          </g>
        </svg>

      您甚至可以使用 hidden-checkbox-label 技巧推动信封,完全无需 JavaScript!

      path {
        fill: none;
        stroke: #000000;
        stroke-width: 1.12199998;
        transform-box: fill-box;
        transform-origin: 50% 50%
      }
      
      text {
        font-size: 10px;
        font-family: Sans;
        fill: #000000;
      }
      
      #dummy {
        display: none;
      }
      
      #dummy:checked+label path {
        transform: rotate(90deg);
      }
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
      
      <input type="checkbox" id="dummy">
      
      <label for="dummy">
        <svg width="200" height="100" viewBox="0 0 50 50">
          <g>
            <path id="switch1"
               d="m 35,27 -1,0 0,-9 1,0 z m -5.7,-4 a 5,5 0 0 0 10,0 5,5 0 1 0 -10,0 z" />
            <text x="26" y="14">OFF</text>
            <text x="45" y="26">ON</text>
          </g>
        </svg>
      </label>

      【讨论】:

      • ON 位置也不是以 Firefox(最新)为中心
      • 我想避免与浏览器相关的问题,以及对 css 的过度依赖,无论如何,这个正在使用的实际页面对 JS 有很大的依赖。但无论如何,这对于我提出的其他东西来说实际上是非常有趣的,这将是一种优雅的方式来保持这些页面的最小/无 JS。出于某种原因,您的回答从未通知我,所以我才看到它。
      猜你喜欢
      • 2017-05-04
      • 1970-01-01
      • 2018-01-17
      • 1970-01-01
      • 2013-02-14
      • 2019-10-22
      • 2018-07-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多