Flutter 的自定义 UI 系列(六)-示例:写轮眼动效

1,253 阅读2分钟


自定义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();
}

Sharingan1.png

步骤二 三勾玉的绘制

计算并找到点的位置,用圆和一个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);
}

Sharingan2.png

另外两个勾玉可以旋转画布,调用同一个绘制方法。省去了计算点位置的步骤。

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();

Sharingan3.png

步骤三 万花筒绘制

万花筒的边缘是个贝塞尔曲线,找到对应的点绘制曲线就可以了

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);

Sharingan4.png


绘制另外两条曲线时,用到了三角函数,这也是 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));

Sharingan5.png

步骤四 加动效

创建 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 再根据动画的值旋转画布对应的角度即可。最后就实现了动画的效果。

Sharingan0.png