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

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


http://www.kler.cn/a/415127.html

相关文章:

  • Linux中使用ping提示“未知的名称或服务”
  • vue3-setup基本使用(非响应式数据)
  • List集合的进一步学习:性能优化
  • Java全栈开发:宠物医院管理系统项目实战
  • 如何分析Windows防火墙日志
  • 103.【C语言】数据结构之二叉树的三种递归遍历方式
  • 【CSS】设置文本超出N行省略
  • 第六篇:其他窗口部件 QLineEdit
  • 更快更省更划算:了解亚马逊云科技自研芯片
  • Vue表单绑定入
  • 【GPT】为什么要力量训练?
  • 使用easyexcel导出复杂模板,同时使用bean,map,list填充
  • MT管理器v2.14.5-MT管理器-能强大的Android文件管理工具,主要用于管理和编辑手机中的文件-MT管理器vip版本下载-登录即可有vip
  • 02.ES6(2)
  • docker-elasticsearch-kibana-logstash
  • Vue Promise的使用,界面使用异步线程循环执行方法(模拟线程)
  • Java基础面试题08:Java中Exception和Error有什么区别?
  • IDEA如何快速地重写方法,如equals、toString等
  • Sybase数据恢复—Sybase数据库无法启动,Sybase Central连接报错的处理案例
  • 反向代理服务器的用途
  • uniapp关闭sourceMap的生成,提高编译、生产打包速度
  • 如何配置 Gitea 的邮箱功能
  • React Native 原生开发指南
  • MySQL并发事务问题和隔离级别
  • Ubuntu 18.04 中安装 RDKit(针对 Python 2.7)
  • vim 显示行数和删除内容操作