flutter开发实战-美颜前后对比图效果实现

flutter开发实战-美颜前后对比图效果实现
在这里插入图片描述

最近使用代码中遇到了图片前后对比,这里使用的是CustomClipper来实现

一、CustomClipper

我们实现CustomClipper子类来实现美颜后的图片裁剪功能

getClip()是用于获取剪裁区域的接口,由于图片大小是60×60,
我们返回剪裁区域为Rect.fromLTWH(10.0, 15.0, 40.0, 30.0),即图片中部40×30像素的范围。
shouldReclip() 接口决定是否重新剪裁。
如果在应用中,剪裁区域始终不会发生变化时应该返回false,这样就不会触发重新剪裁,避免不必要的性能开销。
如果剪裁区域会发生变化(比如在对剪裁区域执行一个动画),那么变化后应该返回true来重新执行剪裁。

二、使用CustomClipper来实现美颜前后对比图效果

美颜前后对比图,原图展示,美颜后的图片根据手势拖动距离进行裁剪
美颜之后的图裁剪,设置Clip.hardEdge。

ClipRect(clipper: compareCustomClipper,clipBehavior: Clip.hardEdge,child: CachedNetworkImage(imageUrl: "https://qiniu.example.com/64c2fba1-81ff-41dc-b32e-6920b0677f8c0",fit: BoxFit.cover,width: widget.width,height: widget.height,),),

手势拖动,更新compareCustomClipper

void onHorizontalDragDown(DragDownDetails details) {print("onHorizontalDragDown");startOffsetX = details.localPosition.dx;print("onHorizontalDragDown startOffsetX:${startOffsetX}");}void onHorizontalDragStart(DragStartDetails details) {print("onHorizontalDragStart");}void onHorizontalDragUpdate(DragUpdateDetails details) {print("onHorizontalDragUpdate");double curOffsetX = details.localPosition.dx;double distance = curOffsetX - startOffsetX;print("onHorizontalDragUpdate curOffsetX:${curOffsetX}, startOffsetX:${startOffsetX}, distance:${distance}");offsetX = widget.width! + distance;if (offsetX > widget.width!) {offsetX = widget.width!;}if (offsetX < 0) {offsetX = 0;}compareCustomClipper = CompareCustomClipper(Rect.fromLTWH(offsetX, 0.0, (widget.width ?? 0) - offsetX, widget.height ?? 0));setState(() {});}

完整代码如下


import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';class ComparePicPage extends StatefulWidget {const ComparePicPage({super.key});@overrideState<ComparePicPage> createState() => _ComparePicPageState();
}class _ComparePicPageState extends State<ComparePicPage> {@overrideWidget build(BuildContext context) {Size size = MediaQuery.of(context).size;return Scaffold(appBar: AppBar(title: const Text('ComparePicPage'),),body: Center(child: ComparePicWidget(width: 320,height: 480,),),);}
}// 自定义裁剪CustomClipper
class CompareCustomClipper extends CustomClipper<Rect> {CompareCustomClipper(this.rect);Rect rect;// Rect getClip(Size size) => Rect.fromLTWH(0.0, 15.0, 40.0, 30.0);@overrideRect getClip(Size size) => rect;@overridebool shouldReclip(CustomClipper<Rect> oldClipper) => true;
}/// 图片美颜前后对比
class ComparePicWidget extends StatefulWidget {const ComparePicWidget({super.key,this.width,this.height,});final double? width;final double? height;@overrideState<ComparePicWidget> createState() => _ComparePicWidgetState();
}class _ComparePicWidgetState extends State<ComparePicWidget>with TickerProviderStateMixin {// 定义一个裁剪CompareCustomClipper? compareCustomClipper;// late AnimationController _animateController =//     AnimationController(vsync: this, duration: Duration(milliseconds: 2500));// late Animation<double> _animation =//     CurvedAnimation(parent: _animateController, curve: Curves.linear);double offsetX = 0;double startOffsetX = 0;bool isStarted = false;// // 显示Tip// bool isShowTip = true;@overridevoid initState() {// TODO: implement initStateoffsetX = widget.width ?? 0;compareCustomClipper = CompareCustomClipper(Rect.fromLTWH(offsetX, 0.0, (widget.width ?? 0) - offsetX, widget.height ?? 0));super.initState();}@overridevoid dispose() {// TODO: implement disposesuper.dispose();}void onHorizontalDragDown(DragDownDetails details) {print("onHorizontalDragDown");if (isStarted == false) {startOffsetX = details.globalPosition.dx;}isStarted = true;print("onHorizontalDragDown startOffsetX:${startOffsetX}");}void onHorizontalDragStart(DragStartDetails details) {print("onHorizontalDragStart");}void onHorizontalDragUpdate(DragUpdateDetails details) {print("onHorizontalDragUpdate");double curOffsetX = details.globalPosition.dx;double distance = curOffsetX - startOffsetX;print("onHorizontalDragUpdate curOffsetX:${curOffsetX}, startOffsetX:${startOffsetX}, distance:${distance}");offsetX = widget.width! + distance;offsetX = offsetX.clamp(0, widget.width!);compareCustomClipper = CompareCustomClipper(Rect.fromLTWH(offsetX, 0.0, (widget.width ?? 0) - offsetX, widget.height ?? 0));setState(() {});}@overrideWidget build(BuildContext context) {return Container(width: widget.width,height: widget.height,decoration: BoxDecoration(color: Colors.black,borderRadius: const BorderRadius.all(Radius.circular(10),),border: Border.all(color: Colors.transparent,width: 0,style: BorderStyle.solid,),),clipBehavior: Clip.none,child: Stack(alignment: Alignment.center,clipBehavior: Clip.none,children: [// 原图ClipRect(clipBehavior: Clip.hardEdge,child: CachedNetworkImage(imageUrl: "https://u3d-qiniu.dface.cn/Fsgjbe7O8Z5x83_Aff8-Qage9bpc.png",fit: BoxFit.cover,width: widget.width,height: widget.height,),),// 美颜之后的图ClipRect(clipper: compareCustomClipper,clipBehavior: Clip.hardEdge,child: CachedNetworkImage(imageUrl: "https://u3d-qiniu.dface.cn/64c2fba1-81ff-41dc-b32e-6920b0677f83",fit: BoxFit.cover,width: widget.width,height: widget.height,),),// linePositioned(left: offsetX + 26.5 + (-27.5),child: buildLine(context),),Positioned(left: offsetX + (-27.5),child: buildCustomButton(context),),// tipPositioned(left: 20,top: 20,child: buildCompareTip(context, "原图"),),Positioned(right: 20,top: 20,child: buildCompareTip(context, "美颜后"),),],),);}Widget buildLine(BuildContext context) {return Image.asset("assets/images/line.png",width: 2,height: 576,fit: BoxFit.cover,);}Widget buildCustomButton(BuildContext context) {return GestureDetector(onHorizontalDragDown: (DragDownDetails details) {onHorizontalDragDown(details);},onHorizontalDragStart: (DragStartDetails details) {onHorizontalDragStart(details);},onHorizontalDragUpdate: (DragUpdateDetails details) {onHorizontalDragUpdate(details);},child: Image.asset("assets/images/move_button.png",width: 55,height: 55,fit: BoxFit.cover,),);}Widget buildCompareTip(BuildContext context, String title) {return Container(width: 60,height: 30,alignment: Alignment.center,decoration: BoxDecoration(color: Colors.black.withOpacity(0.35),borderRadius: const BorderRadius.all(Radius.circular(20),),),child: Text(title,maxLines: 1,overflow: TextOverflow.ellipsis,textAlign: TextAlign.center,style: const TextStyle(fontSize: 13,fontWeight: FontWeight.w600,fontStyle: FontStyle.normal,color: Colors.white,decoration: TextDecoration.none,),),);}
}

三、小结

flutter开发实战-美颜前后对比图效果实现

学习记录,每天不停进步。

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

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

相关文章

AI联想扩图解决方案,智能联想,无需人工干预

对于众多企业而言&#xff0c;无论是广告宣传、产品展示还是客户体验&#xff0c;高质量、宽广视野的图像都是不可或缺的。受限于车载摄像头等设备的物理限制&#xff0c;我们往往难以捕捉到完整、宽广的视觉场景。针对这一挑战&#xff0c;美摄科技凭借其前沿的AI联想扩图解决…

bugku windows 2008应急加固

开始实验&#xff1a; 实验靶场为Windows server 2008&#xff0c;使用给出的账号及密码远程桌面连接服务器。 1、提权方式 请输入黑客的提权方式&#xff08;如有字母&#xff0c;请转换小写&#xff09; 上传河马到服务器&#xff0c;进行安装&#xff0c;然后扫一下站点…

关于Word目录的更新

左侧标题顺序如有调整&#xff0c;自动目录并不会同步更新&#xff0c;每次都要记得在正文目录左上角点击更新目录

排序进阶----插入排序,希尔排序

各位看官们好&#xff0c;接下来鄙人想与大家分享的实现被称为六大排序之一的插入排序。其实关于这六大排序在我们最开始就已经接触过了。我们在最开始学习c语言的时候&#xff0c;我们要学习到其中之一的冒泡排序。虽然现在看起来冒泡排序确实是没有太大的实际效果&#xff0c…

单线程 vs 多进程:Python网络爬虫效率对比

概述 在网络爬虫的开发过程中&#xff0c;性能优化是一个重要的考虑因素。本文将概述单线程和多进程在Python网络爬虫中的应用&#xff0c;并对比它们的效率。 单线程爬虫是最基本的爬虫模型&#xff0c;它按顺序一个接一个地处理任务。这种方法的优点是实现简单&#xff0c;易…

2024最新TikTok抖音国际版,tiktok正版免拔卡安装来了!

保姆级教程&#xff01;2024最新TikTok抖音国际版&#xff0c;无限制&#xff01;tiktok正版免拔卡安装方法来了&#xff01; TikTok这款APP为何让全球都为之疯狂&#xff1f;因为它更懂人性&#xff0c;懂的人都懂&#xff01; 我是你的老朋友阿星&#xff0c;今天阿星要给大…

7777777777777

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;贝叶斯滤波与Kalman估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能&#xff0c…

LeetCode---栈与队列

232. 用栈实现队列 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int pee…

揭秘SQL中的公用表表达式:数据查询的新宠儿

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 揭秘SQL中的公用表表达式&#xff1a;数据查询的新宠儿 前言公用表表述的概述非递归CTE的作用递归CTE的作用CTE性能优化 前言 你是否曾经为SQL查询的复杂性而困扰不已&#xff1f;尤其是那些读写层子…

leetCode.84. 柱状图中最大的矩形

leetCode.84. 柱状图中最大的矩形 题目思路 代码 class Solution { public:int largestRectangleArea( vector<int>& h ) {int n h.size();vector<int> left( n ), right( n );stack<int> st;// 求每个矩形的第一个小于左边界的矩形 - 用单调栈for ( …

【云原生】kubernetes中Configmap原理解析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

深入解读Meta分析:原理、公式、操作步骤及结果分析;R语言Meta回归分析、诊断分析、不确定性分析与精美作图

目录 专题一 Meta分析的选题与文献计量分析CiteSpace应用 专题二 Meta分析与R语言数据清洗及相关应用 专题三 R语言Meta分析与精美作图 专题四 R语言Meta回归分析 专题五 R语言Meta诊断分析与进阶 专题六 R语言Meta分析的不确定性及贝叶斯应用 专题七 深度拓展机器学习在…

HNU-计算机体系结构-实验1-RISC-V流水线

计算机体系结构 实验1 计科210X 甘晴void 202108010XXX 1 实验目的 参考提供为了更好的理解RISC-V&#xff0c;通过学习RV32I Core的设计图&#xff0c;理解每条指令的数据流和控制信号&#xff0c;为之后指令流水线及乱序发射实验打下基础。 参考资料&#xff1a; RISC-…

图形学初识--矩阵和向量

文章目录 前言正文向量什么是向量&#xff1f;向量涉及哪些常见计算&#xff1f;1、取模2、归一化3、向量加法4、向量减法5、向量与标量乘6、向量点乘&#xff08;内积&#xff09;7、向量投影 向量有哪些基本应用&#xff1f; 矩阵什么是矩阵&#xff1f;矩阵涉及哪些常见计算…

PyTorch张量索引用法速查

作为数据科学家或软件工程师&#xff0c;你可能经常处理大型数据集和复杂的数学运算&#xff0c;这些运算需要高效且可扩展的计算。PyTorch 是一个流行的开源机器学习库&#xff0c;它通过 GPU 加速提供快速灵活的张量计算。在本文中&#xff0c;我们将深入研究 PyTorch 张量索…

Ant Design 动态增减form表单,第二三项根据第一项选中内容动态展示内容

效果图&#xff1a; 选中第一项下拉框&#xff0c;第二第三项展示 点击添加条件&#xff0c;第二条仍然只展示第一项select框 后端返回数据格式&#xff1a; ruleList:[{name:通话时长,key:TALK_TIME,type&#xff1a;’INT‘,unitName:秒,operaObj:[{name:>,value:>…

【旋转链表】python

目录 题目&#xff1a; 思路&#xff1a; 代码&#xff1a; 题目&#xff1a; 思路&#xff1a; 求链表长度&#xff1b;找出倒数第 k1 个节点&#xff1b; 3.链表重整&#xff1a;将链表的倒数第 k1 个节点和倒数第 k个节点断开&#xff0c;并把后半部分拼接到链表的头部。…

基于STM32实现智能交通灯控制系统

目录 引言环境准备智能交通灯控制系统基础代码示例&#xff1a;实现智能交通灯控制系统 GPIO控制交通灯定时器配置与使用红外传感器检测车辆用户界面与显示应用场景&#xff1a;城市交通管理与自动化控制问题解决方案与优化收尾与总结 1. 引言 本教程将详细介绍如何在STM32嵌…

python-合并排列数组 I

问题描述&#xff1a;合并两个按升序排列的整数数组a和b&#xff0c;形成一个新数组&#xff0c;新数组也要按升序排列。 问题示例&#xff1a;输入A[1],B[1],输出[1,1],返回合并后的数组。输入A[1,2,3,4],B[2,4,5,6],输出[1,2,2,3,4,4,5,6],返回合并所有元素后的数组。 完整代…

武汉城投城更公司与竹云科技签署战略协议,携手构建智慧城市新未来!

2024年5月16日&#xff0c;武汉城投城更公司与深圳竹云科技股份有限公司&#xff08;以下简称“竹云”&#xff09;签订战略合作协议&#xff0c;双方将深入推进产业项目合作。 签约现场&#xff0c;双方围绕产业项目合作方向、路径和内容等进行了全面深入交流。城投城更公司党…