Home > Net >  How do i avoid the mangled svg path in the svg morph transition?
How do i avoid the mangled svg path in the svg morph transition?

Time:10-16

I ahev the following svg created in inkscape

<?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="345.70975"
   height="352.91309"
   viewBox="0 0 91.469035 93.374923"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.2 5c3e80d, 2017-08-06"
   sodipodi:docname="double-square.svg">
  <defs
     id="defs2" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="0.70710678"
     inkscape:cx="226.41877"
     inkscape:cy="36.880616"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="false"
     units="px"
     inkscape:pagecheckerboard="false"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0"
     inkscape:window-width="1440"
     inkscape:window-height="801"
     inkscape:window-x="0"
     inkscape:window-y="1"
     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></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-21.120772,-24.484056)">
    <path
       style="opacity:1;fill:none;fill-opacity:1;stroke:#800080;stroke-width:1.29393971;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="m 22.242817,24.963907 h 89.459313 c 0.10307,0 0.18605,0.08678 0.18605,0.194558 v 92.051005 c 0,0.10779 -0.0829,0.19456 -0.18605,0.19456 H 22.242817 c -0.103073,0 -0.186053,-0.0867 -0.186053,-0.19456 V 25.158465 c 0,-0.107785 0.08299,-0.194558 0.186053,-0.194558 z"
       id="rect818"
       inkscape:connector-curvature="0" />
    <path
       style="opacity:1;fill:none;fill-opacity:1;stroke:#ff0000;stroke-width:1.31637728;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="m 96.672725,103.80812 c -22.695046,9.46462 -52.584739,24.28389 -62.190302,0.78977 -9.605563,-23.494119 -20.853186,-57.321821 1.841865,-66.786436 22.695046,-9.464613 57.771554,-23.161339 67.377122,0.332775 9.60556,23.494112 15.66636,56.199275 -7.028685,65.663891 z"
       id="path815"
       inkscape:connector-curvature="0"
       sodipodi:nodetypes="sssss" />
  </g>
</svg>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Now when i use anime.js to morph this svg from square to rounded square, i see the square is mangled quite a bit and the transition into a square is not quite right.

let shapes = [
    {
        d: "m 22.242817,24.963907 h 89.459313 c 0.10307,0 0.18605,0.08678 0.18605,0.194558 v 92.051005 c 0,0.10779 -0.0829,0.19456 -0.18605,0.19456 H 22.242817 c -0.103073,0 -0.186053,-0.0867 -0.186053,-0.19456 V 25.158465 c 0,-0.107785 0.08299,-0.194558 0.186053,-0.194558 z"
    },
    {
        d: "m 96.672725,103.80812 c -22.695046,9.46462 -52.584739,24.28389 -62.190302,0.78977 -9.605563,-23.494119 -20.853186,-57.321821 1.841865,-66.786436 22.695046,-9.464613 57.771554,-23.161339 67.377122,0.332775 9.60556,23.494112 15.66636,56.199275 -7.028685,65.663891 z"
    }
]



var morph1 = anime({
    targets: '.square-path',
    d: [
        {value: shapes[0].d},
        {value: shapes[1].d}
    ],
    duration: 3000,
    direction: 'alternate',
    autoplay: true,
    easing: 'linear',
    elasticity: 100,
    loop: true
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.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="345.70975"
   height="352.91309"
   viewBox="0 0 91.469035 93.374923"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.2 5c3e80d, 2017-08-06"
   sodipodi:docname="double-square.svg">
  <defs
     id="defs2" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="0.70710678"
     inkscape:cx="226.41877"
     inkscape:cy="36.880616"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="false"
     units="px"
     inkscape:pagecheckerboard="false"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0"
     inkscape:window-width="1440"
     inkscape:window-height="801"
     inkscape:window-x="0"
     inkscape:window-y="1"
     inkscape:window-maximized="1" />
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-21.120772,-24.484056)">
    <path
       style="opacity:1;fill:none;fill-opacity:1;stroke:#800080;stroke-width:1.29393971;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="m 22.242817,24.963907 h 89.459313 c 0.10307,0 0.18605,0.08678 0.18605,0.194558 v 92.051005 c 0,0.10779 -0.0829,0.19456 -0.18605,0.19456 H 22.242817 c -0.103073,0 -0.186053,-0.0867 -0.186053,-0.19456 V 25.158465 c 0,-0.107785 0.08299,-0.194558 0.186053,-0.194558 z"
       id="rect818"
       class="square-path"
       inkscape:connector-curvature="0" />
  </g>
</svg>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

How do i avoid the mangled square in the svg morph transition ?

CodePudding user response:

  1. Make sure your two shapes have the same number of segments and segment types.

    This is actually an SVG rule, but I believe anime.js helps you work around this rule by modifying the paths so that they work. But it's better to control this yourself rather than rely on a program to guess your intentions.

    Your start path has 8 or 9 path segments:

    m 22.242817,24.963907
    h 89.459313
    c 0.10307,0 0.18605,0.08678 0.18605,0.194558
    v 92.051005
    c 0,0.10779 -0.0829,0.19456 -0.18605,0.19456
    H 22.242817
    c -0.103073,0 -0.186053,-0.0867 -0.186053,-0.19456
    V 25.158465
    c 0,-0.107785 0.08299,-0.194558 0.186053,-0.194558
    z
    

    but your end path has only four:

    m 96.672725,103.80812
    c -22.695046,9.46462 -52.584739,24.28389 -62.190302,0.78977
    c -9.605563,-23.494119 -20.853186,-57.321821 1.841865,-66.786436
    c 22.695046,-9.464613 57.771554,-23.161339 67.377122,0.332775
    c 9.60556,23.494112 15.66636,56.199275 -7.028685,65.663891
    z
    

    Both paths should have the matching path commands. For example, both paths should be M H C V C H C V C Z, or both should be M C C C C Z.

  2. Arrange your path points so that the are positioned close to where you want them to end up in the other shape.

    For instance, in a circle-to-square transition, make the circle have four points and position thos four points at the NW, NE, SW, and SE.

    enter image description here

If instead they are at random positions relative to the other shape, you'll get an animation that apears to tie itself into a not and then unscramble.

In the example below, both paths have exactly four bezier curve path commands (c). And their end points are positioned close to the corresponding position on the other shape. Note that I have used curve path commans (C) even for the straight lines of the square. You can make a curve look straight, but you can't make a straight line bend.

let shapes = [
    {
        d: "M 20, 20 c 100,0 200,0 300,0 c 0,100 0,200 0,300 c -100,0 -200,0 -300,0 c 0,-100 0,-200 0,-300 z"
    },
    {
        d: "M 89.289322,89.289322 c 42.392448,-38.369033 104.952908,-35.372751 141.421358,0 39.05243,39.052428 39.05243,102.368928 0,141.421358 -39.05243,39.05243 -102.36893,39.05243 -141.421358,0 -39.05243,-39.05243 -39.052429,-102.36893 0,-141.421358 z"
    }
]



var morph1 = anime({
    targets: '.square-path',
    d: [
        {value: shapes[0].d},
        {value: shapes[1].d}
    ],
    duration: 3000,
    direction: 'alternate',
    autoplay: true,
    easing: 'linear',
    elasticity: 100,
    loop: true
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg
   width="345.70975"
   height="352.91309"
   viewBox="0 0 340 340"
   id="svg8">
  <g
     id="layer1">
    <path
       style="opacity:1;fill:none;fill-opacity:1;stroke:#800080;stroke-width:1.29393971;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
       d="M 20, 20 c 100,0 200,0 300,0 c 0,100 0,200 0,300 c -100,0 -200,0 -300,0 c 0,-100 0,-200 0,-300 z"
       id="rect818"
       class="square-path" />
  </g>
</svg>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related