携手创作,共同成长!这是我参与「日新计划 8 月更文应战」的第3天,点击查看活动详情

前言

本篇咱们持续介绍 Flutter 绘图的 Path 的使用。Flutter 的 Path 类供给了一个三维空间的改换办法,能够完成路径在三维空间的平移、旋转等操作,然后能够完成3D 制作的作用。经过本篇你将了解到:

  • Path的三维转化办法 transform 的运用。
  • 绕三维空间某一点的旋转完成。
  • 卡片3D 旋滚动效
  • 相似日历的三维翻页作用。

Path 的 transform 办法

Path 类的 transform 办法 将给定的Path 经过一个Float64List的目标进行三维改换,然后回来改换后的 Path目标,办法定义如下。

Path transform(Float64List matrix4) {
  assert(_matrix4IsValid(matrix4));
  final Path path = Path._();
  _transform(path, matrix4);
  return path;
}

其中 Float64List 一般都是经过 Matrix4 目标的 storage得到,例如咱们在 x 方向平移5.0,能够按如下方式得到对应的 Float64List 目标。

var transform = (Matrix4.identity()
  ..translate(5.0, 0.0, 0.0)).storage;

Matrix4供给了平移、旋转、逆矩阵等多种办法,有爱好的能够看一下 Matrix4源码,实际上便是大学线性代数课(这门课还挺难的)的矩阵乘法内容。

绕恣意点旋转

网上关于绕恣意点的旋转推导很多,这儿就不再赘述,结论便是实际上三个矩阵,先按给定点的(x,y,z)平移,再按给定的视点旋转,再按给定点的反向(-x,-y,-z)平移。比方下面是环绕 point 点,在 X 轴方向旋转 angle 视点的改换代码。

var transform = Matrix4.identity()
  ..translate(point.dx, point.dy, point.dz)
  ..rotateX(angle)
  ..translate(-point.dx, -point.dy, -point.dz);

卡片3D 旋转完成

有了上面的根底,咱们就能够完成卡片的3D旋转作用了。

Flutter 绘制3D效果动画
这个实际便是用 Path 制作了一个实心的正方形,然后绕中心点一起在 X 轴和 Y 轴旋转,旋转的视点由动画来操控。然后在动画值的中心的改变色彩,就看起来像是两面了。具体完成的代码如下。

var paint = Paint()
  ..style = PaintingStyle.fill
  ..color = Colors.blue[400]!
  ..strokeWidth = 4.0;
var center = Offset(size.width / 2, size.height / 2);
var path = Path();
final rectSize = 100.0;
path.addRect(Rect.fromCenter(
    center: Offset(center.dx, center.dy),
    width: rectSize,
    height: rectSize));
var transform = Matrix4.identity()
  ..translate(center.dx, center.dy, 0.0)
  ..rotateX(pi * animationValue)
  ..rotateY(pi * animationValue)
  ..translate(-center.dx, -center.dy, 0.0);
var transformedPath = path.transform(transform.storage);
if (animationValue < 0.5) {
  paint.color = Colors.blue[400]!;
} else {
  paint.color = Colors.red;
}
canvas.drawPath(transformedPath, paint);

咱们还能够绕 Z 轴旋转来看看作用。

Flutter 绘制3D效果动画

日历翻页作用

老的日历通常是挂在墙上,过了一天就把这一天的翻上去。

Flutter 绘制3D效果动画
观察上面的图,下面的部分是矩形,上面翻上去的会有一个曲度,这个咱们能够经过贝塞尔曲线来完成。然后,翻页过程其实便是从下面绕中心方位旋转岛上面的过程,只是在旋转过程中需求一起更改制作的路径,逐渐从矩形过渡到带有曲度的形状。

  • 下半部分制作

下半部分制作比较简单,咱们为了体现日历的厚度,能够制作多个高度错开的矩形,并且色彩有点偏差,看起来就像有厚度了。

Flutter 绘制3D效果动画
制作代码如下,这儿有两个要害点,一个是每次制作的矩形会往下偏和往右偏移必定的方位,另一个是更改制作色彩的透明度,这样就会有厚度的感觉了。

var bottomPath = Path();
for (var i = 0; i < 10; ++i) {
  bottomPath.addRect(Rect.fromCenter(
      center: Offset(
          size.width / 2 + i / 1.5, center.dy + rectSize / 2 + i * 1.5),
      width: rectSize,
      height: rectSize));
  paint.color = Colors.white70.withAlpha(240 + 10 * i);
  canvas.drawPath(bottomPath, paint);
  • 上半部分的制作

上半部分咱们的侧边制作必定的曲度,这样看着像翻往后卷起来的感觉。因为有部分卷起来了,因而高度会比下半部分低一些,曲度咱们经过贝塞尔曲线操控,制作的代码如下,这儿有两个常量,一个是 topHeight 代表上半部分的高度,一个是 flippedSize,用于操控贝塞尔曲线的曲度。

final topHeight = 90.0;
final flippedSize = -10.0;
var topPath = Path();
topPath.moveTo(center.dx - rectSize / 2, center.dy);
topPath.lineTo(center.dx + rectSize / 2, center.dy);
topPath.quadraticBezierTo(
    center.dx + rectSize / 2 + flippedSize,
    center.dy - topHeight / 2,
    center.dx + rectSize / 2,
    center.dy - topHeight);
topPath.lineTo(center.dx - rectSize / 2, center.dy - topHeight);
topPath.quadraticBezierTo(center.dx - rectSize / 2 + flippedSize,
    center.dy - topHeight / 2, center.dx - rectSize / 2, center.dy);
canvas.drawPath(topPath, paint);

制作的作用如下,看起来就有日历的感觉了。

Flutter 绘制3D效果动画

  • 翻页动效制作

翻页动效实际上便是再画一个 Path,这个目标在动画过程中逐渐从矩形转化为上半部分的图形,一起经过旋滚动效翻转上去 —— 也便是其实咱们制作的是下半部分,只是经过旋转翻上去完成翻页的动效。完成的代码如下,首要的逻辑为:

  • 下边缘的Y 轴方位在 animationValue = 0.0的时分等于下半部分的下边缘Y 轴的方位(rectSize),在 animationValue = 1.0的时分等于上半部分的上边缘Y 轴相对中心点对称方位的,即 center.dy + topHeight,因而得到高度改变的核算代码如下面第2行代码所示。这儿增加了一些小的偏移,首要是为了和上下部分有点偏移量,这样能够将翻页和其他部分区分开。
  • 左右两侧的曲度一开始是0,直到翻到中心方位后才显示,这个经过第3到第6行操控,当 animationValue < 0.5的时分,aniamtedFlippedSize 一直是0,即贝塞尔的操控点和起止点在同一条直线上,这样就不会有曲度了,比及animationValue > 0.5后,曲度跟从 animationValue 改变,终究和上半部分的曲度保持一致,这样旋转上去后能够重合。
  • 旋转选用上面咱们说的绕恣意一点旋转的方式完成,这儿咱们是绕屏幕的中心,绕 X轴旋转,视点规模是0-180度。
  • 终究是咱们更改了翻页的色彩,这个首要是能够经过色彩区分,如果是相同的色彩的话就分不太出来了。
var flippedPath = Path();
var endY = rectSize - 2 + (topHeight - 1 - rectSize) * animationValue;
var animatedFlippedSize = 0.0;
if (animationValue > 0.5) {
  animatedFlippedSize = flippedSize * animationValue;
}
var offsetX = (1 - animationValue) * 4.0;
flippedPath.moveTo(center.dx - rectSize / 2, center.dy);
flippedPath.lineTo(center.dx + rectSize / 2, center.dy);
flippedPath.quadraticBezierTo(
    center.dx + rectSize / 2 + animatedFlippedSize - offsetX,
    center.dy + endY / 2,
    center.dx + rectSize / 2 - offsetX,
    center.dy + endY);
flippedPath.lineTo(center.dx - rectSize / 2 - offsetX, center.dy + endY);
flippedPath.quadraticBezierTo(
    center.dx - rectSize / 2 + animatedFlippedSize,
    center.dy + endY / 2,
    center.dx - rectSize / 2,
    center.dy);
var transform = Matrix4.identity()
  ..translate(center.dx, center.dy, 0.0)
  ..rotateX(pi * animationValue)
  ..translate(-center.dx, -center.dy, 0.0);
var transformedPath = flippedPath.transform(transform.storage);
if (animationValue < 0.5) {
  paint.color = Colors.white;
} else {
  paint.color = Colors.green[300]!;
}
canvas.drawPath(transformedPath, paint);

终究的完成作用如下所示。

Flutter 绘制3D效果动画

总结

本篇介绍了Flutter 绘图中的 Path类的三维空间改换办法和使用,能够看到,根据三维改换能够完成3D作用图形的制作和完成3D 动效,这在有些特殊制作的场景中或增加趣味性十分有用。


本篇源码已上传至:绘图相关代码,文件名为:path_matrix_demo.dart

我是岛上码农,微信大众号同名,这是Flutter 入门与实战的专栏文章,供给体系化的 Flutter 学习文章。对应源码请看这儿:Flutter 入门与实战专栏源码。如有问题能够加本人微信交流,微信号:island-coder

:觉得有收成请点个赞鼓励一下!

:保藏文章,便利回看哦!

:谈论交流,相互进步!