Unity3d C# 实现一个基于UGUI的自适应尺寸图片查看器(含源码)
前言
Unity3d实现的数字沙盘系统中,总有一些图片或者图片列表需要点击后弹窗显示大图,这个弹窗在不同尺寸分辨率的图片查看处理起来比较麻烦,所以,需要图片能够根据容器的大小自适应地进行缩放,兼容不太尺寸下的横竖图的展示,这个背景下,考虑写一个公共的图片查看器,能通过接口调起展示图片,自动适配尺寸能自动判定高度或者宽度自适应。加入图片的平移和缩放功能,以鼠标为中心点缩放,查看图片细节。
效果





实现
基于Unity3d自带的UGUI系统来实现,使用了DOTweenPro插件作为移动动画的实现,核心思路就是将图片根据显示区域和尺寸做高度和宽度的自适应,动态的计算高度和宽度进行设置。
单图查看器
根据之前的需求,就是对单张图片的预览,缩放、平移功能是基本操作,核心是自适应高宽的实现。
缩放使用了之前博客实现的“Unity3d UGUI以鼠标位置点为中心缩放图片”,其核心思路就是根据鼠标位置计算图片的轴心点和对应于轴心点保持图片不移动的位置信息,让后将这些数值动态的赋值给图片,最后根据滚轮前后赋值缩放值。
窗体和图片的拖拽平移使用了之前博客实现的“Unity3d C#实现UGUI的界面可拖拽移动和选中置顶等功能”,其核心思路是依托于EventTrigger组件,根据拖拽的事件(StartDrag,Draging等)和鼠标点击事件进行相关的界面的位置同步处理即可实现该功能。详情可以移步对应博客查看。
UI搭建
UI的搭建相对简单,主要是使用了Image和mask组件,用于图片显示和超出范围隐藏,同时新增了图片重置和关闭按钮,详情如下:

动画
构想的动画是通过移动、缩放和透明度动画,移动是通过点击图片或者按钮后,窗口从触发位置移动到屏幕中间,实现代码如下:
/// <summary>/// 设置移入移出位置/// </summary>/// <param name="Sender">目标对象</param>private void SetMovePos(Transform Sender){ResetImage();if (Sender == null){WindTran.localPosition = MovePos = Vector3.zero;}else{WindTran.position = Sender.position;MovePos = WindTran.localPosition;}WindTran.DOLocalMove(Vector3.zero, scaleDura);}
而缩放的动画使用了DOScale来实现:
WindTran.DOScale(Vector3.one * initScale, scaleDura); //修改默认缩放
透明度动画是使用CanvasGroup组件来整体控制窗口:
cg.DOFade(1, 0.3f);
尺寸适应
尺寸的适应采用高宽比的思路,如果宽度比 比 高度比高就适配宽度,反之则适配高度:
float WidRate = sp.texture.width / ImgAreaSize.x;
float HeightRate = sp.texture.height / ImgAreaSize.y; ;//Screen.height;if (WidRate >= HeightRate) //适配宽度
{/*float Width = ImgAreaSize.x;float Height = (ImgAreaSize.x / sp.texture.width) * sp.texture.height ;*/showImgRect.sizeDelta = new Vector2(ImgAreaSize.x, (ImgAreaSize.x / sp.texture.width) * sp.texture.height);
}
else //适配高度
{/*float Width = ImgAreaSize.y / sp.texture.height * sp.texture.width;float Height = ImgAreaSize.y;*/showImgRect.sizeDelta = new Vector2(ImgAreaSize.y / sp.texture.height * sp.texture.width, ImgAreaSize.y);
}
调起接口
接口申明如下:
/// <summary>/// 接口,展示公共弹窗/// </summary>/// <param name="sp">预览的Sprite</param>/// <param name="tran">弹出的节点</param>
public void ShowImgWindow(Sprite sp, Transform tran = null)
传入预览的Sprite和弹出的节点(用于动画位置计算),即可调起窗口。
多图查看器
上面的单图查看器在使用起来有个问题,面对图片列表使用该功能时,多个图片查看操作较为繁琐,比如同一个Scroll View下的图片,查看起来需要点开、关闭、点开、关闭的操作,这种情况下需要支持多张图片,可以快速下一张上一张的操作。有此需求,就需要支持多图查看的功能。多图的UI在单图的基础上新增了两个上一张/下一张的按钮:

多图切换
多图的逻辑是接口处发送多张图片,调起多图查看器,这时候将多张图放入到列表(List)中点击上一张、下一张时候直接切换对应的图片即可。
nowIndex = idx;
if (!isImg)
{if (sprites == null || sprites.Count == 0 || idx < 0 || idx >= sprites.Count)return;showImg.sprite = sprites[nowIndex];float WidRate = sprites[nowIndex].texture.width / ImgAreaSize.x;float HeightRate = sprites[nowIndex].texture.height / ImgAreaSize.y; ;//Screen.height;if (WidRate >= HeightRate) //适配宽度{showImgRect.sizeDelta = new Vector2(ImgAreaSize.x, (ImgAreaSize.x / sprites[nowIndex].texture.width) * sprites[nowIndex].texture.height);}else //适配高度{showImgRect.sizeDelta = new Vector2(ImgAreaSize.y / sprites[nowIndex].texture.height * sprites[nowIndex].texture.width, ImgAreaSize.y);}NextBtn.SetActive(nowIndex < sprites.Count - 1);
}if (isImg)
{if (images == null || images.Count == 0 || idx < 0 || idx >= images.Count)return;showImg.sprite = images[nowIndex].sprite;float WidRate = images[nowIndex].sprite.texture.width / ImgAreaSize.x;float HeightRate = images[nowIndex].sprite.texture.height / ImgAreaSize.y; if (WidRate >= HeightRate) //适配宽度{showImgRect.sizeDelta = new Vector2(ImgAreaSize.x, (ImgAreaSize.x / images[nowIndex].sprite.texture.width) * images[nowIndex].sprite.texture.height);}else //适配高度{showImgRect.sizeDelta = new Vector2(ImgAreaSize.y / images[nowIndex].sprite.texture.height * images[nowIndex].sprite.texture.width, ImgAreaSize.y);}NextBtn.SetActive(nowIndex < images.Count - 1);
}LastBtn.SetActive(nowIndex > 0);
ResetImage();
大致逻辑如上图,因为需要兼容Image和RawImage组件所以使用了 List和List两个列表。
调起接口
常用接口是将图片的父节点传入,自动区分Image和RawImage组件:
/// <summary>/// 接口,展示公共弹窗/// 传入图片列表的父节点,适用图片所有都父节点的一级子节点。/// </summary>/// <param name="parentTran">父节点</param>/// <param name="idx">图片的下标(默认0)</param>/// <param name="tran">弹出的节点</param>
public void ShowImgWindow(Transform parentTran, bool isTypeImg = true, int idx = 0, Transform tran = null)
调用代码如下:
ImgShowListWindMgr.instance?.ShowImgWindow(sender.transform.parent, true,GetNodeActiveIndex(sender.transform), sender.transform);
传入Image列表的接口:
//接口,展示公共弹窗/// <summary>/// Image列表预览图片/// </summary>/// <param name="imgList">Image列表</param>/// <param name="idx">图片的下标(默认0)</param>/// <param name="tran">弹出的节点</param>
public void ShowImgWindow(List<Image> imgList, int idx = 0, Transform tran = null)
传入Sprite列表的接口:
//接口,展示公共弹窗
/// <summary>
/// Sprite列表预览图片
/// </summary>
/// <param name="spList"> Sprite列表</param>
/// <param name="idx">图片的下标(默认0)</param>
/// <param name="tran">弹出的节点</param>
public void ShowImgWindow(List<Sprite> spList, int idx = 0, Transform tran = null)
传入RawImage列表的接口:
//接口,展示公共弹窗
/// <summary>
/// RawImage 列表预览图片
/// </summary>
/// <param name="rimgList"> RawImage 列表预览图片</param>
/// <param name="idx">图片的下标(默认0)</param>
/// <param name="tran">弹出的节点</param>
public void ShowRawImgWindow(List<RawImage> rimgList, int idx = 0, Transform tran = null)
源码工程
https://download.csdn.net/download/qq_33789001/90036779
相关文章:
Unity3d C# 实现一个基于UGUI的自适应尺寸图片查看器(含源码)
前言 Unity3d实现的数字沙盘系统中,总有一些图片或者图片列表需要点击后弹窗显示大图,这个弹窗在不同尺寸分辨率的图片查看处理起来比较麻烦,所以,需要图片能够根据容器的大小自适应地进行缩放,兼容不太尺寸下的横竖图…...
【es6进阶】vue3中的数据劫持的最新实现方案的proxy的详解
vuejs中实现数据的劫持,v2中使用的是Object.defineProperty()来实现的,在大版本v3中彻底重写了这部分,使用了proxy这个数据代理的方式,来修复了v2中对数组和对象的劫持的遗留问题。 proxy是什么 Proxy 用于修改某些操作的默认行为࿰…...
w~视觉~3D~合集3
我自己的原文哦~ https://blog.51cto.com/whaosoft/12538137 #SIF3D 通过两种创新的注意力机制——三元意图感知注意力(TIA)和场景语义一致性感知注意力(SCA)——来识别场景中的显著点云,并辅助运动轨迹和姿态的预测…...
IT服务团队建设与管理
在 IT 服务团队中,需要明确各种角色。例如系统管理员负责服务器和网络设备的维护与管理;软件工程师专注于软件的开发、测试和维护;运维工程师则保障系统的稳定运行,包括监控、故障排除等。通过清晰地定义每个角色的职责࿰…...
一文学习开源框架OkHttp
OkHttp 是一个开源项目。它由 Square 开发并维护,是一个现代化、功能强大的网络请求库,主要用于与 RESTful API 交互或执行网络通信操作。它是 Android 和 Java 开发中非常流行的 HTTP 客户端,具有高效、可靠、可扩展的特点。 核心特点 高效…...
自研芯片逾十年,亚马逊云科技Graviton系列芯片全面成熟
在云厂商自研芯片的浪潮中,亚马逊云科技无疑是最早践行这一趋势的先驱。自其迈出自研芯片的第一步起,便如同一颗石子投入平静的湖面,激起了层层涟漪,引领着云服务和云上算力向着更高性能、更低成本的方向演进。 早在2012年&#x…...
Stable Diffusion 3 部署笔记
SD3下载地址:https://huggingface.co/stabilityai/stable-diffusion-3-medium/tree/main https://huggingface.co/spaces/stabilityai/stable-diffusion-3-medium comfyui 教程: 深度测评:SD3模型表现如何?实用教程助你玩转Stabl…...
微信小程序WXSS全局样式与局部样式的使用教程
微信小程序WXSS全局样式与局部样式的使用教程 引言 在微信小程序的开发中,样式的设计与实现是提升用户体验的关键部分。WXSS(WeiXin Style Sheets)作为微信小程序的样式表语言,不仅支持丰富的样式功能,还能通过全局样式与局部样式的灵活运用,帮助开发者构建美观且易于维…...
Docker 部署 MongoDB
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🍃 vue-uniapp-template 🌺 仓库主页: GitCode💫 Gitee …...
Unity图形学之法线贴图原理
1.正常贴图:RGBA 4通道 每个通道取值范围 0-255 贴图里面取值是 0-1 2.法线贴图:法线怎么存入正常贴图的过程 每个通道里面存储的是一个向量(x,y,z,w) 通常我们会对应xyzw为rgba 存储值的范围也是0-1向量的取值范围是 -1到1法线怎么存入正常贴图的过程&…...
爬虫开发(5)如何写一个CSDN热门榜爬虫小程序
笔者 綦枫Maple 的其他作品,欢迎点击查阅哦~: 📚Jmeter性能测试大全:Jmeter性能测试大全系列教程!持续更新中! 📚UI自动化测试系列: SeleniumJava自动化测试系列教程❤ 📚…...
JVM系列之OOM观测准备
OOM, 全称 “Out Of Memory”,即内存用完的意思。JVM 因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时(可分配内存大于需要分配的内存), 就会抛出 java.lang.OutOfMemoryError。在实际的生产应用中,一旦…...
Qt | 开发技能提升档次哈
点击上方"蓝字"关注我们 01、Creator常用快捷键 >>> F1 查看帮助 F2 跳转到函数定义 Shift+F2 声明和定义之间切换 F3 查找下一个 F4 头文件和源文件之间切换 Ctrl+1 欢迎模式 Ctrl+2 编辑模…...
D79【 python 接口自动化学习】- python基础之HTTP
day79 requests模块发送请求 学习日期:20241125 学习目标:http定义及实战 -- requests模块进行get请求带参数&requests模块进行post请求 学习笔记: requests模块进行get请求 import requestsparams{"shouji":"130999…...
C++【日志模块中的writer类】前文中 循环队列用法
用到前文中的循环队列模板 /* ** File name: LogWriter.h ** Author: ** Date: 2024-11-4 ** Brief: 日志写入类 ** Note: 日志写入类,负责将日志写入文件和连接客户端。 ** Copyright (C) 1392019713qq.com All rights reserve…...
Linux:文件管理(一)——文件描述符fd
目录 一、文件基础认识 二、C语言操作文件的接口 1.> 和 >> 2.理解“当前路径” 三、相关系统调用 1.open 2.文件描述符 3.一切皆文件 4.再次理解重定向 一、文件基础认识 文件 内容 属性。换句话说,如果在电脑上新建了一个空白文档࿰…...
【C++初阶】第3课—类和对象(类的默认成员函数)
文章目录 1. 类的默认成员函数2. 构造函数3. 拷贝构造函数3.1 传值传参3.2 传值返回3.3 深拷贝和浅拷贝3.4 总结 4. 析构函数5. 赋值运算符重载5.1 运算符重载5.2 赋值运算符重载5.3 日期类的实现 6. 取地址运算符重载6.1 const 成员函数6.2 取地址运算符重载 1. 类的默认成员函…...
uni-app初学笔记:文件路径与作用
components:可复用的组件pages:页面(可见/不可见)static:静态资源,存放图片视频等 (相当于vue项目的 assets)mainjs:Vue初始化入口文件App.vue:应用配置,用来配置App全局样式以及监听pages.json :配置页面路…...
小程序-使用 iconfont 图标库报错:Failed to load font
官方默认可以忽略此错误,在清除缓存后首次刷新会显示此错误,重新渲染错误消失 解决方法: 在 iconfont 图标库选择项目设置 选中 Base64 保存,重新点击链接 -> 复制代码到项目中 操作步骤:...
【计网】自定义协议与序列化(一) —— Socket封装于服务器端改写
🌎 应用层自定义协议与序列化 文章目录: Tcp协议Socket编程 应用层简介 序列化和反序列化 重新理解read/write/recv/send及tcp的全双工 Socket封装 服务器端改写 🚀应用层简介 我们程序员写的一个个解决…...
滞回比较器设计实战:从理论到参数优化
1. 滞回比较器基础:从门铃到航天器的抗噪神器 第一次接触滞回比较器是在大学电子设计课上,当时教授用一个生动的例子开场:"想象你家的门铃——如果它对任何风吹草动都响个不停,你会疯掉;但如果连用力敲门都没反应…...
学习如何聚合零样本大型语言模型代理以进行企业披露分类
摘要本文研究一个轻量级训练聚合器是否能够将多样化的零样本大语言模型判断整合为更强的下游信号,用于公司披露分类。零样本大语言模型无需针对特定任务进行微调即可阅读披露文本,但其预测结果常因提示词、推理方式和模型家族的不同而存在差异。我采用一…...
Cogito-v1-preview-llama-3B效果展示:中英日法等30+语言生成质量对比
Cogito-v1-preview-llama-3B效果展示:中英日法等30语言生成质量对比 1. 模型核心能力概览 Cogito v1预览版是Deep Cogito推出的混合推理模型系列,在大多数标准基准测试中均超越了同等规模下最优的开源模型。这个3B参数的模型在编码、STEM、指令执行和通…...
精读《Harness design for long-running application development》:真正拉开差距的,不是模型本身,而是你怎么给它harness
精读《Harness design for long-running application development》:真正拉开差距的,不是模型本身,而是你怎么给它搭脚手架 原文:Harness design for long-running application development Anthropic 这篇文章最值得读的地方&a…...
从音频生成到DNA分析:手把手带你用S4和Hyena搞定Transformer不擅长的那些长序列任务
从音频生成到DNA分析:手把手带你用S4和Hyena搞定Transformer不擅长的那些长序列任务 当我们需要处理长达数小时的音频波形、百万碱基对的DNA序列或整本小说级别的文本时,传统Transformer架构很快就会遇到计算瓶颈。本文将带您探索两种突破性的序列建模方…...
nli-distilroberta-base实际项目:高校招生简章关键条款与考生疑问逻辑关系库构建
nli-distilroberta-base实际项目:高校招生简章关键条款与考生疑问逻辑关系库构建 1. 项目背景与需求 高校招生简章通常包含大量专业条款和政策说明,每年都会收到大量考生关于条款理解的咨询。传统的人工解答方式存在几个痛点: 效率低下&am…...
PaddleOCR-VL-1.5:0.9B VLM实现文档解析新SOTA
PaddleOCR-VL-1.5:0.9B VLM实现文档解析新SOTA 【免费下载链接】PaddleOCR-VL-1.5-GGUF 项目地址: https://ai.gitcode.com/paddlepaddle/PaddleOCR-VL-1.5-GGUF 导语:百度飞桨团队推出PaddleOCR-VL-1.5,以0.9B参数量的轻量化视觉语言…...
RWKV7-1.5B-G1A入门实战:手把手教你写文案、做总结、玩对话
RWKV7-1.5B-G1A入门实战:手把手教你写文案、做总结、玩对话 1. 认识RWKV7-1.5B-G1A RWKV7-1.5B-G1A是一个基于RWKV-7架构的多语言文本生成模型,特别适合处理基础问答、文案续写、简短总结和轻量中文对话任务。这个1.5B参数的模型在保持良好生成质量的同…...
MyBatis 中 CDATA 的实战应用与避坑指南
1. 为什么MyBatis需要CDATA 在MyBatis的日常开发中,我们经常需要在XML映射文件中编写SQL语句。但XML本身对特殊字符有着严格的限制,比如小于号(<)、大于号(>)、和号(&)等字符在XML中都有特殊含义。这就导致了一个很现实的问题:当我…...
ESP32嵌入式系统设计与实现指南
1. 项目概述1.1 系统架构本项目基于ESP32主控芯片设计,采用模块化架构实现多功能嵌入式系统。系统包含以下核心模块:主控单元:ESP32-WROOM-32D模组电源管理:TPS63020升降压转换器传感器接口:I2C/SPI多协议兼容设计人机…...
