图片的轮播展示效果如果使用2D实现,需要将3D中存在的近大远小效果使用图片的缩放呈现,因此需要存储和计算图片的位置同时还要计算存储图片的缩放信息。将所有图片的位置连线看作是一个椭圆,就可以根据图片的个数获得所有图片在椭圆上的位置,从0-1均匀分布,如4个图片位置为0、0.25、0.5、0.75,5个图片位置为0、0.2、0.4、0.6、0.8。根据这个位置可以分别计算图片在2D平面上的实际位置(投影的位置)和图片的缩放比例,然后根据这些信息生成图片并实现在鼠标拖动过程中图片的改变,使用DOTWEEN可以方便实现动画效果。
将RotationDiagram2D挂载到空物体上,之后生成的图片都是这个物体的子物体,这个脚本是轮播图的管理脚本。RotationDiagramItem是挂载在图片上的脚本,在生成图片时也会添加这个脚本,实现了图片的运动等各种效果。
using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UI; public class RotationDiagram2D : MonoBehaviour { public Vector2 ItemSize; //图片的大小 public Sprite[] ItemSprites; //图片精灵集合 public float ScaleMax; //图片的最大scale public float ScaleMin; //图片的最小scale public float Offset; //图片的偏移量(相邻图片的间隔) private List<RotationDiagramItem> _items; //存储所有图片RotationDiagramItem组件的集合 private List<ItemPosData> _posData; //存储所有图片ItemPosData的集合 /// <summary> /// 调用相应的方法进行初始化 /// </summary> private void Start() { _items = new List<RotationDiagramItem>(); _posData = new List<ItemPosData>(); CreateItem(); CalculateData(); SetItemData(); } /// <summary> /// 生成临时的空物体,在空物体上添加各种组件并返回 /// </summary> /// <returns></returns> private GameObject CreateTemplate() { GameObject item = new GameObject("Template"); item.AddComponent<RectTransform>().sizeDelta = ItemSize; item.AddComponent<Image>(); item.AddComponent<RotationDiagramItem>(); return item; } /// <summary> /// 生成所有物体,根据图片精灵个数生成对应个数的物体,并设置好图片精灵,将RotationDiagramItem组件添加到集合中,将组件的Change方法注册到Action中 /// </summary> private void CreateItem() { GameObject template = CreateTemplate(); RotationDiagramItem itemTemp = null; foreach(Sprite sprite in ItemSprites) { itemTemp = Instantiate(template).GetComponent<RotationDiagramItem>(); itemTemp.SetParent(transform); itemTemp.SetSprite(sprite); itemTemp.AddMoveListener(Change); _items.Add(itemTemp); } Destroy(template); } /// <summary> /// 根据鼠标拖拽的方向得到相应的信号,并调用Change方法 /// </summary> /// <param name="offsetX"></param> 鼠标拖拽的方向 private void Change(float offsetX) { int symbol = offsetX >= 0 ? 1 : -1; Change(symbol); } /// <summary> /// 根据信号改变所有图片的id并更改物体的位置缩放等信息 /// </summary> /// <param name="symbol"></param> private void Change(int symbol) { foreach (RotationDiagramItem item in _items) { item.ChangeId(symbol, _items.Count); } for (int i = 0; i < _posData.Count; i++) { _items[i].SetPosData(_posData[_items[i].PosId]); } } /// <summary> /// 计算图片的位置缩放等信息 /// </summary> private void CalculateData() { List<ItemData> itemDatas = new List<ItemData>(); float length = (ItemSize.x + Offset) * _items.Count; float radioOffset = 1 / (float)_items.Count; float radio = 0; for(int i = 0;i < _items.Count;i++) { ItemData itemData = new ItemData(); itemData.PosId = i; itemDatas.Add(itemData); _items[i].PosId = i; ItemPosData data = new ItemPosData(); data.x = GetX(radio, length); data.scaleTimes = GetScaleTimes(radio, ScaleMax, ScaleMin); radio += radioOffset; _posData.Add(data); } itemDatas = itemDatas.OrderBy(u => _posData[u.PosId].scaleTimes).ToList(); for (int i = 0; i < itemDatas.Count; i++) { _posData[itemDatas[i].PosId].order = i; } } /// <summary> /// 设置图片的位置缩放等信息 /// </summary> private void SetItemData() { for (int i = 0; i < _posData.Count; i++) { _items[i].SetPosData(_posData[i]); } } /// <summary> /// 计算图片对应的横坐标 /// </summary> /// <param name="radio"></param> 代表图片在椭圆形圆圈上位置的数值,这个值在0-1之间 /// <param name="length"></param> 整个图片展示墙的长度 /// <returns></returns> private float GetX(float radio,float length) { if(radio > 1 || radio < 0) { Debug.LogError("当前比例必须是0-1的值"); return 0; } if(radio >= 0 && radio < 0.25f) { return length * radio; } else if(radio >= 0.25f && radio < 0.75f) { return length * (0.5f - radio); } else { return length * (radio - 1); } } /// <summary> /// 计算图片的缩放倍数 /// </summary> /// <param name="radio"></param> 图片在椭圆上的位置 /// <param name="max"></param> 图片的最大缩放比例 /// <param name="min"></param> 图片的最小缩放比例 /// <returns></returns> private float GetScaleTimes(float radio,float max,float min) { if (radio > 1 || radio < 0) { Debug.LogError("当前比例必须是0-1的值"); return 0; } if (radio < 0.5f) return Mathf.Lerp(max, min, radio); else return Mathf.Lerp(min, max, radio - 0.5f); } } /// <summary> /// 记录图片的位置、缩放和层级信息的类 /// </summary> public class ItemPosData { public float x; public float scaleTimes; public int order; } /// <summary> /// 记录图片次序和层级信息的结构体 /// </summary> public struct ItemData { public int PosId; public int OrderId; }
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; using DG.Tweening; public class RotationDiagramItem : MonoBehaviour, IDragHandler,IEndDragHandler { public int PosId; private float _offsetX; public Action<float> _moveAction; public float _aniTime = 1; private Image _image; private Image Image { get { if (_image == null) _image = GetComponent<Image>(); return _image; } } private RectTransform _rect; private RectTransform Rect { get { if (_rect == null) _rect = GetComponent<RectTransform>(); return _rect; } } /// <summary> /// 设置父物体 /// </summary> /// <param name="parent"></param> 父物体 public void SetParent(Transform parent) { transform.SetParent(parent); } /// <summary> /// 设置图片 /// </summary> /// <param name="sprite"></param> 图片精灵 public void SetSprite(Sprite sprite) { Image.sprite = sprite; } /// <summary> /// 设置位置和大小 /// </summary> /// <param name="data"></param> 保存图片位置和大小信息的对象 public void SetPosData(ItemPosData data) { Rect.DOAnchorPos(Vector2.right * data.x, _aniTime); //Rect.anchoredPosition3D = Vector2.right * data.x; Rect.DOScale(Vector3.one * data.scaleTimes, _aniTime); //Rect.localScale = Vector3.one * data.scaleTimes; StartCoroutine(Wait(data)); } /// <summary> /// 等待后设置图片层级 /// </summary> /// <param name="data"></param> 保存图片信息的对象 /// <returns></returns> private IEnumerator Wait(ItemPosData data) { yield return new WaitForSeconds(_aniTime * 0.5f); transform.SetSiblingIndex(data.order); } /// <summary> /// IDragHandler接口的实现方法 /// </summary> /// <param name="eventData"></param> public void OnDrag(PointerEventData eventData) { _offsetX += eventData.delta.x; } /// <summary> /// IEndDragHandler接口的实现方法 /// </summary> /// <param name="eventData"></param> public void OnEndDrag(PointerEventData eventData) { _moveAction(_offsetX); _offsetX = 0; } /// <summary> /// 添加监听器,监听鼠标的拖动 /// </summary> /// <param name="onMove"></param> public void AddMoveListener(Action<float> onMove) { _moveAction = onMove; } /// <summary> /// 根据信号改变图片的id /// </summary> /// <param name="symbol"></param> 代表图片拖动方向的信号值 /// <param name="totalItemNum"></param> 图片的总数 public void ChangeId(int symbol,int totalItemNum) { int id = PosId; id += symbol; if(id < 0) { id += totalItemNum; } PosId = id % totalItemNum; } }