图片轮播展示效果-2D实现
阅读原文时间:2023年07月11日阅读:1

图片的轮播展示效果如果使用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);  
}

}

///

/// 记录图片的位置、缩放和层级信息的类 ///
public class ItemPosData
{
public float x;
public float scaleTimes;
public int order;
}

///

/// 记录图片次序和层级信息的结构体 ///
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 _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;  
}  

}