前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >深入探索 Flutter 鸿蒙版的画笔使用与高级自定义动画

深入探索 Flutter 鸿蒙版的画笔使用与高级自定义动画

作者头像
淼学派对
修改于 2024-11-06 12:34:04
修改于 2024-11-06 12:34:04
510
举报

写在前面

Flutter 中,绘图是一项强大的功能,可以帮助开发者创建自定义界面和独特的视觉效果。通过 CustomPainter 和 Canvas,我们可以实现复杂的图形和动画。本文将深入探讨 Flutter 中的画笔使用,包括如何编写高级自定义动画。

一、什么是 CustomPainter?

CustomPainter 是 Flutter 提供的一种用于绘制自定义图形的类。通过继承 CustomPainter,你可以重写 paint 和 shouldRepaint 方法,从而在 Canvas 上绘制任意形状、路径、文本等。

CustomPainter 的基本使用

代码语言:txt
AI代码解释
复制
import 'package:flutter/material.dart';
 
class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;
 
    canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, paint);
  }
 
  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}
 
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('CustomPainter 示例')),
      body: CustomPaint(
        size: Size(200, 200),
        painter: MyPainter(),
      ),
    );
  }
}

在这个示例中,我们创建了一个自定义画笔 MyPainter,在 Canvas 上绘制了一个蓝色的圆。

二、Paint 对象的属性

Paint 对象是绘制图形的核心。它有多个属性,可以控制绘制的样式和效果:

color:绘制颜色。

style:绘制样式,包括填充(PaintingStyle.fill)和描边(PaintingStyle.stroke)。

strokeWidth:描边的宽度。

strokeCap:描边的结束样式,如圆形(StrokeCap.round)或方形(StrokeCap.square)。

shader:用于渐变效果的着色器。

示例:使用 Paint 对象、

代码语言:txt
AI代码解释
复制
final paint = Paint()
  ..color = Colors.red
  ..style = PaintingStyle.stroke
  ..strokeWidth = 5;
 
canvas.drawRect(Rect.fromLTWH(50, 50, 100, 100), paint);

在这个示例中,我们使用 Paint 对象绘制了一个红色的矩形,宽度为 5 的描边。

三、实现高级自定义动画

1. 动画基本概念

在 Flutter 中,动画主要通过 Animation 和 AnimationController 实现。AnimationController 控制动画的进度,而 Animation 描述动画的值变化。

2. 创建一个简单的动画

下面是一个使用 CustomPainter 和 AnimationController 创建简单动画的示例:

代码语言:txt
AI代码解释
复制
class AnimatedPainter extends StatefulWidget {
  @override
  _AnimatedPainterState createState() => _AnimatedPainterState();
}
 
class _AnimatedPainterState extends State<AnimatedPainter>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
 
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    )..repeat(reverse: true);
    
    _animation = Tween<double>(begin: 50, end: 100).animate(_controller);
  }
 
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('动画绘制示例')),
      body: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          return CustomPaint(
            size: Size(200, 200),
            painter: MyAnimatedPainter(radius: _animation.value),
          );
        },
      ),
    );
  }
}
 
class MyAnimatedPainter extends CustomPainter {
  final double radius;
 
  MyAnimatedPainter({required this.radius});
 
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;
 
    canvas.drawCircle(Offset(size.width / 2, size.height / 2), radius, paint);
  }
 
  @override
  bool shouldRepaint(MyAnimatedPainter oldDelegate) => radius != oldDelegate.radius;
}

解析代码

AnimationController:创建一个持续时间为 2 秒的动画控制器,并设置为循环。

Tween:定义动画的起始值和结束值(圆的半径)。

AnimatedBuilder:在动画变化时重建 CustomPaint,以更新绘制的圆的半径。

四、创建更复杂的自定义动画

1. 渐变动画

在动画中引入渐变效果,可以使用 Shader 属性来实现:

代码语言:txt
AI代码解释
复制
class GradientAnimatedPainter extends CustomPainter {
  final double radius;
 
  GradientAnimatedPainter({required this.radius});
 
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..shader = RadialGradient(
        colors: [Colors.red, Colors.blue],
        stops: [0.5, 1],
      ).createShader(Rect.fromCircle(center: Offset(size.width / 2, size.height / 2), radius: radius));
 
    canvas.drawCircle(Offset(size.width / 2, size.height / 2), radius, paint);
  }
 
  @override
  bool shouldRepaint(GradientAnimatedPainter oldDelegate) => radius != oldDelegate.radius;
}

2. 结合路径动画

结合路径和自定义动画,可以创建更加复杂的效果。例如,沿路径绘制图形:

代码语言:txt
AI代码解释
复制
class PathAnimationPainter extends CustomPainter {
  final double progress;
 
  PathAnimationPainter({required this.progress});
 
  @override
  void paint(Canvas canvas, Size size) {
    final path = Path()
      ..moveTo(50, 100)
      ..lineTo(150, 100)
      ..lineTo(100, 50)
      ..close();
 
    final paint = Paint()
      ..color = Colors.green
      ..style = PaintingStyle.fill;
 
    canvas.drawPath(Path.combine(PathOperation.intersect, path, path), paint);
    canvas.drawCircle(Offset(100 * progress, 50), 5, Paint()..color = Colors.red);
  }
 
  @override
  bool shouldRepaint(PathAnimationPainter oldDelegate) => progress != oldDelegate.progress;
}

3. 综合示例:完整代码

将上述所有元素组合成一个完整的例子,创建一个包含路径和渐变动画的画布:

代码语言:txt
AI代码解释
复制
class ComplexAnimationExample extends StatefulWidget {
  @override
  _ComplexAnimationExampleState createState() => _ComplexAnimationExampleState();
}
 
class _ComplexAnimationExampleState extends State<ComplexAnimationExample>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
 
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 4),
      vsync: this,
    )..repeat(reverse: true);
    
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller);
  }
 
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('复杂动画示例')),
      body: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          return CustomPaint(
            size: Size(200, 200),
            painter: PathAnimationPainter(progress: _animation.value),
          );
        },
      ),
    );
  }
}

写在最后

在 Flutter 中,CustomPainter 和 Canvas 提供了强大的绘图能力,适合实现各种自定义图形和动画。通过结合 Animation 和 AnimationController,你可以创建平滑且复杂的动画效果。本文介绍了基本的画笔使用、动画控制,以及如何将它们结合实现高级自定义动画的技巧。

希望本篇博客能帮助你更好地理解 Flutter 中的画笔使用与动画创建,开启你的创作之旅!如果你对 Flutter 动画有任何问题或想法,欢迎在评论区讨论!

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Flutter 实战】自定义动画-涟漪和雷达扫描
此动画通过 CustomPainter 绘制配合 AnimationController 动画控制实现,定义动画控制部分:
老孟Flutter
2020/09/11
1.5K0
【Flutter 实战】自定义动画-涟漪和雷达扫描
Flutter 吃豆人加载动画效果
吃豆人加载动画效果是Loading动画系列中的一个,github地址:https://github.com/LaoMengFlutter/flutter-do
老孟Flutter
2021/09/03
9220
Flutter 吃豆人加载动画效果
Flutter 涟漪加载动画效果
涟漪加载动画效果是Loading动画系列中的一个,github地址:https://github.com/LaoMengFlutter/flutter-do
老孟Flutter
2021/09/27
2.6K0
【Flutter 专题】133 图解自定义 ACEWaterButton 水波纹按钮
和尚想自定义一个水波纹按钮,即默认向外扩散的水波样式;实现方式有很多种,和尚尝试最基本的 AnimationController 逐层绘制来处理,和尚简单记录一下尝试过程;
阿策小和尚
2021/08/25
8690
Flutter 漏斗加载动画效果
下面我们看看漏斗加载动画效果是如何实现的?动画效果实现的思路是绘制一个静止的效果,其中可变的效果使用参数控制,回到我们的漏斗加载动画,先绘制一个中间状态,效果如下:
老孟Flutter
2021/09/03
1.9K0
Flutter 漏斗加载动画效果
【Flutter绘制集录】第二画: 流光
本文来通过一个小案例,介绍一下 Flutter 绘制 和 Flutter 动画 的使用。如下,是一个七彩的圆环,其中有两个动画效果:
张风捷特烈
2022/03/08
1.3K0
【Flutter绘制集录】第二画: 流光
Flutter 绘制探索 1 | CustomPainter 正确刷新姿势 | 七日打卡
可能说起 Flutter 绘制,大家第一反应就是用 CustomPaint 组件,自定义 CustomPainter 对象来画。Flutter 中所有可以看得到的组件,比如 Text、Image、Switch、Slider 等等,追其根源都是画出来的,但通过查看源码可以发现,Flutter 中绝大多数组件并不是使用 CustomPaint 组件来画的,其实 CustomPaint 组件是对框架底层绘制的一层封装。这个系列便是对 Flutter 绘制的探索,通过测试、调试及源码分析来给出一些在绘制时被忽略或从未知晓的东西,而有些要点如果被忽略,就很可能出现问题。
张风捷特烈
2023/09/01
1.5K0
Flutter 绘制探索 1 | CustomPainter 正确刷新姿势 | 七日打卡
【flutter高级玩法】贝塞尔实战1 - 波浪
一切视觉的动效都只是感性的欺骗,如我手中的线,跳动的人偶。她征服着你,我控制着她。--捷特
张风捷特烈
2020/04/30
1.2K0
【flutter高级玩法】贝塞尔实战1 - 波浪
Flutter 动画之 Animation
1.前言 1.1:Flutter动画中: 首先要看的是Flutter中动画的几个类之间的关系: 主角当然是我们的Animation类了,它可以借助Animatable进行强化 Animata
张风捷特烈
2020/04/30
2.1K0
Flutter 动画之 Animation
Flutter 动画鼻祖之CustomPaint
老孟导读:CustomPaint可以称之为动画鼻祖,它可以实现任何酷炫的动画和效果。CustomPaint本身没有动画属性,仅仅是绘制属性,一般情况下,CustomPaint会和动画控制配合使用,达到理想的效果。
老孟Flutter
2020/09/11
5910
Flutter 绘制探索 1 | CustomPainter 正确刷新姿势 | 七日打卡
@charset "UTF-8";.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1:first-child,.markdown-body h2:first-child,.markdown-body h3:first-child,.markdown-body h4:first-child,.markdown-body h5:first-child,.markdown-body h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.markdown-body h1:before,.markdown-body h2:before,.markdown-body h3:before,.markdown-body h4:before,.markdown-body h5:before,.markdown-body h6:before{content:"#";display:inline-block;color:#3eaf7c;padding-right:.23em}.markdown-body h1{position:relative;font-size:2.5rem;margin-bottom:5px}.markdown-body h1:before{font-size:2.5rem}.markdown-body h2{padding-bottom:.5rem;font-size:2.2rem;border-bottom:1px solid #ececec}.markdown-body h3{font-size:1.5rem;padding-bottom:0}.markdown-body h4{font-size:1.25rem}.markdown-body h5{font-size:1rem}.markdown-body h6{margin-top:5px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body strong{color:#3eaf7c}.markdown-body img{max-width:100%;border-radius:2px;display:block;margin:auto;border:3px solid rgba(62,175,124,.2)}.markdown-body hr{border:none;border-top:1px solid #3eaf7c;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;overflow-x:auto;padding:.2rem .5rem;margin:0;color:#3eaf7c;font-weight:700;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75;border-radius:6px;border:2px solid #3eaf7c}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{font-weight:500;text-decoration:none;color:#3eaf7c}.markdown-body a:active,.ma
张风捷特烈
2021/01/14
1.5K0
Flutter 绘制探索 1 | CustomPainter 正确刷新姿势 | 七日打卡
Flutter 绘制探索 6 | 深入分析 CustomPaint 组件 | 七日打卡
可能说起 Flutter 绘制,大家第一反应就是用 CustomPaint 组件,自定义 CustomPainter 对象来画。Flutter 中所有可以看得到的组件,比如 Text、Image、Switch、Slider 等等,追其根源都是画出来的,但通过查看源码可以发现,Flutter 中绝大多数组件并不是使用 CustomPaint 组件来画的,其实 CustomPaint 组件是对框架底层绘制的一层封装。这个系列便是对 Flutter 绘制的探索,通过测试、调试及源码分析来给出一些在绘制时被忽略或从未知晓的东西,而有些要点如果被忽略,就很可能出现问题。
张风捷特烈
2021/01/20
1.6K0
Flutter 绘制探索 6 | 深入分析 CustomPaint 组件 | 七日打卡
如何快速提升 Flutter App 中的动画性能
观前提醒:本文假设你已经有一定的 Flutter 开发经验,对Flutter 的 Widget,RenderObject 等概念有所了解,并且知道如何开启 DevTools。
逆锋起笔
2020/12/14
1.5K0
[-Flutter 自组篇-] 蛛网图+绘制+动画实践
在Android的时候自定义过蛛网图,花了半天时间。复刻到Flutter只用了不到20分钟 不得不说Flutter中的Canvas对安卓玩家还是非常友好的,越来越觉得Flutter非常有趣。 在视
张风捷特烈
2020/04/30
1.4K0
[-Flutter 自组篇-] 蛛网图+绘制+动画实践
Flutter:如何使用 CustomPaint 绘制心形
您可以在官方文档中找到有关 CustomPaint 小部件和 CustomPainter 类的更多详细信息:
徐建国
2021/10/18
1K0
flutter路径的用法(下)
本节目标: [1]. 了解路径的 [封闭] [重置] [偏移] 操作。 [2]. 了解路径的 [矩形边距] 和 [检测点是否在路径中]。 [3]. 了解路径的 [路径变换] 和 [路径联合]。 [4]. 了解路径测量的用法和作用。 ---- 一、路径操作 路径的操作是路径使用的重要一环,很多路径的特效和复杂路径的拼合都会使用它们。 ---- 1.close、reset、shift path#close :用于将路径尾点和起点,进行路径封闭。 path#reset :用于将路径进行重置,清除路径内容。
用户1974410
2022/09/20
9810
flutter路径的用法(下)
【Flutter 专题】38 Animation 基本动画 (二)
和尚前两天学习了以下 Animation 的基本动画,接下来和尚学习以下稍微进阶版的 Animation 动画。
阿策小和尚
2019/08/12
7020
【Flutter 专题】38 Animation 基本动画 (二)
flutter自定义组件
如何用canvas绘制我们任何想要任意图案的组件,这篇文章用自定义一个五角星组件来说明
韦东锏
2022/04/11
5510
flutter自定义组件
【Flutter 专题】83 图解自定义 ACEWave 波浪 Widget (一)
和尚今天尝试一下绘制波浪的效果,虽然 pub 仓库中已经有成熟的插件,但和尚还是准备用之前学习的 Canvas 和 Animation 尝试自定义一个 ACEWave;
阿策小和尚
2020/04/27
9620
带你快速掌握Flutter的视图(Widgets)
在这篇文章中,将向大家分享Flutter开发中的一些视图(Widgets)相关的一些知识和经验,主要包含:
CrazyCodeBoy
2019/12/10
11.1K0
带你快速掌握Flutter的视图(Widgets)
相关推荐
【Flutter 实战】自定义动画-涟漪和雷达扫描
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文