当前位置: 首页 > news >正文

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实现的数字沙盘系统中&#xff0c;总有一些图片或者图片列表需要点击后弹窗显示大图&#xff0c;这个弹窗在不同尺寸分辨率的图片查看处理起来比较麻烦&#xff0c;所以&#xff0c;需要图片能够根据容器的大小自适应地进行缩放&#xff0c;兼容不太尺寸下的横竖图…...

【es6进阶】vue3中的数据劫持的最新实现方案的proxy的详解

vuejs中实现数据的劫持,v2中使用的是Object.defineProperty()来实现的&#xff0c;在大版本v3中彻底重写了这部分&#xff0c;使用了proxy这个数据代理的方式&#xff0c;来修复了v2中对数组和对象的劫持的遗留问题。 proxy是什么 Proxy 用于修改某些操作的默认行为&#xff0…...

w~视觉~3D~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/12538137 #SIF3D 通过两种创新的注意力机制——三元意图感知注意力&#xff08;TIA&#xff09;和场景语义一致性感知注意力&#xff08;SCA&#xff09;——来识别场景中的显著点云&#xff0c;并辅助运动轨迹和姿态的预测…...

IT服务团队建设与管理

在 IT 服务团队中&#xff0c;需要明确各种角色。例如系统管理员负责服务器和网络设备的维护与管理&#xff1b;软件工程师专注于软件的开发、测试和维护&#xff1b;运维工程师则保障系统的稳定运行&#xff0c;包括监控、故障排除等。通过清晰地定义每个角色的职责&#xff0…...

一文学习开源框架OkHttp

OkHttp 是一个开源项目。它由 Square 开发并维护&#xff0c;是一个现代化、功能强大的网络请求库&#xff0c;主要用于与 RESTful API 交互或执行网络通信操作。它是 Android 和 Java 开发中非常流行的 HTTP 客户端&#xff0c;具有高效、可靠、可扩展的特点。 核心特点 高效…...

自研芯片逾十年,亚马逊云科技Graviton系列芯片全面成熟

在云厂商自研芯片的浪潮中&#xff0c;亚马逊云科技无疑是最早践行这一趋势的先驱。自其迈出自研芯片的第一步起&#xff0c;便如同一颗石子投入平静的湖面&#xff0c;激起了层层涟漪&#xff0c;引领着云服务和云上算力向着更高性能、更低成本的方向演进。 早在2012年&#x…...

Stable Diffusion 3 部署笔记

SD3下载地址&#xff1a;https://huggingface.co/stabilityai/stable-diffusion-3-medium/tree/main https://huggingface.co/spaces/stabilityai/stable-diffusion-3-medium comfyui 教程&#xff1a; 深度测评&#xff1a;SD3模型表现如何&#xff1f;实用教程助你玩转Stabl…...

微信小程序WXSS全局样式与局部样式的使用教程

微信小程序WXSS全局样式与局部样式的使用教程 引言 在微信小程序的开发中,样式的设计与实现是提升用户体验的关键部分。WXSS(WeiXin Style Sheets)作为微信小程序的样式表语言,不仅支持丰富的样式功能,还能通过全局样式与局部样式的灵活运用,帮助开发者构建美观且易于维…...

Docker 部署 MongoDB

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f343; vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode&#x1f4ab; Gitee &#x1f…...

Unity图形学之法线贴图原理

1.正常贴图&#xff1a;RGBA 4通道 每个通道取值范围 0-255 贴图里面取值是 0-1 2.法线贴图&#xff1a;法线怎么存入正常贴图的过程 每个通道里面存储的是一个向量(x,y,z,w) 通常我们会对应xyzw为rgba 存储值的范围也是0-1向量的取值范围是 -1到1法线怎么存入正常贴图的过程&…...

爬虫开发(5)如何写一个CSDN热门榜爬虫小程序

笔者 綦枫Maple 的其他作品&#xff0c;欢迎点击查阅哦~&#xff1a; &#x1f4da;Jmeter性能测试大全&#xff1a;Jmeter性能测试大全系列教程&#xff01;持续更新中&#xff01; &#x1f4da;UI自动化测试系列&#xff1a; SeleniumJava自动化测试系列教程❤ &#x1f4da…...

JVM系列之OOM观测准备

OOM, 全称 “Out Of Memory”&#xff0c;即内存用完的意思。JVM 因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时&#xff08;可分配内存大于需要分配的内存&#xff09;, 就会抛出 java.lang.OutOfMemoryError。在实际的生产应用中&#xff0c;一旦…...

Qt | 开发技能提升档次哈

点击上方"蓝字"关注我们 01、Creator常用快捷键 >>> F1 查看帮助 F2 跳转到函数定义 Shift+F2 声明和定义之间切换 F3 查找下一个 F4 头文件和源文件之间切换 Ctrl+1 欢迎模式 Ctrl+2 编辑模…...

D79【 python 接口自动化学习】- python基础之HTTP

day79 requests模块发送请求 学习日期&#xff1a;20241125 学习目标&#xff1a;http定义及实战 -- requests模块进行get请求带参数&requests模块进行post请求 学习笔记&#xff1a; requests模块进行get请求 import requestsparams{"shouji":"130999…...

C++【日志模块中的writer类】前文中 循环队列用法

用到前文中的循环队列模板 /* ** File name: LogWriter.h ** Author: ** Date: 2024-11-4 ** Brief: 日志写入类 ** Note: 日志写入类&#xff0c;负责将日志写入文件和连接客户端。 ** Copyright (C) 1392019713qq.com All rights reserve…...

Linux:文件管理(一)——文件描述符fd

目录 一、文件基础认识 二、C语言操作文件的接口 1.> 和 >> 2.理解“当前路径” 三、相关系统调用 1.open 2.文件描述符 3.一切皆文件 4.再次理解重定向 一、文件基础认识 文件 内容 属性。换句话说&#xff0c;如果在电脑上新建了一个空白文档&#xff0…...

【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:页面&#xff08;可见/不可见&#xff09;static:静态资源&#xff0c;存放图片视频等 &#xff08;相当于vue项目的 assets&#xff09;mainjs:Vue初始化入口文件App.vue:应用配置&#xff0c;用来配置App全局样式以及监听pages.json :配置页面路…...

小程序-使用 iconfont 图标库报错:Failed to load font

官方默认可以忽略此错误&#xff0c;在清除缓存后首次刷新会显示此错误&#xff0c;重新渲染错误消失 解决方法&#xff1a; 在 iconfont 图标库选择项目设置 选中 Base64 保存&#xff0c;重新点击链接 -> 复制代码到项目中 操作步骤&#xff1a;...

【计网】自定义协议与序列化(一) —— Socket封装于服务器端改写

&#x1f30e; 应用层自定义协议与序列化 文章目录&#xff1a; Tcp协议Socket编程 应用层简介 序列化和反序列化       重新理解read/write/recv/send及tcp的全双工       Socket封装       服务器端改写 &#x1f680;应用层简介 我们程序员写的一个个解决…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...