Flutter如何动画画出水平水波纹效果
老规矩,先上GIF图
参考老孟大佬制作水波纹的效果,思考如何制作一个水平的水波纹。
这里我们需要考虑3点:
- 水波纹得是水平效果
- 水波纹是由中间扩散到周围
- 水波纹是逐渐减弱的
所以我们先画一个水波纹,通过控制圆的大小来控制扩散过程,在扩散的同时增加透明度实现减弱效果。下面讲如何做成水平的效果。
import 'package:flutter/material.dart';
import 'dart:math';
class RippleAnimatedWidget extends StatefulWidget {
final Color color;
final AnimationController controller;
const RippleAnimatedWidget({super.key, this.color = Colors.white, required this.controller});
@override
State<StatefulWidget> createState() => _RippleAnimatedWidgetState();
}
class _RippleAnimatedWidgetState extends State<RippleAnimatedWidget>
with SingleTickerProviderStateMixin {
late Animation<double> animation;
@override
void initState() {
super.initState();
//增加一个缓出的效果
animation = CurvedAnimation(
parent: widget.controller,
curve: const Interval(0.5, 1, curve: Curves.easeOut));
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: widget.controller,
builder: (context, child) {
return CustomPaint(
painter: RipplePainter(widget.controller.value, color: widget.color, count: 3),
);
},
);
}
}
class RipplePainter extends CustomPainter {
final int count;
final double progress;
final Color color;
Paint painter = Paint()..style = PaintingStyle.stroke..strokeWidth = 2;
RipplePainter(this.progress, {this.color = Colors.white, this.count = 1});
@override
void paint(Canvas canvas, Size size) {
double width = min(size.width / 2, size.height / 2);
for (int i = count; i >= 0; i--) {
//改变透明度
final double opacity = (1.0 - ((i + progress) / (count + 1)));
final Color tmpColor = color.withOpacity(opacity);
painter.color = tmpColor;
//宽度递增
double radius = width * ((i + progress) / (count + 1));
canvas.drawCircle(
Offset(size.width / 2, size.height / 2), radius, painter);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
接下来就是把水波纹做成水平的
这里首先想到的是使用Transform
做偏移,果不其然,它可以让竖向的Widget转换成横向,那么我们就可以把RippleAnimatedWidget
做偏移。
Transform
的源码
/// Creates a widget that transforms its child.
///
/// The [transform] argument must not be null.
const Transform({
super.key,
required this.transform,
this.origin,
this.alignment,
this.transformHitTests = true,
this.filterQuality,
super.child,
}) : assert(transform != null);
其中起到关键作用的就是transform
,它的类型是Matrix4
,官方文档介绍
Matrix4.identity()..setEntry(1, 1, 0.2)
具体意思是
1、Matrix4.identity() 创建生成一个矩阵
2、setEntry(int row, int col, double v) 设置矩阵中的行、列、视图距离,第三个的视图距离相当于对着屏幕的远近,当越远时就看着此物体就会越全面,看着就如3D整体效果一样。
3、rotateX(angle),rotateY(angle),rotateZ(angle) 分别设置改变XYZ轴方向
这里由于我们只需要做视距调整,不需要做坐标轴的转换,所以只需要做如下实现
Transform(
transform: Matrix4.identity()..setEntry(1, 1, 0.2),
alignment: FractionalOffset.center,
child: RippleAnimatedWidget(
color: Colors.white,
controller: animationController,
),
)
加上创建一个AnimationController
,重复执行即可
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
late AnimationController animationController;
@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1300),
)..repeat();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: EdgeInsets.all(50),
color: Colors.black,
width: double.infinity,
height: double.infinity,
alignment: Alignment.center,
child: Stack(
children: <Widget>[
SizedBox(
width: 400,
height: 400,
child: Transform(
transform: Matrix4.identity()..setEntry(1, 1, 0.2),
alignment: FractionalOffset.center,
child: RippleAnimatedWidget(
color: Colors.white,
controller: animationController,
),
),
)
],
),
),
);
}
}
至此,水平水波纹已经完成了~
转载自:https://juejin.cn/post/7227476190017159225