SVG圆形变形

IT技术 reactjs animation svg svg-animate
2021-05-05 13:52:56

如何使用形状变形制作圆形 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>

3个回答

我认为出现毛刺效应是因为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>

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

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

逐步实现变形:

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

在此处输入图片说明

  • 移动节点。我们保存文件。这将是变形的最终路径。

在此处输入图片说明

  • 我们写了一个动画命令来改变d属性。此属性的值更改为

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

注:开始完成的路径用分号隔开。

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

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

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

  • 所有形状的锚点数量必须相同
  • 一开始位置相同的锚点类型,在所有形状中必须相同

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

额外的模糊 + 粘性颜色矩阵将平滑锯齿状边缘,但一些位移非常锯齿状,导致部分圆消失。因此,我增加了原始圆的笔触宽度,然后使用 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>