flutter 画转盘

import 'package:flutter/material.dart';
import 'dart:math';const double spacingAngle = 45.0; // 每两个文字之间的角度
// 自定义绘制器,ArcTextPainter 用于在圆弧上绘制文字
class ArcTextPainter extends CustomPainter {final double rotationAngle; // 动画旋转角度final double strokeWidth; // 圆环的宽度final List<String> text; // 文字列表final double curIndex; // 当前旋转进度ArcTextPainter({required this.rotationAngle,required this.strokeWidth,required this.text,required this.curIndex,});@overridevoid paint(Canvas canvas, Size size) {final radius = size.width / 2; // 圆的半径final center = Offset(size.width / 2, size.height / 2); // 圆心的坐标// 创建用于绘制圆弧的画笔final paint = Paint()..color = Colors.grey.shade300 // 圆弧的颜色..strokeWidth = strokeWidth // 圆弧的宽度..style = PaintingStyle.stroke; // 画笔样式为描边// 计算圆弧的矩形区域final arcRect = Rect.fromCircle(center: center, radius: radius - strokeWidth / 2);canvas.drawArc(arcRect, pi, pi, false, paint); // 绘制圆弧// 创建用于绘制箭头的画笔final arrowPaint = Paint()..color = Colors.purple // 箭头的颜色..style = PaintingStyle.fill; // 画笔样式为填充// 定义箭头的路径final arrowPath = Path();arrowPath.moveTo(center.dx, center.dy - radius + strokeWidth / 2); // 箭头起点arrowPath.lineTo(center.dx - 10, center.dy - radius + strokeWidth / 2 + 20); // 箭头的左边arrowPath.lineTo(center.dx + 10, center.dy - radius + strokeWidth / 2 + 20); // 箭头的右边arrowPath.close(); // 结束路径canvas.drawPath(arrowPath, arrowPaint); // 绘制箭头// 绘制圆弧上的文字_drawTextAlongArc(canvas, center, radius - strokeWidth / 2);}// 在圆弧上绘制文字void _drawTextAlongArc(Canvas canvas, Offset center, double radius) {final textPainter = TextPainter(textAlign: TextAlign.center, // 文字对齐方式为居中textDirection: TextDirection.ltr, // 文字方向为从左到右);// 遍历所有文字并绘制for (int i = 0; i < text.length; i++) {// 计算当前文字的角度double angle = (i - curIndex) * spacingAngle * (pi / 180) - pi/2;// print("angle:${i} ${angle*180/pi}");// 检查文字是否在可视范围内if (angle >= -pi && angle <= 0) {// 计算文字的位置final x = center.dx + radius * cos(angle); // x 坐标final y = center.dy + radius * sin(angle); // y 坐标canvas.save(); // 保存当前画布状态canvas.translate(x, y); // 移动画布到文字的位置// 设置文字的样式和内容textPainter.text = TextSpan(text: text[i],style: TextStyle(fontSize: 14, color: Colors.black), // 文字的样式);textPainter.layout(); // 计算文字的大小// 计算文字的实际可见区域double visibleFraction = _calculateVisibleFraction(angle);if (visibleFraction < 1.0) {// 如果文字不完全可见,则应用裁剪遮罩canvas.clipRect(Rect.fromLTWH(-textPainter.width / 2, // 左上角 x 坐标-textPainter.height / 2, // 左上角 y 坐标textPainter.width, // 文字的宽度textPainter.height, // 文字的高度));}textPainter.paint(canvas, Offset(-textPainter.width / 2, -textPainter.height / 2)); // 绘制文字canvas.restore(); // 恢复画布状态}}}// 计算文字的可见比例double _calculateVisibleFraction(double angle) {// 文字显示的比例,确保在 [-pi, 0] 范围内显示完全if (angle < -pi / 2) {return max(0, (angle + pi) / (pi / 2)); // 文字被遮挡的部分} else if (angle > 0) {return max(0, (-angle) / (pi / 2)); // 文字被遮挡的部分}return 1.0; // 文字完全可见}@overridebool shouldRepaint(CustomPainter oldDelegate) => true; // 是否需要重新绘制
}// ArcTextExample 是一个示例 widget,用于展示自定义绘制的效果
class ArcTextExample extends StatefulWidget {final double strokeWidth; // 圆环的宽度final List<String> text; // 文字列表final int initialIndex; // 初始索引final double animationDuration; // 动画持续时间const ArcTextExample({Key? key,required this.strokeWidth,required this.text,required this.initialIndex,required this.animationDuration,}) : super(key: key);@override_ArcTextExampleState createState() => _ArcTextExampleState();
}class _ArcTextExampleState extends State<ArcTextExample>with SingleTickerProviderStateMixin {late AnimationController _controller; // 动画控制器late Animation<double> _animation; // 动画double curIndex = 0.0; // 保存当前旋转的进度bool isAnimating = false; // 标记动画是否正在进行final TextEditingController indexController = TextEditingController(); // 目标索引的文本控制器final TextEditingController durationController = TextEditingController(); // 动画持续时间的文本控制器@overridevoid initState() {super.initState();// 初始化文本控制器的值indexController.text = widget.initialIndex.toString();durationController.text = widget.animationDuration.toString();// 计算初始旋转角度double initialAngle = ( - widget.initialIndex ) * spacingAngle * (pi / 180) - pi / 2;curIndex = widget.initialIndex.toDouble(); // 初始化时 curIndex 是初始索引// 创建动画控制器_controller = AnimationController(duration: Duration(seconds: widget.animationDuration.toInt()), // 设置动画的持续时间vsync: this, // 与当前的 TickerProvider 绑定);// print("initialAngle: ${initialAngle*180/pi}");// 创建动画_animation = Tween<double>(begin: initialAngle, // 动画开始的角度end: initialAngle + 2 * pi, // 动画结束的角度).animate(_controller)..addListener(() {setState(() {print("_animation.value:  ${_animation.value * 180 / pi}");// 更新当前角度对应的索引范curIndex = (-(_animation.value + pi / 2) * (180 / pi)) / spacingAngle;print("Current Index: ${curIndex.toStringAsFixed(2)}"); // 打印当前索引});});}@overridevoid dispose() {_controller.dispose(); // 释放动画控制器资源indexController.dispose(); // 释放目标索引的文本控制器资源durationController.dispose(); // 释放动画持续时间的文本控制器资源super.dispose();}// 旋转到目标索引void rotateToIndex(int targetIndex, double duration) {if(targetIndex != curIndex){setState(() {if (isAnimating) {// 如果正在进行动画,则停止并重置_controller.stop();isAnimating = false;}_controller.duration = Duration(seconds: duration.toInt()); // 设置动画的持续时间double startAngle = (-curIndex) * spacingAngle * (pi / 180) - pi / 2; // 使用当前索引角度作为起始角度double targetAngle = (-targetIndex) * spacingAngle * (pi / 180) - pi / 2;  // 计算目标角度print("开始度数: ${startAngle * 180/pi} 结束度数:${targetAngle * 180/pi}");double endAngle;// 确定旋转方1if (targetAngle < 0) {// 顺时针旋转endAngle = startAngle + targetAngle;} else {// 逆时针旋转endAngle = startAngle - targetAngle;}_animation = Tween<double>(begin: startAngle, // 动画开始的角度end: targetAngle, // 动画结束的角度).animate(_controller);isAnimating = true; // 标记动画为进行中_controller.reset(); // 重置动画控制_controller.forward(); // 开始动画});}}@overrideWidget build(BuildContext context) {return Scaffold(body: Column(children: [SizedBox(height: 140), // 上部间距Center(child: CustomPaint(size: Size(300, 200), // 设置圆弧的大小painter: ArcTextPainter(rotationAngle: _animation.value, // 当前旋转角度strokeWidth: widget.strokeWidth, // 圆环的宽度text: widget.text, // 文字列表curIndex: curIndex, // 当前旋转进度),),),SizedBox(height: 20), // 下部间距Padding(padding: const EdgeInsets.symmetric(horizontal: 20.0), // 水平内边距child: Column(children: [// 目标索引的输入框TextField(controller: indexController,decoration: InputDecoration(labelText: 'Target Index', // 输入框标签),keyboardType: TextInputType.number, // 键盘类型为数字),// 动画持续时间的输入框TextField(controller: durationController,decoration: InputDecoration(labelText: 'Animation Duration (seconds)', // 输入框标签),keyboardType: TextInputType.number, // 键盘类型为数字),SizedBox(height: 20), // 输入框和按钮之间的间距// 旋转按钮ElevatedButton(onPressed: () {// 获取目标索引和动画持续时间int targetIndex = int.tryParse(indexController.text) ?? 0;double duration = double.tryParse(durationController.text) ?? 10.0;// if (isAnimating) {//   // 如果动画正在进行,停止并保存当前进度//   _controller.stop();//   curIndex = (-(_animation.value + pi / 2) * (180 / pi)) / spacingAngle; // 保存当前进度为 curIndex//   _controller.reset(); // 重置动画控制器// }// 旋转到目标索引rotateToIndex(targetIndex, duration);},child: Text('Rotate to Index'), // 按钮文本),],),),],),);}
}void main() {runApp(MaterialApp(home: ArcTextExample(strokeWidth: 100.0, // 圆环的宽度text: List.generate(11, (i) => '第$i层'), // 文字列表initialIndex: 3, // 初animationDuration: 10.0, // 默认动画时间为10秒),));
}

​​​​​​​

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

elementplus 二次封装 select 自定义指令上拉加载更多 完美解决 多次接口调用 重新加载数据多次调用数据!!!

效果&#xff1a;&#xff08;名字都是测试数据 随便乱写的 若有冒犯 请联系&#xff09; select 二次封装 网上的这种自定义指令上拉加载更多的实例有很多&#xff0c;但是基本都是有缺陷和问题的。为了记录这个问题 我研究了一天&#xff0c;在今天终于搞定了 呜呜呜。 网上…

TSP-Detection:CMU 提出抛弃交叉注意力的 DETR 变体 | ICCV 2021

DETR基于Transformer将目标检测视为集合预测问题&#xff0c;实现了最先进的性能&#xff0c;但需要超长的训练时间才能收敛。论文研究了DETR优化困难的原因&#xff0c;揭示了收敛缓慢因素主要是匈牙利损失和Transformer交叉注意机制。为了克服这些问题&#xff0c;论文提出了…

微信小程序--26(全局配置-1)

一、全局配置文件 1.标志 app.json 2.配置项 pages 记录当前小程序所有页面的存放路径 window 全局配置小程序窗口配置 tabBar 设置小程序底部的tabBar效果 style 是否启用新版本的组将样式 3.window 导航栏区域 navigationBar …

11 Linux 设备驱动

11 Linux 设备驱动 1、Linux软件特点1.1 用户空间1.2 内核空间 2、Linux程序2.1 应用程序2.2 内核程序2.2.1 编程2.2.2 编译 2.3 内核命令行传参2.3.1 应用程序的命令行传参2.3.2 内核程序命令行传参 2.4 内核程序符号导出2.4.1 应用程序多文件之间的访问调用2.4.1 内核多文件之…

1Panel应用推荐:KubePi开源Kubernetes管理面板

1Panel&#xff08;github.com/1Panel-dev/1Panel&#xff09;是一款现代化、开源的Linux服务器运维管理面板&#xff0c;它致力于通过开源的方式&#xff0c;帮助用户简化建站与运维管理流程。为了方便广大用户快捷安装部署相关软件应用&#xff0c;1Panel特别开通应用商店&am…

H7-TOOL混合脱机烧录以及1拖4不同的通道烧录不同的程序操作说明(2024-08-07)

【应用场景】 原本TOOL的1拖4是用于同时烧录相同程序给目标板&#xff0c;但有时候一个板子上有多个不同的MCU&#xff0c; 客户希望仅通过一个TOOL就可以完成对板子上多个MCU的烧录&#xff0c;也就是1拖4不同的通道烧录不同的程序&#xff0c;此贴为此制作。 【实验目标】…

序列建模之循环和递归网络 - 循环神经网络篇

序言 在探索序列数据的深层规律时&#xff0c;循环神经网络&#xff08; RNN \text{RNN} RNN&#xff09;以其独特的设计思想成为了序列建模领域的中流砥柱。与传统的神经网络不同&#xff0c; RNN \text{RNN} RNN引入了循环结构&#xff0c;使得网络能够处理任意长度的序列数…

winform 大头针实现方法——把窗口钉在最上层

平时我们再使用成熟的软件的时候&#xff0c;会发现有个大头针的功能挺不错的。就是点一下大头针&#xff0c;窗口就会钉住&#xff0c;一直保持在最上面一层&#xff0c;这样可以一边设置参数&#xff0c;一边观察这个窗口里面的变化&#xff0c;比较方便。下面我就来简单实现…

移动APP测试有哪些注意事项?专业APP测试报告如何获取?

移动APP在其生命周期中有不同的阶段&#xff0c;从开始到投入目标市场再到被淘汰。移动APP的成功有多种因素&#xff0c;例如创建、部署、推广、粘性等。但是&#xff0c;创建出色APP的关键在于它的测试&#xff0c;软件测试负责为客户提供安全有效的产品&#xff0c;因此移动A…

大数据-83 Spark 集群 RDD编程简介 RDD特点 Spark编程模型介绍

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Golang 与 Java:编程语言比较及如何选择

Golang 与 Java&#xff1a;哪种语言更好&#xff1f;我们的详细比较指南涵盖了语法、性能和流行度方面的主要差异&#xff0c;以帮助您做出决定。 在规划项目时&#xff0c;有许多编程语言可供选择。但一开始就选择正确的语言是成功启动或交付的关键。选择错误的语言&#xff…

Apache Tomcat 信息泄露漏洞排查处理CVE-2024-21733)

一、漏洞描述 Apache Tomcat作为一个流行的开源Web服务器和Java Servlet容器并用于很多中小型项目的开发中。其中,Coyote作为Tomcat的连接器组件,是Tomcat服务器提供的供客户端访问的外部接口,客户端通过Coyote与服务器建立链接、发送请求并且接收响应。 近日发现Apache To…

【python】OpenCV—Optical Flow

文章目录 1、光流2、Opencv 中光流的实现3、稀疏光流4、密集光流4.1、farneback4.2、lucaskanade_dense4.3、rlof 5、涉及到的库5.1、cv2.goodFeaturesToTrack5.2、cv2.calcOpticalFlowPyrLK5.3、cv2.optflow.calcOpticalFlowSparseToDense5.4、cv2.calcOpticalFlowFarneback5.…

CentOS7.9上通过KVM安装Centos虚拟机

目录 1 开发前准备&#xff08;先确保服务器可以虚拟化&#xff09;&#xff1a; 2、安装KWM环境 3、创建镜像文件存放目录 4、创建镜像文件存放目录 5、安装桥连接虚拟网络 6、安装虚拟机 7、配置操作系统 8、虚拟机配置网卡地址 9、克隆虚拟机执行 1开发前准备&am…

Unity教程(十)Tile Palette搭建平台关卡

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

IDEA 创建类时自动生成注释

一、背景 在开发的过程中&#xff0c;公司都会要求开发针对自己创建的类进行一些描述说明&#xff0c;为了便于程序员在创建类时快速生成注释。 二、如何配置? 打开File -> Settings -> Editor -> File and Code Templates -> Includes&#xff0c;在File Header…

Unity新输入系统结构概览

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正 在学习新输入系统之前&#xff0c;我们需要对其构成有个印象 1.输入动作&#xff08;Inputaction&#xff09; 是定义输…

一次caffeine引起的CPU飙升问题

背景 背景是上游服务接入了博主团队提供的sdk&#xff0c;已经长达3年&#xff0c;运行稳定无异常&#xff0c;随着最近冲业绩&#xff0c;流量越来越大&#xff0c;直至某一天&#xff0c;其中一个接入方&#xff08;流量很大&#xff09;告知CPU在慢慢上升且没有回落的迹象&…

2分钟搭建一个简单的WebSocket服务器

你好同学&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏和关注。个人知乎 如何用2分钟在本地搭建一个简单的 WebSocket 服务器&#xff1f;其实使用 Node.js&#xff0c;加上一些流行的库&#xff0c;是很容易实现的。前端同学通过自己搭建 WebSocket 服务器&#xff0c;对于…

百问网全志系列开发板音频ALSA配置步骤详解

8 ALSA 8.1 音频相关概念 ​ 音频信号是一种连续变化的模拟信号&#xff0c;但计算机只能处理和记录二进制的数字信号&#xff0c;由自然音源得到的音频信号必须经过一定的变换&#xff0c;成为数字音频信号之后&#xff0c;才能送到计算机中作进一步的处理。 ​ 数字音频系…