Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVG circle shape morphing

How to make an circle SVG with shape morphing ?

I am trying to make circle with organic movement.

But as you can see pixels seem glitchy.

Any ideas on how to make it better looking? It is even the right way I am using to do it? I am not an expert in svg nor in shape morphing.

class App extends React.Component {
  render() {
    return (
      <div>
       <svg width="200px" height="200px" viewBox="0 0 120 120">
    <defs>
        <filter id="distort">
            <feTurbulence baseFrequency=".02" type="fractalNoise" />
            <feColorMatrix type="hueRotate" values="0">
                <animate attributeName="values" from="0" to="360" dur="1s" repeatCount="indefinite" />
            </feColorMatrix>
            <feDisplacementMap in="SourceGraphic" xChannelSelector="R" yChannelSelector="B" scale="20">
                <animate attributeName="scale" values={Math.round(Math.random() * 20) + ';' + Math.round(Math.random() * 10) + ';' + Math.round(Math.random() * 10) + ';' + Math.round(Math.random() * 10) + ';'}  dur="5s" repeatCount="indefinite" />
            </feDisplacementMap>
        </filter>
    </defs>
    <circle filter="url(#distort)" cx="60" cy="60" r="30" />
</svg>
      </div>
    );
  }
}

// Render it
ReactDOM.render(
  <App />,
  document.getElementById("app")
);
svg {
    stroke-width: 1;
    stroke: #293133;
    stroke-linecap: round;
    fill: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
like image 723
crg Avatar asked May 06 '21 11:05

crg


3 Answers

I think the glitching effect is happening because the feTurbulence filter outputs 8-bit integer values in the R, G and B channels. This quantization makes it hard to achieve smooth variation in the displacement mapping.

Anyway, you'll get better results with an animated vector curve. Here's an example that animates a polygon with 50 vertices. You could reduce the number of points by using quadratic or cubic Bézier curves instead of straight-line segments, but this should at least get you started:

window.onload = function() {
    var radius = 60, npoints = 50, nwaves = 8;
    var min_amp = 0.5, max_amp = 2.0;       // Spatial amplitude
    var min_freq = 0.1, max_freq = 0.2;     // Spatial frequency
    var min_speed = 0.05, max_speed = 0.1;  // Temporal speed
    var time_step = 0.2;
    
    // Create path data for initial circle
    var base_coords = [];
    for (var i=0; i<=npoints; i++) {
        var x = Math.sin(i * 2 * Math.PI / npoints) * radius;
        var y = Math.cos(i * 2 * Math.PI / npoints) * radius;
        base_coords.push([x,y]);
    }
    
    // Create wave data for distortion
    var wave_data = [];
    for (var i=0; i<nwaves; i++) {
        var amp = Math.random() * (max_amp - min_amp) + min_amp;
        var freq = Math.random() * (max_freq - min_freq) + min_freq;
        var speed = Math.random() * (max_speed - min_speed) + min_speed;
        var angle = Math.random() * 2 * Math.PI;
        wave_data.push([amp,freq,speed,angle]);
    }
    
    var ticks = 0;
    var update_blob = function() {
        ticks++;
        var blob_coords = [];
        for (var i=0; i<base_coords.length; i++) {
            // Fetch base coordinate
            var x = base_coords[i][0];
            var y = base_coords[i][1];
            // Distort using wave data
            for (var j=0; j<wave_data.length; j++) {
                // Rotate x & y to wave orientation
                var s = Math.sin(wave_data[j][3]);
                var c = Math.cos(wave_data[j][3]);
                var tx = x * c + y * s;
                var ty = x * -s + y * c;
                // Shift along x axis using wave parameters and x value
                tx += Math.sin(tx * wave_data[j][1] + ticks * wave_data[j][2]) * wave_data[j][0];
                // Rotate back to original orientation
                x = tx * c + ty * -s;
                y = tx * s + ty * c;
            }
            blob_coords.push([x,y]);
        }
        var d = "M";
        for (var i=0; i<blob_coords.length; i++) {
            d += " " + blob_coords[i][0].toFixed(2) + " " + blob_coords[i][1].toFixed(2);
        }
        d += "Z";
        // console.log(d);
        document.getElementById("blob").setAttribute("d", d);
        // if (ticks == 1) alert(d);
    }
    setInterval(update_blob, 20);
}
<svg width="150" height="150" viewBox="-75 -75 150 150">
<path id="blob" d="M0 0 0 0Z" stroke="#000" stroke-width="2" fill="none" />
</svg>
like image 106
r3mainer Avatar answered Oct 24 '22 23:10

r3mainer


Any ideas on how to make it better looking ?

It is possible to implement morphing of circle borders by changing the d attribute of the patch

Implementation of morphing step by step:

  • Loading the circle into the vector editor
  • Add additional anchor points. Save the file in SVG format. This will be the starting path for morphing

enter image description here

  • Move the node points. We save the file. This will be the final path for morphing.

enter image description here

  • We write an animation command to change the d attribute. The values of this attribute change to

values="path Start; path Finish; path Start"

Note: that the Start and Finish paths are separated by semicolons.

Below is the complete code. Animation will start after clicking

<svg id="svg1" width="200px" height="200px" viewBox="0 0 120 120"> 

<path
     style="fill:none;stroke:#000000;stroke-width:2"
     d="M 90,60 C 90,68.642693 86.345292,76.431784 80.497023,81.906123 75.133823,86.926409 67.925849,90 60,90 51.779957,90 44.332072,86.694001 38.913442,81.339102 33.410134,75.90052 30,68.3485 30,60 30,52.173162 32.99728,45.046376 37.906648,39.704832 43.389864,33.738925 51.258296,30 60,30 68.057492,30 75.373063,33.17654 80.762728,38.345633 86.455778,43.805696 90,51.488949 90,60 Z">
       <animate
        attributeName="d"
        begin="svg1.click"
        dur="1.5s"
        fill="freeze"
        repeatCount="indefinite"
        restart="whenNotActive"
        values="
            M 90,60 C 90,68.642693 86.345292,76.431784 80.497023,81.906123 75.133823,86.926409 67.925849,90 60,90 51.779957,90 44.332072,86.694001 38.913442,81.339102 33.410134,75.90052 30,68.3485 30,60 30,52.173162 32.99728,45.046376 37.906648,39.704832 43.389864,33.738925 51.258296,30 60,30 68.057492,30 75.373063,33.17654 80.762728,38.345633 86.455778,43.805696 90,51.488949 90,60 Z;
            
            m 92.542373,60 c 0,8.642693 -9.247929,11.982631 -15.096197,17.45697 -5.363201,5.020286 -9.647445,15.212522 -17.573295,15.212522 -8.220042,0 -9.947589,-10.678881 -15.366218,-16.03378 -5.503309,-5.438582 -17.176155,-8.541449 -17.176155,-16.889949 0,-7.826839 9.480331,-11.267184 14.389699,-16.608727 5.483217,-5.965908 10.046563,-16.187883 18.788268,-16.187883 8.057491,0 12.195097,9.786709 17.584761,14.955802 C 83.786287,47.365018 92.542373,51.488949 92.542373,60 Z;
            
            M 90,60 C 90,68.642693 86.345292,76.431784 80.497023,81.906123 75.133823,86.926409 67.925849,90 60,90 51.779957,90 44.332072,86.694001 38.913442,81.339102 33.410134,75.90052 30,68.3485 30,60 30,52.173162 32.99728,45.046376 37.906648,39.704832 43.389864,33.738925 51.258296,30 60,30 68.057492,30 75.373063,33.17654 80.762728,38.345633 86.455778,43.805696 90,51.488949 90,60 Z" />
                          
  </path> 

</svg>

Another example of morphing from a circle to a triangle, then to a rectangle and back to a circle

The paths of all shapes are drawn in a vector editor. For the animation to be smooth, without jerking, two conditions must be met when creating the path:

  • The number of anchor points must be the same in all shapes
  • The type of anchor points with the same location from the beginning, must be the same in all shapes

<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink" width="600" height="600" viewBox="0 0 400 400" >
     <rect width="100%" height="100%" fill="#2772C7" />
 <path  
     style="stroke:white; fill:none;stroke-width:18; stroke-linecap:round; stroke-dasharray:0,15"
     d="m 350,200 c 0,27.23032 -7.25588,52.76593 -19.93968,74.77889 C 304.15623,319.7359 255.6124,350 200,350 144.67416,350 96.344223,320.04698 70.34191,275.47268 57.409394,253.30319 50,227.51687 50,200 50,117.15729 117.15729,50 200,50 c 82.84271,0 150,67.15729 150,150 z">
            <!-- Animation of morphing circle into a triangle and a rectangle -->
      <animate
        attributeName="d"
        begin="svg1.click"
        dur="3s"
        fill="freeze"
        repeatCount="3"
        restart="whenNotActive"
        values="
            m 350,200 c 0,27.23032 -7.25588,52.76593 -19.93968,74.77889 C 304.15623,319.7359 255.6124,350 200,350 144.67416,350 96.344223,320.04698 70.34191,275.47268 57.409394,253.30319 50,227.51687 50,200 50,117.15729 117.15729,50 200,50 c 82.84271,0 150,67.15729 150,150 z;
            
            m 285,200 c 15,25 55,90 65,110 -45,0 -94.3876,0 -150,0 -55.32584,0 -85,0 -150,-0.0962 C 64.378221,285 99.019238,225 113.45299,200 153.86751,130 170,100 200,50 c 45,79.01924 48.45299,85 85,150 z;
            
            m 285,200 c 15,25 55,90 65,110 -45,0 -94.3876,0 -150,0 -55.32584,0 -85,0 -150,-0.0962 C 64.378221,285 99.019238,225 113.45299,200 153.86751,130 170,100 200,50 c 45,79.01924 48.45299,85 85,150 z;
            
            m 350,50 c 0,40 0,240 0,260 -45,0 -94.3876,0 -150,0 -55.32584,0 -85,0 -150,-0.0962 C 50,285 50,85 50,50 c 70,0 105,0 150,0 80,0 100,0 150,0 z;
            
            m 350,50 c 0,40 0,240 0,260 -45,0 -94.3876,0 -150,0 -55.32584,0 -85,0 -150,-0.0962 C 50,285 50,85 50,50 c 70,0 105,0 150,0 80,0 100,0 150,0 z;
            
            m 350,200 c 0,27.23032 -7.25588,52.76593 -19.93968,74.77889 C 304.15623,319.7359 255.6124,350 200,350 144.67416,350 96.344223,320.04698 70.34191,275.47268 57.409394,253.30319 50,227.51687 50,200 50,117.15729 117.15729,50 200,50 c 82.84271,0 150,67.15729 150,150 z;
            
            m 350,200 c 0,27.23032 -7.25588,52.76593 -19.93968,74.77889 C 304.15623,319.7359 255.6124,350 200,350 144.67416,350 96.344223,320.04698 70.34191,275.47268 57.409394,253.30319 50,227.51687 50,200 50,117.15729 117.15729,50 200,50 c 82.84271,0 150,67.15729 150,150z"    
             />
  </path>
</svg>    
like image 25
Alexandr_TT Avatar answered Oct 24 '22 23:10

Alexandr_TT


Well, you can play around with additional filter primitives to try to smooth things out - but it's not perfect. Here is a valiant attempt.

The additional blur + gooey colormatrix will smooth out the jagged edges, but some of the displacement is quite jaggy which results in part of the circle disappearing. So I increased the strokewidth of the original circle and then use a feMorphology/erode to thin it again - which, as you can see, is not perfect. (I also had to get rid of the original white fill because of the way that the gooey feColorMatrix works.)

class App extends React.Component {
  render() {
    return (
      <div>
       <svg width="200px" height="200px" viewBox="0 0 120 120">
    <defs>
        <filter id="distort" width="130%" height="130%">
            <feTurbulence baseFrequency=".02" type="fractalNoise" />
            <feColorMatrix type="hueRotate" values="0">
                <animate attributeName="values" from="0" to="360" dur="1s" repeatCount="indefinite" />
            </feColorMatrix>
            <feDisplacementMap in="SourceGraphic" xChannelSelector="R" yChannelSelector="B" scale="15"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="1 0 0 0 0   0 1 0 0 0   0 0 1 0 0   0 0 0 10 -1"/>
<feMorphology operator="erode" radius="1"/>
        </filter>
    </defs>
    <circle filter="url(#distort)" cx="60" cy="60" r="30" />
</svg>
      </div>
    );
  }
}

// Render it
ReactDOM.render(
  <App />,
  document.getElementById("app")
);
svg {
    stroke-width: 3;
    stroke: #293133;
    stroke-linecap: round;
    fill: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
like image 34
Michael Mullany Avatar answered Oct 25 '22 00:10

Michael Mullany