多种方式实现波浪动画
基础动画实现
当我知道有些动画效果是纯靠图片拼起来的时候我是绝望的,虽然的确很省计算资源,但是你们让高分屏往哪哭?
大部分动画效果,直接用相应图形库自带的函数就能解决
有些稍微麻烦一点的,就得手写了(当然还是不推荐,毕竟人家的函数一般都带各种加速)
这时候就体现出了一个 客户端/网页端 的前端稍微懂点数学是多么的重要
不过太高级的数学我也不会,只能讲点简单的,就是实现一个波浪动画
首先想到的,是用各种绘图库都有提供的贝塞尔曲线直接画(关于贝塞尔曲线的介绍 可以看这个,讲的很清晰)
下面这个就是用一条三次贝塞尔曲线画出来的(源码请直接查看网页源码)
基本步骤是,先在中间画一条三次贝塞尔曲线,再画三条线把下面包起来填充做成 "水"
最后找规律写个控制算法控制曲线的两个控制点循环就行
不过控制算法写的不好,导致看起来并不是很舒服,也不想继续优化了,所以这个版本弃坑
第二个版本,直接用三角函数画,简单暴力
下面开始复习初中数学
我们都知道,在二维坐标系里,有 x
y
就有了一个点,俩点连起来就有了一条线
而要画一条横着的直线,只需要知道三个值,x1
x2
和 y
,因为两个点 y
是一样的
画起来是这样的(伪代码):
x1 = 0
x2 = 10
y = 0
ctx = canvas.get_context() // 基本各种绘图库都有这个方法,获取一个 "画笔"
ctx.begin_path() // 开始标记一个路径
ctx.move_to(x0, y) // 将画笔移到起点
for (x=x1+1; x<x2+1; x++) {
ctx.line_to(x, y) // 将前后两点连起来
}
ctx.stroke() // 将路径画出来
当然可以直接一个 line_to
到 x2
,不过这里是为了演示逐点画线
至于曲线,聪明的你肯定知道了,画点的过程中想个办法让 y
根据 x
上下跑就可以了
for (x=x1+1; x<x2+1; x++) {
y = magic(x)
ctx.line_to(x, y)
}
把里面的 magic 换成三角函数,比如正弦函数(sin),你就可以画出来这样的曲线:
(图片来自维基百科,公有领域)
但这还不够,我们需要让这条曲线看起来更 "柔和" 一些,所以让我们把 x
变小一点,比如:
y = sin(0.3 * x)
红线是 * 0.3
的结果,看起来好多了
目前为止浪高都是一样的,需要让他再大一些,修改一下 y
的波动幅度,比如两倍(黄线)
y = 2 * sin(0.3 * x)
同时需要让曲线能上下移动(控制水量),直接改变 y
的高度,比如向上 1 点(绿线)
y = 2 * sin(0.3 * x) + 1
最后让波浪向左平移,动起来(offset 在每次动画循环中自增)
y = 2 * sin(0.3 * x + offset) + 1
演示就不做了,好麻烦
总之现在就有了一个波浪动画,改变波浪形状只需要把上面挨个介绍过的量修改就行
上面的双波浪是分别用的正弦和余弦函数做成的,也可以直接给 offset 加个差值
第三个版本,因为在 svg 中靠预先绘制点连线做曲线是相当丑陋的,根本经不起放大,所以这个版本用二次贝塞尔曲线绘制
这次直接讲源码
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="wave"> <!--单条波浪,具体怎么画的请看之前的介绍贴-->
<path d="M0 0 Q 50 10, 100 0 T 200 0 V 50 H 0 V 0" />
</g>
<clipPath id="boder"> <!--圆形遮罩,用来做边框-->
<circle cx="50" cy="50" r="50" />
</clipPath>
</defs>
<g clip-path="url(#boder)">
<rect x="0" y="0" width="100" height="100" fill="white" /> <!--白色背景-->
<g>
<g fill="rgba(10, 132, 255, 0.7)"> <!--多条波浪拼成一条完整波浪-->
<!--需要的最少波浪数量为:
向上取整(图宽/波浪长度)+1-->
<use x="0" y="50" xlink:href="#wave" />
<use x="200" y="50" xlink:href="#wave" />
</g>
<!--第二条波浪,用 translate 加偏移,推荐偏移波浪长的 1/4-->
<g transform="translate(-50)" fill="rgba(1, 112, 223, 0.7)">
<!--因为加了偏移,所以还要判断
向上取整(图宽/波浪长度)*波浪长度-偏移>=图宽
否则再+1-->
<use x="0" y="50" xlink:href="#wave" />
<use x="200" y="50" xlink:href="#wave" />
</g>
<!--动画效果,to 等于单条波浪长度,dur 为波浪移动速度-->
<animateTransform attributeName="transform"
attributeType="XML"
type="translate"
from="0"
to="-200"
dur="1s"
repeatCount="indefinite"/>
</g>
</g>
</svg>
拼接波浪的数量也不用真的去算,看见缺了的话再加一条就行了
把动画放慢再展开的效果(蓝色是第一条波浪,红色第二条,可以看见波浪的拼接线):