Unity实现仿3D轮转图效果
作者:Hello?Bug. 发布时间:2023-11-24 12:26:56
本文实例为大家分享了Unity实现仿3D轮转图效果的具体代码,供大家参考,具体内容如下
一、效果演示
二、实现思路
——获取位置:可以将每个item的运行轨迹看作一个椭圆,分为四段进行观察,四个黑点视为四个item,观察四个黑点的位置,比例值为0.125和0.375的位置相同,比例值为0.625和0.875的位置相同,比例值为0.375和0.625的位置相反,可得结论
[0,0.25]:轨迹总长度*当前比例值
(0.25,0.5]:轨迹总长度 * (0.5 - 当前比例值)
(0.5,0.75]:轨迹总长度 * (0.5 - 当前比例值)
(0.75,1]:轨迹总长度 * (当前比例值 - 1)
——获取缩放值:可以将每个item的运行轨迹看作一个椭圆,分为四段进行观察,四个黑点视为四个item,观察四个黑点的位置,比例值为0时缩放值应为最大,比例值为0.5时缩放值应为最小,可得结论
[0-0.5]:缩放最大值 - 比例值 * (缩放最大值 - 缩放最小值) * 2
(0.5-1]:缩放最大值 - (1 - 比例值) * (缩放最大值 - 缩放最小值) * 2
——获取层级:使用UGUI的自然层级进行排序(越靠下越后渲染),拷贝一份列表item数据列表按照缩放值从小到大的顺序排序,再通过SetSiblingIndex依次设置层级
三、使用
——常规使用
SetData:传入item预制体和列表中item个数
OnSetItem:绑定设置item的方法
SetList:设置列表的显示
using UnityEngine;
using UnityEngine.UI;
public class Test : MonoBehaviour
{
public GameObject prefab;
public Rotary3DList rotary3DList;
private void Start()
{
rotary3DList.SetData(prefab, 5);
rotary3DList.OnSetItem = SetItem;
rotary3DList.SetList();
}
void SetItem(Rotary3DList.ListItemData listItemData)
{
listItemData.go.GetComponent<Text>().txt = listItemData.index.ToString();
}
}
——MoveToIndex:移动到某一个下标位置,isScroll表示是否滑动到指定位置
——GetListItemData:获取到某个下标的item数据
——CenterIndex:当前中心点item下标
四、代码实现
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Linq;
/// <summary>
/// 仿3D轮转图组件
/// </summary>
[AddComponentMenu("LFramework/UI/Rotary3DList", 50)]
[RequireComponent(typeof(Image))]
public class Rotary3DList : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
/// <summary>
/// 列表item数据
/// </summary>
public class ListItemData
{
public int index;
public GameObject go;
public float targetValue;//目标位置长度值
public float tempValue;//临时位置长度值(每次拖拽结束后才更新数值)
}
/// <summary>
/// 轮转类型
/// </summary>
public enum RotaryType
{
Horizontal,
Vertical,
}
public RotaryType rotaryType;//轮转类型
public float spacing;//间隔
public float maxScale = 1;//最大缩放值
public float minScale = 0.5f;//最小缩放值
public float t = 0.1f;//缓动插值
public Action<ListItemData> OnSetItem;//设置item
public Action<PointerEventData> OnDragBegin;//拖拽开始
public Action<PointerEventData> OnDragging;//拖拽中
public Action<PointerEventData> OnDragEnd;//拖拽结束
//中心item下标
public int CenterIndex
{
get { return GetCenterItemIndex(); }
}
int m_ItemCount;//列表item总数量
float m_TotalValue;//总长度值
float m_DeltaValue;//长度值增量
GameObject m_Prefab;
RectTransform m_ItemContainer;
List<ListItemData> m_ListItemDataList = new List<ListItemData>();
bool m_InDrag;
float m_BeginPos;
List<float> m_InitValueList = new List<float>();//初始每个item的位置长度值
/// <summary>
/// 设置数据
/// </summary>
public void SetData(GameObject prefab, int itemCount)
{
m_ItemContainer = GetComponent<RectTransform>();
m_ItemCount = itemCount;
m_Prefab = prefab;
m_DeltaValue = rotaryType == RotaryType.Horizontal
? (spacing + m_Prefab.GetComponent<RectTransform>().rect.width)
: (spacing + m_Prefab.GetComponent<RectTransform>().rect.height);
m_TotalValue = m_DeltaValue * m_ItemCount;
InitData();
}
/// <summary>
/// 初始化数据
/// </summary>
void InitData()
{
float tempValue = 0;
for (int i = 0; i < m_ItemCount; i++)
{
ListItemData data = new ListItemData();
data.index = i;
data.go = Instantiate(m_Prefab, transform, m_ItemContainer);
data.targetValue = tempValue;
data.tempValue = tempValue;
m_ListItemDataList.Add(data);
m_InitValueList.Add(tempValue);
tempValue += m_DeltaValue;
}
m_InitValueList.Add(m_TotalValue);
}
/// <summary>
/// 设置列表
/// </summary>
public void SetList()
{
foreach (var data in m_ListItemDataList)
{
OnSetItem?.Invoke(data);
}
UpdateItem(true);
}
/// <summary>
/// 移动到某个下标位置
/// </summary>
public void MoveToIndex(int index, bool isScroll = true)
{
if (index < 0
|| index >= m_ItemCount)
{
Debug.LogError("下标超出范围,index : " + index);
return;
}
int indexOffset = CenterIndex - index;
foreach (var data in m_ListItemDataList)
{
float tempValue = data.tempValue + m_DeltaValue * indexOffset < 0
? data.tempValue + m_DeltaValue * indexOffset + m_TotalValue
: data.tempValue + m_DeltaValue * indexOffset;
float targetValue = tempValue % m_TotalValue;
data.targetValue = targetValue;
data.tempValue = targetValue;
}
UpdateItem(!isScroll);
}
public void OnBeginDrag(PointerEventData eventData)
{
m_InDrag = true;
m_BeginPos = rotaryType == RotaryType.Horizontal
? eventData.position.x
: eventData.position.y;
OnDragBegin?.Invoke(eventData);
}
public void OnDrag(PointerEventData eventData)
{
OnDragging?.Invoke(eventData);
float endPos = rotaryType == RotaryType.Horizontal
? eventData.position.x
: eventData.position.y;
float offset = endPos - m_BeginPos;
//计算item数据
if (offset > 0)
{
foreach (var data in m_ListItemDataList)
{
float tempValue = (data.tempValue + offset) % m_TotalValue;
data.targetValue = tempValue;
}
}
else if (offset < 0)
{
foreach (var data in m_ListItemDataList)
{
float tempValue = data.tempValue + offset < 0
? m_TotalValue - Mathf.Abs(data.tempValue + offset) % m_TotalValue
: (data.tempValue + offset) % m_TotalValue;
data.targetValue = tempValue;
}
}
}
public void OnEndDrag(PointerEventData eventData)
{
m_InDrag = false;
OnDragEnd?.Invoke(eventData);
foreach (var data in m_ListItemDataList)
{
float nearlyValue = GetNearlyValue(data.targetValue);
data.targetValue = nearlyValue;
data.tempValue = nearlyValue;
}
}
private void Update()
{
UpdateItem(false);
}
/// <summary>
/// 更新item
/// </summary>
void UpdateItem(bool isForce)
{
//拖拽中-实时更新
if (m_InDrag)
{
foreach (var data in m_ListItemDataList)
{
float ratio = data.targetValue / m_TotalValue;
//更新位置
float pos = GetPos(ratio);
Vector2 targetPos = rotaryType == RotaryType.Horizontal
? new Vector2(pos, 0)
: new Vector2(0, pos);
data.go.transform.localPosition = targetPos;
//更新缩放值
float scale = GetScale(ratio);
Vector2 targetScale = Vector3.one * scale;
data.go.transform.localScale = targetScale;
}
}
//非拖拽中-缓动更新
else
{
foreach (var data in m_ListItemDataList)
{
float ratio = data.targetValue / m_TotalValue;
//更新位置
float pos = GetPos(ratio);
Vector2 targetPos = rotaryType == RotaryType.Horizontal
? new Vector2(pos, 0)
: new Vector2(0, pos);
float targetPosOffset = rotaryType == RotaryType.Horizontal
? data.go.transform.localPosition.x - targetPos.x
: data.go.transform.localPosition.y - targetPos.y;
data.go.transform.localPosition = Vector2.Lerp(data.go.transform.localPosition, targetPos, isForce ? 1 : t);
if (Mathf.Abs(targetPosOffset) <= 0.01f)
{
data.go.transform.localPosition = targetPos;
}
//更新缩放值
float scale = GetScale(ratio);
Vector2 targetScale = Vector3.one * scale;
float targetScaleOffset = data.go.transform.localScale.x - targetScale.x;
data.go.transform.localScale = Vector2.Lerp(data.go.transform.localScale, targetScale, isForce ? 1 : t);
if (Mathf.Abs(targetScaleOffset) <= 0.01f)
{
data.go.transform.localScale = targetScale;
}
}
}
//更新层级
var listItemDataList = m_ListItemDataList.OrderBy(data => GetScale(data.targetValue / m_TotalValue)).ToList();
for (int i = 0; i < m_ItemCount; i++)
{
listItemDataList[i].go.transform.SetSiblingIndex(i);
}
}
/// <summary>
/// 得到位置
/// </summary>
float GetPos(float ratio)
{
if (ratio < 0
&& ratio > 1)
{
Debug.LogError("比例值错误,比例值必须为[0-1],ratio : " + ratio);
return 0;
}
if (ratio >= 0 && ratio <= 0.25f)
{
return m_TotalValue * ratio;
}
else if (ratio > 0.25f && ratio <= 0.75f)
{
return m_TotalValue * (0.5f - ratio);
}
else
{
return m_TotalValue * (ratio - 1);
}
}
/// <summary>
/// 得到缩放值
/// </summary>
float GetScale(float ratio)
{
if (ratio < 0
&& ratio > 1)
{
Debug.LogError("比例值错误,比例值必须为[0-1],ratio : " + ratio);
return 0;
}
float v = (maxScale - minScale) * 2;
if (ratio >= 0 && ratio <= 0.5f)
{
return maxScale - ratio * v;
}
else
{
return maxScale - (1 - ratio) * v;
}
}
/// <summary>
/// 得到距离最近的位置长度值
/// </summary>
float GetNearlyValue(float curValue)
{
float minDis = Mathf.Abs(curValue - m_InitValueList.First());
float nearlyValue = m_InitValueList.First();
foreach (var value in m_InitValueList)
{
float tempDis = Mathf.Abs(curValue - value);
if (tempDis < minDis)
{
minDis = tempDis;
nearlyValue = value == m_TotalValue
? m_InitValueList.First()
: value;
}
}
return nearlyValue;
}
/// <summary>
/// 得到中心item的下标
/// </summary>
int GetCenterItemIndex()
{
int index = 0;
float minDis = Mathf.Min(Mathf.Abs(m_ListItemDataList[0].targetValue - m_InitValueList.First()), Mathf.Abs(m_ListItemDataList[0].targetValue - m_InitValueList.Last()));
foreach (var data in m_ListItemDataList)
{
float tempDis = Mathf.Min(Mathf.Abs(data.targetValue - m_InitValueList.First()), Mathf.Abs(data.targetValue - m_InitValueList.Last()));
if (tempDis < minDis)
{
minDis = tempDis;
index = data.index;
}
}
return index;
}
/// <summary>
/// 得到列表item数据
/// </summary>
public ListItemData GetListItemData(int index)
{
if (index < 0
|| index >= m_ItemCount)
{
Debug.LogError("下标超出范围,index : " + index);
return null;
}
return m_ListItemDataList[index];
}
}
来源:https://blog.csdn.net/LLLLL__/article/details/122271245


猜你喜欢
- 如下所示:TextView tv = (TextView) findViewById(R.id.text); tv.getPaint().s
- 一、前言最近做的项目由于引入第三方库导致在运行mvn clean package 打jar时,编译出来的 Jar 包很大(服务器多达500M
- IISExpress 配置允许外部访问详细介绍1.找到IISExpress的配置文件,位于 <文档>/IISExpr
- 项目开发的过程中,经常会遇到添加水印的需求,其作用无非就是防止重要信息通过截图外传。(虽然我觉得并没有什么卵用,但领导的需求是不容质疑的)那
- 一、问题分析入门案例的内容已经做完了,在入门案例中我们创建过一个SpringMvcConfig的配置类,再回想前面咱们学习Spring的时候
- 什么是反射机制反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个ja
- 本文初步探讨了C#缓存的原理及应用,并以实例加以分析总结,这些对C#初学者来说是很有必要熟练掌握的内容。具体如下:一、概述:缓存应用目的:缓
- 先说结论:对于有捕获的lambda,其等价于对象。对于没有任何捕获的lambda,其等价于函数!首先,很多C++程序员从lambda 用法上
- Web服务器是运行及发布Web应用的容器,只有将开发的Web项目放置到该容器中,才能使网络中的所有用户通过浏览器进行访问。开发Java We
- Maven --> Gradle首先安装gradle:Mac安 * rew install gradleUbuntu安装apt inst
- XY个人记SparkSQL是spark的一个模块,主入口是SparkSession,将SQL查询与Spark程序无缝混合。DataFrame
- 0.导入命名空间:using Microsoft.Office.Core;using Microsoft.Office.Interop.Ex
- 刚开始做开发学习的小伙伴可能在有一个知识上面有错误的认知,我们天天写程序是在Idea下写的,运行也是在Idea下运行的。但是实际开发完成后,
- 0.背景简介微软在 .NET 框架中提供了多种实用的线程同步手段,其中包括 monitor 类及 reader-writer锁。
- 需求介绍相信大家在请求接口的时候,很多时候都是需要传参的,除了业务必要的字段外,还有一些恒定不变的字段,包括一些用来编码的固定字段。这些固定
- 本文实例讲述了Java生产者消费者模式。分享给大家供大家参考,具体如下:java的生产者消费者模式,有三个部分组成,一个是生产者,一个是消费
- 先说下这个demo,这是一个模仿课程表的布局文件,虽然我是个菜鸟,但我还是想留给学习的人一些例子,先看下效果 然后再来看一下我们学
- 先要把word或ppt转换为pdf; 以pdf的格式展示,防止文件拷贝。转换方法1、安装Word、Excel、PowerPoint组件注意:
- 一、简介JetCache是一个基于Java的缓存系统封装,提供统一的API和注解来简化缓存的使用。 JetCache提供了比SpringCa
- 本文实例为大家分享了java 利用Socket实现SMTP协议发送邮件的具体代码,供大家参考,具体内容如下package mail;impo