Flutter-自定义图片3D画廊

效果

111.gif

需求
  • 3D画廊效果
设计内容
  • Stack
  • GestureDetector
  • Transform
  • Positioned
  • 数学三角函数
代码实现

具体代码大概300行

import 'dart:math';import 'package:flutter/material.dart';
import 'package:flutter_xy/widgets/xy_app_bar.dart';import '../../r.dart';class ImageSwitchPage extends StatefulWidget {const ImageSwitchPage({Key? key}) : super(key: key);@overrideState<ImageSwitchPage> createState() => _ImageSwitchPageState();
}class _ImageSwitchPageState extends State<ImageSwitchPage> {var imgList = [R.img1_jpg,R.img2_jpg,R.img3_jpg,];var deviationRatio = 0.8;@overrideWidget build(BuildContext context) {return Scaffold(appBar: XYAppBar(title: "3D画廊",onBack: () {Navigator.pop(context);},),body: Center(child: Column(children: [Expanded(child: ImageSwitchWidget(deviationRatio: deviationRatio,childWidth: 150,childHeight: 150,children: [Image.asset(R.image1_webp,),Image.asset(R.image2_webp,),Image.asset(R.image3_jpg,),Image.asset(R.image4_webp,),Image.asset(R.image5_webp,),Image.asset(R.image6_webp,),Image.asset(R.image7_webp,),]),),Slider(value: deviationRatio,onChanged: (value) {setState(() {deviationRatio = value;});},)],),),);}
}class ImageSwitchWidget extends StatefulWidget {const ImageSwitchWidget({Key? key,this.children,this.childWidth = 80,this.childHeight = 80,this.deviationRatio = 0.8,this.minScale = 0.4,this.circleScale = 1,}) : super(key: key);//所有的子控件final List<Widget>? children;//每个子控件的宽final double childWidth;//每个子控件的高final double childHeight;//偏移X系数  0-1final double deviationRatio;//最小缩放比 子控件的滑动时最小比例final double minScale;//圆形缩放系数final double circleScale;@overrideState<StatefulWidget> createState() => ImageSwitchState();
}class ImageSwitchState extends State<ImageSwitchWidget>with TickerProviderStateMixin {//所有子布局的位置信息List<Point> childPointList = [];//滑动系数final slipRatio = 0.5;//开始角度double startAngle = 0;//旋转角度double rotateAngle = 0.0;//按下时X坐标double downX = 0.0;//按下时的角度double downAngle = 0.0;//大小late Size size;//半径double radius = 0.0;late AnimationController _controller;late Animation<double> animation;late double velocityX;@overridevoid initState() {super.initState();_controller = AnimationController(vsync: this,duration: const Duration(milliseconds: 1000),);animation = CurvedAnimation(parent: _controller,curve: Curves.linearToEaseOut,);animation = Tween<double>(begin: 1, end: 0).animate(animation)..addListener(() {//当前速度var velocity = animation.value * -velocityX;var offsetX = radius != 0 ? velocity * 5 / (2 * pi * radius) : velocity;rotateAngle += offsetX;setState(() => {});})..addStatusListener((status) {if (status == AnimationStatus.completed) {}});}@overridevoid dispose() {_controller.dispose();super.dispose();}///子控件集List<Point> _childPointList({Size size = Size.zero}) {size = Size(max(widget.childWidth, size.width * widget.circleScale),max(widget.childWidth, size.height * widget.circleScale),);childPointList.clear(); //清空之前的数据if (widget.children?.isNotEmpty ?? false) {//子控件数量int count = widget.children?.length ?? 0;//平均角度double averageAngle = 360 / count;//半径radius = size.width / 2 - widget.childWidth / 2;for (int i = 0; i < count; i++) {//当前子控件的角度double angle = startAngle + averageAngle * i - rotateAngle;//当前子控件的中心点坐标  x=width/2+sin(a)*R   y=height/2+cos(a)*Rvar centerX = size.width / 2 + sin(radian(angle)) * radius;var centerY = size.height / 2 +cos(radian(angle)) * radius * cos(pi / 2 * widget.deviationRatio);var minScale = min(widget.minScale, 0.99);var scale = (1 - minScale) / 2 * (1 + cos(radian(angle - startAngle))) +minScale;childPointList.add(Point(centerX,centerY,widget.childWidth * scale,widget.childHeight * scale,centerX - widget.childWidth * scale / 2,centerY - widget.childHeight * scale / 2,centerX + widget.childWidth * scale / 2,centerY + widget.childHeight * scale / 2,scale,angle,i,));}childPointList.sort((a, b) {return a.scale.compareTo(b.scale);});}return childPointList;}@overrideWidget build(BuildContext context) {return LayoutBuilder(builder: (BuildContext context,BoxConstraints constraints,) {var minSize = min(constraints.maxWidth, constraints.maxHeight);size = Size(minSize, minSize);return GestureDetector(///水平滑动按下onHorizontalDragDown: (DragDownDetails details) {_controller.stop();},///水平滑动开始onHorizontalDragStart: (DragStartDetails details) {//记录拖动开始时当前的选择角度值downAngle = rotateAngle;//记录拖动开始时的x坐标downX = details.globalPosition.dx;},///水平滑动中onHorizontalDragUpdate: (DragUpdateDetails details) {//滑动中X坐标值var updateX = details.globalPosition.dx;//计算当前旋转角度值并刷新rotateAngle = (downX - updateX) * slipRatio + downAngle;if (mounted) setState(() {});},///水平滑动结束onHorizontalDragEnd: (DragEndDetails details) {//x方向上每秒速度的像素数velocityX = details.velocity.pixelsPerSecond.dx;_controller.reset();_controller.forward();},///滑动取消onHorizontalDragCancel: () {},behavior: HitTestBehavior.opaque,child: CustomPaint(size: size,child: Stack(children: _childPointList(size: size).map((Point point) {return Positioned(width: point.width,left: point.left,top: point.top,child: Transform(transform: Matrix4.rotationY(radian(point.angle)),alignment: AlignmentDirectional.center,child: Container(decoration: BoxDecoration(boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.5),blurRadius: 16,offset: const Offset(0, 16),),],),child: widget.children![point.index],),),);},).toList(),),),);});}///角度转弧度///弧度 =度数 * (π / 180)///度数 =弧度 * (180 / π)double radian(double angle) {return angle * pi / 180;}
}///子控件属性对象
class Point {Point(this.centerX, this.centerY, this.width, this.height, this.left,this.top, this.right, this.bottom, this.scale, this.angle, this.index);double centerX;double centerY;double width;double height;double left;double top;double right;double bottom;double scale;double angle;int index;
}

运行看看:
111.gif

详情github : github.com/yixiaolunhui/flutter_xy

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

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

相关文章

zookeeper集群安装部署和集群异常处理

准备jdk和zookeeper安装包【官网即可下载】 zookeeper-3.5.1-alpha.tar.gz jdk1.7.0_8020200612.tar 准备三台linux虚拟机【具体以项目实际需要为准】&#xff0c;并安装jdk和zookeeper 虚拟机地址如下&#xff1a;194.1.1.86&#xff08;server.1&#xff09;、194.1.1.74…

Lyricsx让歌词悬浮于最顶层

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Lyricsx让歌词悬浮于最顶层 前言Lyricsx的特色功能&#xff1a;歌词展现的新高度安装在 macOS 上安装 Lyricsx&#xff1a; 前言 在音符的世界里&#xff0c;歌词是一把打开心扉的钥匙&#xff0c;而…

全国各省市县统计年鉴/中国环境统计年鉴/中国工业企业数据库/中国专利数据库/污染排放数据库

统计年鉴是指以统计图表和分析说明为主&#xff0c;通过高度密集的统计数据来全面、系统、连续地记录年度经济、社会等各方面发展情况的大型工具书来获取统计数据资料。 统计年鉴是进行各项经济、社会研究的必要前提。而借助于统计年鉴&#xff0c;则是研究者常用的途径。目前国…

蓝桥之手撕排序算法——冒泡、选择、插入、快排、归并(Python版)

目录 1. 排序引言 2. 冒泡排序 2.1 算法思想 2.2 代码实现 2.3 时空复杂度分析 3. 选择排序 3.1 算法思想 3.2 代码实现 3.3 时空复杂度分析 4. 插入排序 4.1 算法思想 4.3 代码实现 4.4 时空复杂度分析 5. 快速排序 5.1 算法思想 5.2 代码实现 5.3 时空复杂度分…

考研C语言复习进阶(5)

目录 1. 为什么使用文件 2. 什么是文件 2.1 程序文件 2.2 数据文件 2.3 文件名 3. 文件的打开和关闭 3.1 文件指针 3.2 文件的打开和关闭 4. 文件的顺序读写 ​编辑 ​编辑 4.1 对比一组函数&#xff1a; ​编辑 5. 文件的随机读写 5.1 fseek 5.2 ftell 5.3 rewind…

深度学习实战模拟——softmax回归(图像识别并分类)

目录 1、数据集&#xff1a; 2、完整代码 1、数据集&#xff1a; 1.1 Fashion-MNIST是一个服装分类数据集&#xff0c;由10个类别的图像组成&#xff0c;分别为t-shirt&#xff08;T恤&#xff09;、trouser&#xff08;裤子&#xff09;、pullover&#xff08;套衫&#xf…

论文笔记:Llama 2: Open Foundation and Fine-Tuned Chat Models

导语 Llama 2 是之前广受欢迎的开源大型语言模型 LLaMA 的新版本&#xff0c;该模型已公开发布&#xff0c;可用于研究和商业用途。本文记录了阅读该论文的一些关键笔记。 链接&#xff1a;https://arxiv.org/abs/2307.09288 1 引言 大型语言模型&#xff08;LLMs&#xff…

深入探索C与C++的混合编程

实现混合编程的技术细节 混合使用C和C可能由多种原因驱动。一方面&#xff0c;现有的大量优秀C语言库为特定任务提供了高效的解决方案&#xff0c;将这些库直接应用于C项目中可以节省大量的开发时间和成本。另一方面&#xff0c;C的高级特性如类、模板和异常处理等&#xff0c;…

ONLYOFFICE文档8.0全新发布:私有部署、卓越安全的协同办公解决方案

ONLYOFFICE文档8.0全新发布&#xff1a;私有部署、卓越安全的协同办公解决方案 文章目录 ONLYOFFICE文档8.0全新发布&#xff1a;私有部署、卓越安全的协同办公解决方案摘要&#x1f4d1;引言 &#x1f31f;正文&#x1f4da;一、ONLYOFFICE文档概述 &#x1f4ca;二、ONLYOFFI…

2024同城招标投标微网站微信小程序版本源码

2024同城招标投标微网站&微信小程序版本源码 功能介绍&#xff1a; 同城招投标这套程序主要是为了解决招投标问题 用户缴纳保证金发布起来招标&#xff0c;然后商家进行认证成功后可以对招标发起投标&#xff0c;投标过程也需要缴纳保证金&#xff0c;单招标结束或者下架保…

第五篇:数字视频广告格式概述 - IAB视频广告标准《数字视频和有线电视广告格式指南》

第五篇&#xff1a;第五篇&#xff1a;数字视频广告格式概述 - IAB视频广告标准《数字视频和有线电视广告格式指南 --- 我为什么要翻译介绍美国人工智能科技公司IAB系列技术标准&#xff08;2&#xff09; ​​​​​​​翻译计划 第一篇序言第二篇简介和目录第三篇概述- IA…

完成系统支持Github三方登录

文章目录 1、需求2、在对接系统中完成客户端注册3、创建客户端应用4、CommonOAuth2Provider SpringSecurity OAuth2.0文档&#xff1a; https://docs.spring.io/spring-security/reference/servlet/oauth2/index.html 1、需求 对接Github&#xff0c;在自己系统实现支持Githu…

Mit6.s081 前置开发环境: 虚拟机ubuntu + ssh + vscode

虚拟机 ssh vscode 前置条件 下载VMware Download VMware Workstation ProUbuntuUbuntu系统下载 | Ubuntu vscode Visual Studio Code - Code Editing. Redefined Ubuntu版本&#xff1a;20.04 Ubuntu基本操作 ubuntu 安装 ssh 服务 sudo apt-get install openssh-serv…

TT-100K数据集,YOLO格式

TT-100K数据集YOLO格式&#xff0c;分为train、val和test&#xff0c;其中train中共有6793张图片&#xff0c;val中共有1949张图片&#xff0c;test中共有996张图片。数据集只保留包含图片数超过100的类别。共计46类。

基于支持向量机(svm)的人脸识别

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 数据集加载与可视化 from sklearn.datasets import fetch_lfw_people faces fetch_lfw_people(min_faces_per_person60) # Check out sample…

MS12_020 漏洞利用与安全加固

文章目录 环境说明1 MS12_020 简介2 MS12_020 复现过程3 MS12_020 安全加固 环境说明 渗透机操作系统&#xff1a;kali-linux-2024.1-installer-amd64漏洞复现操作系统: cn_win_srv_2003_r2_enterprise_with_sp2_vl_cd1_X13-46432 1 MS12_020 简介 MS12_020 漏洞全称为&#x…

【视频异常检测】Delving into CLIP latent space for Video Anomaly Recognition 论文阅读

Delving into CLIP latent space for Video Anomaly Recognition 论文阅读 ABSTRACT1. Introduction2. Related Works3. Proposed approach3.1. Selector model3.2. Temporal Model3.3. Predictions Aggregation3.4. Training 4. Experiments4.1. Experiment Setup4.2. Evaluat…

linux安装WordPress问题汇总,老是提示无法连接到FTP服务器解决方案

最近在做一些建站相关的事情&#xff0c;遇到一些大大小小的问题都整理在这里 1.数据库密码和端口&#xff0c;千万要复杂一点&#xff0c;不要使用默认的3306端口 2.wordpress算是一个php应用吧&#xff0c;所以安装流程一般是 apache http/nginx——php——mysql——ftp &…

ASP.NET使用Applicaiton状态来存储和检索数据

目录 背景: 实例: Button1事件: Button2事件: 效果展示: 总结: 背景: 在ASP.NET Web From应用程序中&#xff0c;Appliciton是一个内置对象&#xff0c;用于在整个Web应用程序范围内存储和检索数据&#xff0c;这意味着存储在Application对象中的数据可以被应用程序中的…

算法笔记p154最大公约数和最小公倍数

目录 最大公约数辗转相除法证明例子代码实现 最小公倍数代码实现 最大公约数 正整数a与b的最大公约数是指a与b的所有公约数中最大的那个公约数&#xff0c;一般用gcd(a, b)表示a和b的最大公约数。 辗转相除法 设a、b均为正整数&#xff0c;则gcd(a, b) gcd(b, a % b)。即被…