Flutter canvas 画一条会动的波浪线 进度条

news2025/5/20 0:13:16

之前用 Flutter Canvas 画过一个三角三角形,html 的 Canvas 也画过一次类似的, 今天用 Flutter Canvas 试了下 感觉差不多:

html 版本

大致效果如下:

思路和 html 实现的类似:

也就是找出点的位置,使用二阶贝塞尔曲线实现:

 代码如下:
import 'package:flutter/material.dart';

class PageCanvas extends StatefulWidget {
  const PageCanvas({Key? key}) : super(key: key);

  @override
  State<PageCanvas> createState() => _PageCanvasState();
}

class _PageCanvasState extends State<PageCanvas> with TickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;
  double _waveHeight = 0.5;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller = AnimationController(
        duration: const Duration(milliseconds: 2000), vsync: this);
    animation = Tween<double>(begin: 0, end: 1).animate(controller);
    controller.repeat();
  }

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Canvas'),
        backgroundColor: Colors.blue,
      ),
      body: Column(
        children: [
          AnimatedBuilder(
              animation: controller,
              builder: (context, widget) {
                return CustomPaint(
                  size: Size(size.width, size.height / 3),
                  painter: MyPainter(animation.value, _waveHeight),
                );
              }),
          const SizedBox(
            height: 60,
          ),
          Center(
            child: SizedBox(
              // color: Colors.grey,
              width: 200,
              height: 200,
              child: ClipOval(
                child: Container(
                  color: Colors.grey.withOpacity(0.3),
                  child: AnimatedBuilder(
                      animation: controller,
                      builder: (context, widget) {
                        return CustomPaint(
                          size: Size(size.width, size.height / 3),
                          painter: MyPainter2(animation.value, _waveHeight),
                        );
                      }),
                ),
              ),
            ),
          ),
          const SizedBox(
            height: 20,
          ),
          Slider(
            min: 0,
            max: 2,
            value: _waveHeight,
            onChanged: (value) {
              setState(() {
                _waveHeight = value;
              });
              //_waveHeight = value;
            },
          )
        ],
      ),
    );
  }

  @override
  void dispose() {
    controller.dispose();
    // TODO: implement dispose
    super.dispose();
  }
}

class MyPainter extends CustomPainter {
  final double value;
  final double waveHeight;
  const MyPainter(this.value, this.waveHeight);
  @override
  void paint(Canvas canvas, Size size) {
    // print(value);

    Paint paint = Paint();
    Path path = Path();

    double positionX = -size.width * value;
    double positionY = 100;
    double positionRange = 5 * (1 + waveHeight);

    double positionX2 = -size.width * (1 - value);
    double positionY2 = 110;
    double positionRange2 = 10 * (1 + waveHeight);

    double positionX3 = -size.width * (1.3 - value);
    double positionY3 = 120;
    double positionRange3 = 20 * (1 + waveHeight);

    double step = size.width / 4;
    //
    path.moveTo(0 + positionX, positionY);
    for (int i = 1; i < 13; i++) {
      if (i % 2 == 1) {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY - positionRange, step * (2 * i) + positionX, positionY);
      } else {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY + positionRange, step * (2 * i) + positionX, positionY);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.2);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX2, positionY2);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 1) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 - positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 + positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.6);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX3, positionY3);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 1) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 - positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 + positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue;
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return oldDelegate != this;
    //return true;
  }
}

class MyPainter2 extends CustomPainter {
  final double value;
  final double waveHeight;
  const MyPainter2(this.value, this.waveHeight);
  @override
  void paint(Canvas canvas, Size size) {
    // print(value);

    Paint paint = Paint();
    Path path = Path();

    double positionX = -size.width * value;
    double positionY = 50;
    double positionRange = 5 * (1 + waveHeight);

    double positionX2 = -size.width * (1 - value);
    double positionY2 = 60;
    double positionRange2 = 10 * (1 + waveHeight);

    double positionX3 = -size.width * (1.3 - value);
    double positionY3 = 70;
    double positionRange3 = 20 * (1 + waveHeight);

    double step = size.width / 4;
    //
    path.moveTo(0 + positionX, positionY);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 0) {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY - positionRange, step * (2 * i) + positionX, positionY);
      } else {
        path.quadraticBezierTo(step * (2 * i - 1) + positionX,
            positionY + positionRange, step * (2 * i) + positionX, positionY);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.2);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX2, positionY2);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 0) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 - positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX2,
            positionY2 + positionRange2,
            step * (2 * i) + positionX2,
            positionY2);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue.withOpacity(0.6);
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();

    path = Path();
    path.moveTo(0 + positionX3, positionY3);

    for (int i = 1; i < 13; i++) {
      if (i % 2 == 0) {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 - positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      } else {
        path.quadraticBezierTo(
            step * (2 * i - 1) + positionX3,
            positionY3 + positionRange3,
            step * (2 * i) + positionX3,
            positionY3);
      }
    }

    path.lineTo(step * 10, 250);
    path.lineTo(0, 250);
    path.close();
    paint.color = Colors.blue;
    canvas.drawPath(path, paint);

    canvas.save();
    canvas.restore();
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return oldDelegate != this;
    //return true;
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1425669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PySimpleGUI 综合应用|英语文本朗读以及转换为语音Mp3

PySimpleGUI 综合应用 目录 PySimpleGUI 综合应用 应用界面 完整代码 所需模块 PySimpleGUI pyttsx3 pyaudio rapidfuzz 字典格式 应用界面 完整代码 英语朗读器.pyw import PySimpleGUI as sg import pyttsx3,pyaudio,pyperclip import os,re,datetime,wave,threa…

如何分辨坏信息?

每当有社会热点&#xff0c;大家也许都会遇到一个困扰&#xff1a; 铺天盖地的信息&#xff0c;实在是太多了。究竟哪一些值得信任&#xff0c;哪些不值得信任&#xff1f;哪些可以接受&#xff0c;哪些最好保持怀疑&#xff1f; 我想用这篇文章&#xff0c;彻底把这个问题讲清…

python数据类型-元组

1 元组(tuple)的定义 元组是集合类型&#xff0c;和列表类似&#xff0c;元组中的元组类型可以不同&#xff0c;元素间用逗号隔开&#xff0c;和列表的不同之处在于&#xff1a; 1 元组的元组不可改变&#xff0c;也被称为只读列表 2 且元组用括号()表示&#xff0c;列表用方括…

HTML+JavaScript-06

节点操作 目前对于节点操作还是有些困惑&#xff0c;只是了解简单的案例 具体操作可以看菜鸟教程&#xff1a;https://www.runoob.com/js/js-htmldom-elements.html 案例-1 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8…

前端构建变更:从 webpack 换 vite

现状 这里以一个 op &#xff08;内部运营管理用&#xff09;项目为例&#xff0c;从 webpack 构建改为 vite 构建&#xff0c;提高本地开发效率&#xff0c;顺便也加深对 webpack 、 vite 的了解。 vite 是前端构建工具&#xff0c;使用 一系列预配置进行rollup 打包&#x…

获客助手助力提升企业微信添加客户的效率和精准度

数字化营销时代&#xff0c;企业微信成为企业与客户沟通的重要渠道&#xff0c;但如何快速、精准地添加潜在客户&#xff0c;一直是企业面临的一大挑战。获客助手作为一款企业微信服务商数灵通外链工具提供的营销工具&#xff0c;为企业提供了一种高效、精准的解决方案。 获客助…

【C++】 C++入门 — auto关键字

C入门 auto 关键字1 介绍2 使用细则3 注意事项 Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读下一篇文章见&#xff01;&#xff01;&#xff01; auto 关键字 1 介绍 编程时常常需要把表达式的值赋给变量&#xff0c;这就要求在声明变量时清楚地知道表达式的类…

服务网格(Service Mesh)流行工具

在这篇博客中&#xff0c;我们将介绍微服务的最佳服务网格工具列表&#xff0c;这些工具提供安全性、金丝雀部署、遥测、负载均衡等。 用于部署和操作微服务的服务网格工具的数量不断增加。在这篇文章中&#xff0c;我们将探讨您应该用来构建自己的服务网格架构的顶级服务网格…

(已解决)spingboot项目如何做QQ邮箱注册功能,如何在邮箱注册中进行随机数添加作为动态验证码,并满足分层解耦

前面我们已经完成了发送静态验证码&#xff0c;现在用随机数作为动态验证码。 文章地址&#xff1a;spingboot 后端发送QQ邮箱验证码 使用注解Component进行分层解耦加入ioc容器&#xff0c;方便调用。 package com.example.tianyidemo.utils; import org.springframework.st…

深度学习(7)--Keras项目详解(卷积神经网络)

目录 一.项目介绍 二.卷积神经网络构造 2.1.判断是否是channels first的back end 2.2.卷积层构造 2.3.添加激活函数 2.4.池化层构造 2.5.全连接FC层构造 三.完整代码 3.1.学习率衰减设置 四.首次运行结果 五.数据增强对结果的影响 六.BatchNormalization对结果的影…

MIT6.5830 实验3

前置回顾 在实验2中&#xff0c;完成了增删查改、排序、分组、聚合、连接等基本操作&#xff0c;在已提供 sql 解析器的基础上&#xff0c;能够运行进本的 sql 语句。都是逻辑层的实现&#xff0c;没有涉及物理存储方面的内容。 实验目标 实现最简单的基于锁的transaction&am…

RabbitMQ之死信交换机

前言 消息队列是分布式系统中常用的组件&#xff0c;用于异步通信、解耦和提高系统可靠性。然而&#xff0c;在实际应用中&#xff0c;难免会遇到一些异常情况&#xff0c;例如消息处理失败、超时等。为了更好地处理这些异常情况&#xff0c;死信交换机&#xff08;Dead Lette…

搭建高效企业培训平台:教育系统源码开发详解

为了更好地满足企业培训的需求&#xff0c;许多组织纷纷转向数字化教育&#xff0c;搭建高效的企业培训平台成为当务之急。本篇文章&#xff0c;小编将为您讲解教育系统源码的开发细节&#xff0c;为搭建一个功能强大、灵活高效的企业培训平台提供详尽的指南。 一、教育系统的…

“2024成都国际自动驾驶技术展览会”展示前沿技术与创新融合

近年来&#xff0c;新一轮科技革命和产业革命正向纵深发展&#xff0c;以互联网为代表的新一代信息技术与汽车产业的加速融合推动了汽车产品形态和分布的深刻变革&#xff0c;汽车已开始向大型移动智能终端的方向演变。汽车、信息、互联网等企业、研究院所、高校及各国政府纷纷…

单细胞scRNA-seq测序基础知识笔记

单细胞scRNA-seq测序基础知识笔记 scRNA-seq技术scRNA-seq 分析流程数据预处理聚类标准化数据筛选有用的数据数据降维聚类 Clustering 注释细胞类型 scRNA数据分析结尾 该笔记来源于 B站up 江湾青年 scRNA-seq技术 首先是如何测序&#xff0c;上图瓶中有很多细胞&#xff0c;…

echarts 饼图循环高亮展示

echarts 饼图循环高亮展示 this.categorychart.setOption(option);let currentIndex 0; // 当前高亮图形在饼图数据中的下标selectPie()if (this.changePieInterval)clearInterval(this.changePieInterval);this.changePieInterval setInterval(selectPie, 5000); // 设置自动…

实现vue3响应式系统核心-shallowReactive

简介 今天来实现一下 shallowReactive 这个 API。 reactive函数是一个深响应&#xff0c;当你取出的值为对象类型&#xff0c;需要再次调用 reactive进行响应式处理。很明显我们目前的代码是一个浅响应&#xff0c;即 只代理了对象的第一层&#xff0c;也就是 shallowReactiv…

以 AI 升级自我 | Kyligence 荣获多个奖项及榜单认可

回顾 2023 年的企业开年信&#xff0c;Kyligence 联合创始人兼 CEO 韩卿借用了历史上一句经典口号“时间就是金钱&#xff0c;效率就是生命”&#xff0c;鼓励团队顺势而为&#xff0c;抓住时代的机会&#xff0c;快速发展&#xff0c;快速成长。 Kyligence 一直都在践行“Ret…

【系统设计】12306架构设计难点(下)

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

Linux第38步_编译“正点原子移植好的uboot”

uboot的全称是Universal Boot Loader&#xff0c;uboot是一个遵循GPL协议的开源软件&#xff0c;uboot是一个裸机代码&#xff0c;可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB等高级功能。 uboot官方的uboot源码是给所有的半导体厂商准备的。ST公司会…