前言
最近用flutter custompaint做了个曲线随声音波动的动效,和大家来分享下实现的过程和思路
曲线绘制的实现
无论原生,Flutter还是前端,曲线的绘制在不同平台实现都大同小异。通过确定不同点的位置坐标(x1, y1)(x2,y2)... ,然后用过贝塞尔曲线或者系统方法拟合两点之间的曲线,最后把曲线path给到画布绘制,这样曲线就出来了
小插曲
-
最终效果是 开始和末尾振幅为0 向中间位置递增的曲线
-
我们从基础开始,我们先画条振幅相同的曲线,并波动。
-
写代码要循循渐进,一步步的来。。。 这简单啊
-
画条两倍容器view宽的波纹曲线
-
重复从左到右滑动,视觉效果就是曲线波动的效果
搞定后,设计说上面这个是当时偷懒给错了图,当时内心的想法
平复下情绪,那我们就把 振幅相同 和 振幅不同 的曲线都分享下
绘制振幅相同的曲线
class VoiceWavePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 2;
Path path = Path();
//
path.quadraticBezierTo(50, -80, 100, 0);
path.quadraticBezierTo(150, 80, 200, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(VoiceWavePainter oldDelegate) => false;
}
path.quadraticBezierTo
定义P1 和 P2 位置 一个类似正弦的曲线绘制完成,P0 是路径起始点 path.quadraticBezierTo(50, -80, 100, 0); P0(0,0) P1 (50,-80) p2 (100,0) path.quadraticBezierTo(150, 80, 200, 0); P0(100,0) P1 (150,80) p2 (200,0) 以此类推,就出现需要的类正弦曲线
振幅相同的曲线的波动
原理已经说过不再重复,图示更直观, 重复的左滑动,矩形框左右两侧波形 左滑的开始和结束时完全一样的,视觉效果就产生曲线一直波动的效果
最终实现
@override
void paint(Canvas canvas, Size size) {
int waveNum = 4;
double waveWidth = this.painterWidth / waveNum;
canvas.clipRect((Rect.fromCenter(
center: Offset(0, size.height / 2),
width: painterWidth * 2,
height: 120)));
canvas.translate(0, size.height / 2);
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 2;
Path path = Path();
for (var i = 0; i < waveNum * 2; i++) {
path.quadraticBezierTo(waveWidth / 2 + waveWidth * i,
waveHeight * (i.isOdd ? 1 : -1), waveWidth * (i + 1), 0);
}
canvas.translate(-painterWidth * repaint.value, 0);
canvas.drawPath(path, paint);
}
}
class _VoiceWaveState extends State<VoiceWave>
with SingleTickerProviderStateMixin {
AnimationController _waveController;
@override
void initState() {
_waveController =
AnimationController(vsync: this, duration: Duration(seconds: 1))..repeat();
}
@override
void dispose() {
_waveController.dispose();
super.dispose();
}
- 定义
AnimationController
并传入 自定义的CustomPainter
painterWidth
曲线的长度waveNum
可见波动的个数waveWidth = this.painterWidth / waveNum
,waveWidth
是半个波长的长度
for (var i = 0; i < waveNum * 2; i++) {
path.quadraticBezierTo(waveWidth / 2 + waveWidth * i,
waveHeight * (i.isOdd ? 1 : -1), waveWidth * (i + 1), 0);
}
- 循环定义点位的位置,假如要看到4个上下波峰长的曲线,我们需要绘制8的上下波峰的曲线,共16 个坐标点,简化后通过循环确定点的位置
- 通过
canvas.translate(-painterWidth * repaint.value, 0)
实现canvas的重复移动
总结
- 优点
- 能够快速实现曲线波动的效果
- 缺点
- 波动个数必须是半个的正弦周期的偶数倍,奇数的话 重复动画的开始和奇数的点y值正好相反,无所出现连贯的动效
- 无法使用渐变色效果
未经作者授权,禁止转载