简体   繁体   中英

SVG smoothly morphing shape into other predefined shapes with Javascript

  • I want a smooth transition between the shapes (the example below shows a sudden transition just so you get an idea where I need a smooth transition).
  • The order of the shapes is determined by Javascript (for the example I fixed an arbitrary sequence, but in the actual problem user input determines which shape is choosen, so it isn't known beforehand).

example.svg :

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="100%" height="100%" viewBox="0 0 400 400">
<script>
window.animate = function( fromId, toId, next )
{
    return function()
    {
        var elem = document.getElementById( 'elem' );
        /* Here a smooth animation is supposed to happen. */
        elem.setAttribute( 'xlink:href', '#' + toId );

        if( next )
        {
            window.setTimeout( next, 1000 );
        }
    };
};

window.onload = function()
{
    /* The animation order is determined by javascript. */
    var step3 = window.animate( 'path-2', 'path-1', null );
    var step2 = window.animate( 'path-1', 'path-2', step3 );
    var step1 = window.animate( 'path-0', 'path-1', step2 );
    var step0 = window.animate( 'path-0', 'path-0', step1 );

    step0();
};
</script>

<style>path{stroke:#000;}</style>

<defs>
<path id="path-0" style="fill:#fcc" d="M0,0 h100 v100 h-100 v-100" />
<path id="path-1" style="fill:#ccf" d="M0,0 h50 l50 50 l-100 50 v-100" />
<path id="path-2" style="fill:#cfc" d="M0,0 h150 l-50 50 l-100 50 v-100" />
</defs>

<use id="elem" xlink:href="#path-0" x="150" y="150" />
</svg>

Supposedly it's somehow doable with <animate> , but I can't get it to work.

animate.svg :

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="100%" height="100%" viewBox="0 0 400 400">
<script>
window.set_animation = function( animId, fromId, toId )
{
    var anim = document.getElementById( animId );
    var from = document.getElementById( fromId );
    var to = document.getElementById( toId );
    anim.setAttribute( 'from', from.getAttribute( 'd' ) );
    anim.setAttribute( 'to', to.getAttribute( 'd' ) );
};

window.onload = function()
{
    /* The animation order is determined by javascript. */
    window.set_animation( 'anim-0', 'path-0', 'path-1' );
    window.set_animation( 'anim-1', 'path-1', 'path-2' );
    window.set_animation( 'anim-2', 'path-2', 'path-1' );

    /* Can start animation only once animation steps are defined. */
    var anim = document.getElementById( 'anim-0' );
    anim.beginElement();
};
</script>

<style>path{stroke:#000;}</style>

<defs>
    <path id="path-0" style="fill:#fcc" d="M0,0 l100,0 l0,100 l-100,0 l0,-100" />
    <path id="path-1" style="fill:#ccf" d="M0,0 l50,0 l50,50 l-100,50 l0,-100" />
    <path id="path-2" style="fill:#cfc" d="M0,0 l150,0 l-50,50 l-100,50 l0,-100" />
</defs>

<path id="elem" x="150" y="150" d="">
    <animate id="anim-0" begin="indefinite" attributeType="XML" attributeName="d" dur="2s" from="[set by javascript]" to="[set by javascript]" />
    <animate id="anim-1" begin="anim-1.end" attributeType="XML" attributeName="d" dur="2s" from="[set by javascript]" to="[set by javascript]" />
    <animate id="anim-2" begin="anim-2.end" attributeType="XML" attributeName="d" dur="2s" from="[set by javascript]" to="[set by javascript]" />
</path>
</svg>

With <animate> the rule is that the two paths have to:

  1. have the same number of path elements
  2. have matching path commands

Your animations won't work because the paths are incompatible:

path-0: M h v h v
path-1: M h l l v

It's easier to put all the values into a single animation as I've done here. If you don't then you have to start each subsequent animation as the previous one finishes which is doable but more complicated.

You'll need fakeSmile or the Chrome SMIL shim for IE/Chrome but this does play without plugins on Firefox.

 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 400 400"> <script> function create_animation(animId, paths, attribute) { var anim = document.getElementById( animId ); var values = paths.map(function(item) { return document.getElementById(item).getAttribute(attribute) }).join(';'); anim.setAttribute( 'values', values ); } window.set_animation = function( animId, paths ) { create_animation(animId, paths, 'd'); create_animation(animId + '-colour', paths, 'fill'); }; window.onload = function() { /* The animation order is determined by javascript. */ window.set_animation( 'anim-0', ['path-0', 'path-1', 'path-2'] ); }; </script> <style>path{stroke:#000;}</style> <defs> <path id="path-0" fill="#fcc" d="M0,0 l100,0 l0,100 l-100,0 l0,-100" /> <path id="path-1" fill="#ccf" d="M0,0 l50,0 l50,50 l-100,50 l0,-100" /> <path id="path-2" fill="#cfc" d="M0,0 l150,0 l-50,50 l-100,50 l0,-100" /> </defs> <path> <animate id='anim-0' dur="3s" attributeName='d' fill="freeze"/> <animate id='anim-0-colour' dur="3s" attributeName='fill' fill="freeze"/> </path> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM