自定义UI系列专栏
前言
CustomPainter 除了常规的绘制外,还可以配合 AnimationController 实现动态效果。这篇文章将展示一个写轮眼的绘制动画。具体效果看封面,没有太多的技术难点,就是计算点位置和调用对应的 API 做绘制。
步骤一 背景的绘制
绘制背景,比较简单,绘制两个圆就可以了。
技巧:绘制圆形相关的图像时,可以把画布的原点挪到 widget 的中心,这样在计算三角函数、旋转画布等操作更方便。
@override
void paint(Canvas canvas, Size size) {
var center = size.center(Offset.zero);
// 挪到画布到 CustomPaint 的中心位置
canvas.translate(center.dx, center.dy);
canvas.save();
canvas.restore();
}
步骤二 三勾玉的绘制
计算并找到点的位置,用圆和一个path路径共同绘制了一个勾玉。
void _drawMagatama(Offset center, Canvas canvas) {
var magatamaOrigin = center.translate(90, 0);
canvas.drawCircle(magatamaOrigin, 20, blackPaintFill);
path.moveTo(magatamaOrigin.dx, magatamaOrigin.dy);
path.relativeLineTo(0, -20);
var magatamaEnd = magatamaOrigin.translate(40, 20);
path.conicTo(magatamaOrigin.dx + 20, magatamaOrigin.dy - 20, magatamaEnd.dx, magatamaEnd.dy, 1);
path.moveTo(magatamaOrigin.dx, magatamaOrigin.dy);
path.conicTo(magatamaOrigin.dx + 20, magatamaOrigin.dy - 10, magatamaEnd.dx, magatamaEnd.dy, 1);
path.close();
canvas.drawPath(path, blackPaintFill);
}
另外两个勾玉可以旋转画布,调用同一个绘制方法。省去了计算点位置的步骤。
canvas.save();
canvas.rotate(pi * 2 / 3);
canvas.rotate(_animationDirection);
_drawMagatama(Offset.zero, canvas);
canvas.restore();
canvas.save();
canvas.rotate(pi * 4 / 3);
canvas.rotate(_animationDirection);
_drawMagatama(Offset.zero, canvas);
canvas.restore();
步骤三 万花筒绘制
万花筒的边缘是个贝塞尔曲线,找到对应的点绘制曲线就可以了
Offset ctrPoint1 = Offset(-30, -100);
Offset ctrPoint2 = Offset(90, 150);
Path path = Path();
var offsetA = Offset(145, 0);
var offsetB = Offset(radius * cos(pi * 2 / 3), radius * sin(pi * 2 / 3));
path.moveTo(offsetA.dx, offsetA.dy);
path.cubicTo(ctrPoint1.dx, ctrPoint1.dy, ctrPoint2.dx, ctrPoint2.dy, offsetB.dx, offsetB.dy);
canvas.drawPath(path, blackPaint..strokeWidth=1);
绘制另外两条曲线时,用到了三角函数,这也是 canvas 绘制中最常用的函数,用来计算指定角度点的位置的方法。
var offsetB = Offset(radius * cos(pi * 2 / 3), radius * sin(pi * 2 / 3));
var offsetC = Offset(radius * cos(pi * 4 / 3), radius * sin(pi * 4 / 3));
步骤四 加动效
创建 AnimationController ,并添加 CurvedAnimation 差值器,使动画效果更加逼真。关于差值器效果可以参考官方的示例 Curves
_controllerMagatama = AnimationController(vsync: this, duration: Duration(seconds: 6));
_curvedAnimationMagatama = CurvedAnimation(parent: _controllerMagatama, curve: Curves.elasticIn);
_curvedAnimationMagatama.addListener(() {
_valueMagatama = _curvedAnimationMagatama.value * 3;
setState(() {});
});
_controllerMagatama.forward();
把 CurvedAnimation 的值传给 CustomPainter 再根据动画的值旋转画布对应的角度即可。最后就实现了动画的效果。