【问题标题】:SVG circle shape morphingSVG 圆形变形
【发布时间】:2021-05-08 20:00:30
【问题描述】:

如何制作带有形状变形的圆形 SVG?

我正在尝试用有机运动做圆圈。

但正如您所见,像素似乎有问题。

关于如何使它更好看的任何想法?这甚至是我用来做这件事的正确方式吗?我不是 svg 方面的专家,也不是形状变形方面的专家。

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>

【问题讨论】:

    标签: reactjs animation svg svg-animate


    【解决方案1】:

    我认为毛刺效应正在发生,因为feTurbulence 过滤器在 R、G 和 B 通道中输出 8 位整数值。这种量化使得置换映射很难实现平滑变化。

    无论如何,使用动画矢量曲线可以获得更好的结果。这是一个使用 50 个顶点为多边形设置动画的示例。您可以通过使用二次或三次贝塞尔曲线而不是直线段来减少点数,但这至少可以帮助您入门:

    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>

    【讨论】:

    • 感谢您的回答,这对我来说是另一个级别的 javascript ! :) 由于我使用它作为光标,我不能使用你的功能,这太复杂而无法重新制作并且有很多过程。但你的回答是我想要的最好的。如果有人对此主题感兴趣,我提出了另一个问题。 stackoverflow.com/questions/67471636/…
    • 你认为可以启动和停止对点击或类似的效果吗?还是因为它的逻辑是无限循环我不会工作?
    【解决方案2】:

    关于如何使它更好看的任何想法?

    可以通过修改patch的d属性来实现圆形边框的变形

    变形的逐步实现:

    • 将圆加载到矢量编辑器中
    • 添加额外的锚点。以 SVG 格式保存文件。这将 成为变形的起始路径

    • 移动节点。我们保存文件。这将是最后的道路 用于变形。

    • 我们编写动画命令来更改d 属性。价值 此属性更改为

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

    注意:StartFinish路径用分号隔开。

    下面是完整的代码。点击后会开始动画

    <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>

    另一个从圆形变形为三角形,然后变形为矩形并返回圆形的示例

    所有形状的路径都在矢量编辑器中绘制。 为了使动画流畅、不抖动,创建路径时必须满足两个条件:

    • 所有形状的锚点数量必须相同
    • 从头开始位置相同的锚点类型, 所有形状都必须相同

    <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>    

    【讨论】:

    • 这是一个很好的解决方案,但定义形状的不是随机值。或者我可能不得不添加很多节点并且只移动那些我想随机移动的节点
    【解决方案3】:

    好吧,您可以尝试使用其他过滤器基元来尝试使事情变得平滑 - 但它并不完美。这是一次勇敢的尝试。

    额外的模糊 + 粘糊糊的颜色矩阵将平滑锯齿状边缘,但一些位移非常锯齿状,导致部分圆圈消失。所以我增加了原始圆圈的笔画宽度,然后使用 feMorphology/erode 再次使其变薄 - 正如您所看到的,这并不完美。 (由于粘糊糊的 feColorMatrix 的工作方式,我还不得不去掉原来的白色填充。)

    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>

    【讨论】:

      猜你喜欢
      • 2014-11-28
      • 1970-01-01
      • 2015-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多