3D效果如下
无3D效果如下
弯曲度调整效果如下:
缩放调整效果如下图:
渐变调整效果如下图:
Unity Demo 地址:https://download.csdn.net/download/qq_30259857/10893338
常见问题:
位置错误,位置偏移,位置不居中 : 在你的UIPanel 底下 重新创建 ScrollView 然后位置不动只动Size
参考来自:https://www.cnblogs.com/SHOR/p/5458538.html
使用和调用方法请看下面说明:
第一步:挂脚本
在ScrollView控件挂的脚本和大致参数如下图:
第二步:初始化
初始化:OnInit(OnRefreshCallBack, OnSelectCenterCallBack, OnLastSelectCenterCallBack)
第一个参数为所有项刷新回调函数,第二个参数为中间选中物体回调函数,第三个参数为上一个选中物体回调函数(主要用于初始化上一个选中物体)。所有回调函数的参数类型是GameObject, object,然后在回调函数把object转成数据类型即可,如下图
第三步:传入List 数据类型
刷新数据函数:OnRefresh<数据类型>(数据列表)
传入后回调 初始化函数(OnInit())当中传的第一个函数。
3D效果调整:
1. 需把UICamera调整为3D摄像机,关闭此界面的时候再调整为2D摄像机即可。
调整为3D摄像机 代码如下:
UICamera.currentCamera.orthographic = false;
UICamera.currentCamera.transform.localPosition = new Vector3(0, 0, -845);
UICamera.currentCamera.nearClipPlane = 0.1f;
2.脚本上把 isUsedRote 勾上
注:上图中 IsLoop 为首尾相连功能。
直接上整体代码:
/**
* @Author Leon Kim
*
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace projectQ
{
public class UIScrollAnimationList : MonoBehaviour
{
[Range(0, 2)]
public float curveValue = 0.8f; //弯曲度
[Range(0, 10)]
public float scaleValue = 1.5f; //渐变缩放
[Range(0, 10)]
public float alphaValue = 0; //渐变透明
public float offsetValue = 0; //每个物体的偏移值
public bool isLoop = false; //是否循环(首尾相连)
public int loopCount = 4; //多少个数量开始 首尾循环
public UIGrid grid;
public GameObject mItem;
public bool isUsedRote = false;
private Transform[] ItemsArr;
private Vector3[] PosArr;
private bool isInited = false;
private float itemWidth;
private float itemHeight;
private bool isDrag = false;
private int itemCount; //数量
private List DataList = new List();
private Dictionary ItemsDict = new Dictionary();
private UIScrollView.Movement movement; //方向
private GameObject content;
private GameObject lastOnClickItem; //上一次点击的物体
private Action OnCenterCallBack; //中间物体回调
private Action OnLastCenterCallBack; //上一次中间物体回调 (比如 上一个选中物体 初始化用)
private Action OnRefreshCallBack; //刷新物体
private UICenterOnChild CenterOnChild;
private Vector2 panelOffset;
private float panelWidth;
private float panelHeight;
private Vector2 initPos;
private bool isCanLoop = false;
public void OnInit(Action OnRefreshCallBack, Action OnCenterCallBack = null, Action OnLastCenterCallBack = null)
{
content = transform.GetChild(0).gameObject;
CenterOnChild = content.GetComponent();
if(CenterOnChild == null)
{
CenterOnChild = content.AddComponent();
}
CenterOnChild.onCenter = OnCenterClick;
CenterOnChild.onFinished = OnFinished;
this.GetComponent().onDragStarted = OnDragStart;
//this.GetComponent().onDragFinished = OnStopDrag;
panelOffset = this.GetComponent().clipOffset;
panelHeight = this.GetComponent().height;
panelWidth = this.GetComponent().width;
movement = this.GetComponent().movement;
this.OnCenterCallBack = OnCenterCallBack;
this.OnRefreshCallBack = OnRefreshCallBack;
this.OnLastCenterCallBack = OnLastCenterCallBack;
mItem = mItem == null ? transform.Find("Item").gameObject : mItem;
mItem.transform.SetParent(this.transform);
content.transform.DestroyChildren();
mItem.SetActive(false);
if(grid == null)
{
grid = transform.GetChild(0).GetComponent();
}
itemWidth = grid.cellWidth;
itemHeight = grid.cellHeight;
initPos = transform.localPosition;
}
public void OnRefresh(List list = null, int index = -1)
{
//仅刷新
if (list == null)
{
int listCount = DataList.Count;
for (int i = 0; i < listCount; i++)
{
OnRefreshCallBack(ItemsArr[i].gameObject, DataList[i]);
}
return;
}
lastOnClickItem = null;
isDrag = false;
DataList.Clear();
int count = list.Count;
for (int i = 0; i < count; i++)
{
DataList.Add(list[i] as object);
}
this.itemCount = count;
ItemsArr = new Transform[count];
PosArr = new Vector3[count];
int dataCount = DataList.Count;
int childCount = content.transform.childCount;
int forCount = dataCount > childCount ? dataCount : childCount;
for(int i = 0; i < forCount; i++)
{
GameObject obj = null;
if(i < childCount)
{
Transform trans = content.transform.GetChild(i);
obj = trans.gameObject;
if(i >= dataCount)
{
obj.SetActive(false);
continue;
}
}
else if(i < dataCount)
{
obj = Clone(i);
}
ItemsArr[i] = obj.transform;
obj.SetActive(true);
obj.name = i.ToString();
ItemsDict[obj] = DataList[i];
OnRefreshCallBack(obj, DataList[i]);
}
panelOffset = this.GetComponent().clipOffset;
float offsetX = panelOffset.x;
float offsetY = panelOffset.y;
//还原到初始位置
transform.localPosition = initPos;
if (movement == UIScrollView.Movement.Horizontal)
{
offsetX = -transform.localPosition.x;
}
else
{
offsetY = -transform.localPosition.y;
}
this.GetComponent().clipOffset = new Vector2(offsetX, offsetY);
grid.Reposition();
for (int i = 0; i < count; i++)
{
if (ItemsArr[i] != null)
{
PosArr[i] = ItemsArr[i].transform.localPosition;
}
}
if(index == -1 || index >= count)
{
index = Mathf.FloorToInt(count / 2f);
}
if(DataList.Count > 0)
{
JumpToCenter(index);
}
//是否可循环(首尾相连)
CheckCanLoop();
isInited = true;
}
//是否可循环(首尾相连)
private Transform minItem;
private Transform maxItem;
void CheckCanLoop()
{
OnUpdateScale();
float minScale = minItem.localScale.x;
float maxScale = maxItem.localScale.x;
isCanLoop = isLoop;
if(ItemsArr.Length < loopCount)
{
isCanLoop = false;
}
}
//跳转到某一个物体
public void JumpToCenter(int index)
{
CenterOnChild.CenterOn(ItemsArr[index].transform);
}
//跳转到某一个物体
public void JumpToCenter(GameObject target)
{
if (CenterOnChild == null) return;
CenterOnChild.CenterOn(target.transform);
}
//克隆物体
GameObject Clone(int i)
{
GameObject _clone = Instantiate(mItem) as GameObject;
_clone.transform.SetParent(grid.transform);
_clone.transform.localScale = Vector3.one;
_clone.SetActive(true);
_clone.name = i.ToString();
if(alphaValue > 0 && _clone.GetComponent() == null)
{
_clone.AddComponent();
}
return _clone;
}
//开始滑动回调
private void OnDragStart()
{
isDrag = true;
}
//滑动停止
void OnFinished()
{
isDrag = false;
}
//刷新列表滑动反向 相关参数
private float lastPosX = 0;
private float lastPosY = 0;
private bool isBigDirection = false; //是否往 坐标大的方向移动 (Horizontal时是 左方向, Vertical是 上方向)
//刷新列表滑动反向
void UpdateSliderMovement()
{
if (movement == UIScrollView.Movement.Horizontal)
{
if (lastPosX != transform.localPosition.x)
{
if (lastPosX < transform.localPosition.x)
{
isBigDirection = true;
}
else
{
isBigDirection = false;
}
lastPosX = transform.localPosition.x;
}
}
else
{
if (lastPosY != transform.localPosition.y)
{
if (lastPosY < transform.localPosition.y)
{
isBigDirection = true;
}
else
{
isBigDirection = false;
}
lastPosY = transform.localPosition.y;
}
}
}
private void Update()
{
if (!isInited) return;
//刷新缩放值和偏移值
OnUpdateScale();
//刷新滑动的方向
UpdateSliderMovement();
//首尾相连
LoopItem();
if(!isDrag)
{
//选中物体移动到中间位置
MoveToCenter();
}
}
//刷新缩放值,弯曲值,透明值
private void OnUpdateScale()
{
float tempMinPos = -1;
float tempMaxPos = -1;
float interval = isDrag ? 0 : 0.02f;
for (int i = 0; i < itemCount; i++)
{
Transform itemTrans = ItemsArr[i];
float alpha = 0;
if (movement == UIScrollView.Movement.Horizontal)
{
Vector3 Ipos = PosArr[i];
float val = (Ipos.x + transform.localPosition.x) / 1335f;
float factor = Mathf.Abs(val);
float yCurve = (factor + factor * curveValue) * (factor * curveValue * offsetValue); //曲线
alpha = 1 - factor * alphaValue;
float scale = 1 - factor * scaleValue;
if (scale < 0.1f)
{
scale = 0.1f;
}
if (scale > 0.98f)
{
scale = 1;
alpha = 1;
}
float v = GetValueX(val < 0 ? true : false, scale, Ipos.x);
if (v < 0.1f) v = 0;
float posX = val < 0 ? Ipos.x + v : Ipos.x - v;
Vector3 p = Vector3.one * scale;
if(Mathf.Abs(p.x - itemTrans.localScale.x) > interval)
{
itemTrans.localScale = p;
}
if (isUsedRote)
{
itemTrans.transform.rotation = Quaternion.Euler(0, val < 0 ? (90.0f * (1.0f - scale)) : -(90.0f * (1.0f - scale)), 0);
}
if(Mathf.Abs(itemTrans.localPosition.x - posX) > interval)
{
itemTrans.localPosition = new Vector2(posX, yCurve);
}
if (Ipos.x < tempMinPos || tempMinPos == -1)
{
tempMinPos = Ipos.x;
minPos = posX;
minPosIndex = i;
minItem = itemTrans;
}
if (Ipos.x > tempMaxPos || tempMaxPos == -1)
{
tempMaxPos = Ipos.x;
maxPos = posX;
maxPosIndex = i;
maxItem = itemTrans;
}
}
else
{
Vector3 Ipos = PosArr[i];
float val = (Ipos.y + transform.localPosition.y) / 750f;
float factor = Mathf.Abs(val);
float xCurve = (factor + factor * curveValue) * (factor * curveValue * offsetValue); //曲线
alpha = 1 - factor * alphaValue;
float scale = 1 - factor * scaleValue;
if (scale < 0.1f)
{
scale = 0.1f;
}
if (scale > 0.98f)
{
scale = 1;
}
float v = GetValueY(val < 0 ? true : false, scale, Ipos.y);
if (v < 0.1f) v = 0;
float posY = val < 0 ? Ipos.y + v : Ipos.y - v;
Vector3 p = Vector3.one * scale;
if (Mathf.Abs(p.x - itemTrans.localScale.x) > interval)
{
itemTrans.localScale = p;
}
if (isUsedRote)
{
itemTrans.transform.rotation = Quaternion.Euler(0, val < 0 ? (90.0f * (1.0f - scale)) : -(90.0f * (1.0f - scale)), 0);
}
if (Mathf.Abs(itemTrans.localPosition.y - posY) > interval)
{
itemTrans.localPosition = new Vector2(xCurve, posY);
}
if (Ipos.y < tempMinPos || tempMinPos == -1)
{
tempMinPos = Ipos.y;
minPos = posY;
minPosIndex = i;
minItem = itemTrans;
}
if (Ipos.y > tempMaxPos || tempMaxPos == -1)
{
tempMaxPos = Ipos.y;
maxPos = posY;
maxPosIndex = i;
maxItem = itemTrans;
}
}
if (alphaValue > 0)
{
itemTrans.GetComponent().alpha = alpha;
}
}
}
//首尾相连 相关参数
private int minPosIndex;
private int maxPosIndex;
private float minPos = 0;
private float maxPos = 0;
//首尾相连
void LoopItem()
{
if (!isCanLoop) return;
if (movement == UIScrollView.Movement.Horizontal)
{
float panelPosX = transform.localPosition.x;
if(isBigDirection)
{
if (Mathf.Abs(maxPos + panelPosX) > panelWidth / 2)
{
float posX = PosArr[minPosIndex].x - itemWidth;
PosArr[maxPosIndex] = new Vector2(posX, PosArr[maxPosIndex].y);
}
}
else
{
if (Mathf.Abs(minPos + panelPosX) > panelWidth / 2)
{
float posX = PosArr[maxPosIndex].x + itemWidth;
PosArr[minPosIndex] = new Vector2(posX, PosArr[minPosIndex].y);
}
}
}
else
{
float panelPosY = transform.localPosition.y;
if(isBigDirection)
{
if (Mathf.Abs(maxPos + panelPosY) > panelHeight / 2)
{
float posY = PosArr[minPosIndex].y - itemHeight;
PosArr[maxPosIndex] = new Vector2(PosArr[maxPosIndex].x, posY);
}
}
else
{
if (Mathf.Abs(minPos + panelPosY) > panelHeight / 2)
{
float posY = PosArr[maxPosIndex].y + itemHeight;
PosArr[minPosIndex] = new Vector2(PosArr[minPosIndex].x, posY);
}
}
}
}
//物体 lerp 移动到中间位置
void MoveToCenter()
{
if (lastOnClickItem != null)
{
float posX = this.transform.localPosition.x;
float posY = this.transform.localPosition.y;
float value = 0;
float panelOffsetX = panelOffset.x;
float panelOffsetY = panelOffset.y;
if (movement == UIScrollView.Movement.Horizontal)
{
value = lastOnClickItem.transform.localPosition.x + posX;
posX -= value;
}
else
{
value = lastOnClickItem.transform.localPosition.y + posY;
posY -= value;
}
if (Mathf.Abs(value) > 0.2f)
{
LoopItem();//首尾相连
Vector2 pos = Vector2.Lerp(transform.localPosition, new Vector2(posX, posY), Time.deltaTime * 5);
transform.localPosition = pos;
if (movement == UIScrollView.Movement.Horizontal)
panelOffsetX = -transform.localPosition.x;
else
panelOffsetY = -transform.localPosition.y;
this.GetComponent().clipOffset = new Vector2(panelOffsetX, panelOffsetY);
}
}
}
//中间放大的物体 回调
void OnCenterClick(GameObject centerObj)
{
if (centerObj == null || (lastOnClickItem != null && lastOnClickItem == centerObj))
return;
//上一个选中物体回调
if (lastOnClickItem != null && ItemsDict.ContainsKey(lastOnClickItem) && OnLastCenterCallBack != null)
{
OnLastCenterCallBack(lastOnClickItem, ItemsDict[lastOnClickItem]);
}
lastOnClickItem = centerObj;
if (ItemsDict.ContainsKey(centerObj) && OnCenterCallBack != null)
{
OnCenterCallBack(centerObj, ItemsDict[centerObj]);
}
isDrag = false;
}
//返回当前物体坐标 需要增加或减的值
float GetValueX(bool isLeft, float curScale, float posX)
{
float reValue = 0;
for (int i = 0; i < itemCount; i++)
{
Vector3 Ipos = PosArr[i];
float val = (Ipos.x + transform.localPosition.x) / 1335f;
if (isLeft)
{
if (val >= 0 || Ipos.x <= posX)
{
continue;
}
}
else
{
if (val <= 0 || Ipos.x >= posX)
{
continue;
}
}
float factor = Mathf.Abs(val);
float scale = 1 - factor * scaleValue;
if (scale < 0.1f)
{
scale = 0.1f;
}
if (scale > 0.98f)
{
scale = 1;
}
if (scale >= curScale && scale != 1)
{
reValue += (1 - scale) * itemWidth;
}
}
reValue += (1 - curScale) / 2 * itemWidth;
return reValue;
}
//返回当前物体坐标 需要增加或减的值
float GetValueY(bool isDown, float curScale, float posY)
{
float reValue = 0;
for (int i = 0; i < itemCount; i++)
{
Vector3 Ipos = PosArr[i];
float val = (Ipos.y + transform.localPosition.y) / 750f;
if (isDown)
{
if (val >= 0 || Ipos.y <= posY)
{
continue;
}
}
else
{
if (val <= 0 || Ipos.y >= posY)
{
continue;
}
}
float factor = Mathf.Abs(val);
float scale = 1 - factor * scaleValue;
if (scale < 0.1f)
{
scale = 0.1f;
}
if (scale > 0.98f)
{
scale = 1;
}
if (scale >= curScale && scale != 1)
{
reValue += (1 - scale) * itemHeight;
}
}
reValue += (1 - curScale) / 2 * itemHeight;
return reValue;
}
}
/* 暂时无用
//获取离中间位置最近的物体
private Transform GetCenterMinPosX()
{
Transform item = ItemsArr[0];
float min = Mathf.Abs(ItemsArr[0].localPosition.x + transform.localPosition.x);
for (int i = 0; i < itemCount; i++)
{
Vector3 Ipos = ItemsArr[i].localPosition;
float factor = Mathf.Abs(Ipos.x + transform.localPosition.x);
if (factor < min)
{
min = factor;
item = ItemsArr[i];
}
}
return item;
}
private Transform GetCenterMinPosY()
{
Transform item = ItemsArr[0];
float min = Mathf.Abs(ItemsArr[0].localPosition.y + transform.localPosition.y);
for (int i = 0; i < itemCount; i++)
{
Vector3 Ipos = ItemsArr[i].localPosition;
float factor = Mathf.Abs(Ipos.y + transform.localPosition.y);
if (factor < min)
{
min = factor;
item = ItemsArr[i];
}
}
return item;
}
*/
}