目录
前言
继续动画探索,今天用Flutter制作一个心碎的感觉,灵感来源于今天的股市,哎,心哇凉哇凉的。废话不多说,开始。
效果图先上:
实现步骤
1、绘制一个心
首先我们使用两段三阶贝塞尔曲线制作一个心型,这里因为需要实现心碎的效果,所以我们需要将心的两段用两段路径path
进行绘制出来,效果:
绘制代码:
canvas.translate(size.width / 2, size.height / 2); Paint paint = Paint(); paint ..style = PaintingStyle.stroke ..strokeWidth = 2 ..color = Colors.black87; Path path = Path(); path.moveTo(0, 0); path.cubicTo(-200, -80, -60, -240, 0, -140); path.close(); Path path2 = Path(); canvas.save(); canvas.drawPath( path, paint ..color = Colors.red ..style = PaintingStyle.stroke); canvas.restore(); path2.cubicTo(200, -80, 60, -240, 0, -140); path2.close(); canvas.drawPath( path2, paint..color = Colors.black87);
2、绘制心的裂痕
我们看到心确实分成两半了,但是中间还缺少裂痕,接下来我们就绘制心碎的裂痕,也很简单,在两段路径path
闭合前进行绘制线,效果:
绘制代码:
path.relativeLineTo(-10, 30); path.relativeLineTo(20, 5); path.relativeLineTo(-20, 30); path.relativeLineTo(20, 20); path.relativeLineTo(-10, 20); path.relativeLineTo(10, 10); path2.relativeLineTo(-10, 30); path2.relativeLineTo(20, 5); path2.relativeLineTo(-20, 30); path2.relativeLineTo(20, 20); path2.relativeLineTo(-10, 20); path2.relativeLineTo(10, 10);
OK,我们已经看到心已经有了裂痕,如何心碎,只需将画布进行翻转一定角度即可,这里我们将画布翻转45°,看下效果:
左边:
右边:
3、加入动画
已经有心碎的感觉了,接下来加入动画元素让心碎的过程动起来。
思路: 我们可以想一下,心碎的过程是什么样子,心的颜色慢慢变灰,心然后慢慢裂开,下方的动画运动曲线看起来更符合心碎的过程,里面有不舍,不甘,但最后心还是慢慢的碎了。
我们把画笔进行填充将这个动画加入进来看下最终效果。
是不是心碎了一地。
知识点: 这里我们需要找到红色和灰色的RGB色值,通过Color.fromRGBO(r, g, b, opacity)
方法赋值颜色的色值。然后通过动画值改变RGB的值即可。 这里我使用的色值是:
红色:Color.fromRGBO(255, 0, 0, 1)
灰色:Color.fromRGBO(169, 169, 169, 1)
完整代码
class XinSui extends StatefulWidget { const XinSui({Key? key}) : super(key: key); @override _XinSuiState createState() => _XinSuiState(); } class _XinSuiState extends State<XinSui> with SingleTickerProviderStateMixin { late AnimationController _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 4000)) ..repeat(); late CurvedAnimation cure = CurvedAnimation(parent: _controller, curve: Curves.bounceInOut); late Animation<double> animation = Tween<double>(begin: 0.0, end: 1.0).animate(cure); @override Widget build(BuildContext context) { return Container( child: CustomPaint( size: Size(double.infinity, double.infinity), painter: _XinSuiPainter(animation), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } } class _XinSuiPainter extends CustomPainter { Animation<double> animation; _XinSuiPainter(this.animation) : super(repaint: animation); @override void paint(Canvas canvas, Size size) { canvas.translate(size.width / 2, size.height / 2); Paint paint = Paint(); paint ..style = PaintingStyle.stroke ..strokeWidth = 2 ..color = Colors.black87; Path path = Path(); path.moveTo(0, 0); path.cubicTo(-200, -80, -60, -240, 0, -140); path.relativeLineTo(-10, 30); path.relativeLineTo(20, 5); path.relativeLineTo(-20, 30); path.relativeLineTo(20, 20); path.relativeLineTo(-10, 20); path.relativeLineTo(10, 10); path.close(); Path path2 = Path(); canvas.save(); canvas.rotate(-pi / 4 * animation.value); canvas.drawPath( path, paint ..color = Colors.red ..color = Color.fromRGBO( 255 - (86 * animation.value).toInt(), (animation.value * 169).toInt(), (animation.value * 169).toInt(), 1) ..style = PaintingStyle.fill); canvas.restore(); path2.cubicTo(200, -80, 60, -240, 0, -140); path2.relativeLineTo(-10, 30); path2.relativeLineTo(20, 5); path2.relativeLineTo(-20, 30); path2.relativeLineTo(20, 20); path2.relativeLineTo(-10, 20); path2.relativeLineTo(10, 10); path2.close(); canvas.rotate(pi / 4 * animation.value); canvas.drawPath( path2,paint); } @override bool shouldRepaint(covariant _XinSuiPainter oldDelegate) { return oldDelegate.animation != animation; } }
小结
动画曲线Curves配合绘制可以实现很多好玩的东西,这个需要勤加练习方能掌握,仅将此心碎献给今天受伤的股民朋友们