UGUI各种优化效果
本文所实现的UGUI效果需求如下:
- 支持缩放滑动效果
- 支持动态缩放循环加载
- 支持大数据固定Item复用加载
- 支持不用Mask遮罩无限循环加载
- 支持ObjectPool动态加载
- 支持无限不规则子物体动态加载
- 支持拖动并点击和拖拽
- 支持拖动并拖拽
- 支持ScrollRect拖动自动吸附功能(拖动是否超过一半自动进退)
前言
要实现以上效果,我从网上搜索得到部分解决方案链接,但不是完全满足想要的效果,就自己继续改造优化和添加想要的效果,本文最后会附带上完整Demo下载链接。
效果图
缩放滑动效果
缩放循环展示卡牌效果
- 大量数据无卡顿动态加载,并且支持拖拽、点击和吸附功能
大量数据循固定Item复用
无限无遮罩动态加载
- 不规则子物体动态循环加载
部分核心代码
- 有遮罩无卡顿加载
思路:并没有使用UGUI的ScrollRect组件,摆放几张卡片,通过移动和缩放来实现
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class EnhancelScrollView : MonoBehaviour
{
// 缩放曲线
public AnimationCurve scaleCurve;
// 位移曲线
public AnimationCurve positionCurve;
// 位移系数
public float posCurveFactor = 500.0f;
// y轴坐标固定值(所有的item的y坐标一致)
public float yPositionValue = 46.0f;
// 添加到EnhanceScrollView的目标对象
public List scrollViewItems;
// 目标对象Widget脚本,用于depth排序
private List imageTargets;
// 当前处于中间的item
private EnhanceItem centerItem;
private EnhanceItem preCenterItem;
// 当前出移动中,不能进行点击切换
private bool canChangeItem = true;
// 计算差值系数
public float dFactor = 0.2f;
// 点击目标移动的横向目标值
private float[] moveHorizontalValues;
// 对象之间的差值数组(根据差值系数算出)
private float[] dHorizontalValues;
// 横向变量值
public float horizontalValue = 0.0f;
// 目标值
public float horizontalTargetValue = 0.1f;
// 移动动画参数
private float originHorizontalValue = 0.1f;
public float duration = 0.2f;
private float currentDuration = 0.0f;
private static EnhancelScrollView instance;
public static EnhancelScrollView GetInstance()
{
return instance;
}
void Awake()
{
instance = this;
}
void Start()
{
if((scrollViewItems.Count % 2) == 0)
{
Debug.LogError("item count is invaild,please set odd count! just support odd count.");
}
if(moveHorizontalValues == null)
moveHorizontalValues = new float[scrollViewItems.Count];
if(dHorizontalValues == null)
dHorizontalValues = new float[scrollViewItems.Count];
if (imageTargets == null)
imageTargets = new List();
int centerIndex = scrollViewItems.Count / 2;
for (int i = 0; i < scrollViewItems.Count;i++ )
{
scrollViewItems[i].scrollViewItemIndex = i;
Image tempImage = scrollViewItems[i].gameObject.GetComponent();
imageTargets.Add(tempImage);
dHorizontalValues[i] = dFactor * (centerIndex - i);
dHorizontalValues[centerIndex] = 0.0f;
moveHorizontalValues[i] = 0.5f - dHorizontalValues[i];
scrollViewItems[i].SetSelectColor(false);
}
centerItem = scrollViewItems[centerIndex];
canChangeItem = true;
}
public void UpdateEnhanceScrollView(float fValue)
{
for (int i = 0; i < scrollViewItems.Count; i++)
{
EnhanceItem itemScript = scrollViewItems[i];
float xValue = GetXPosValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]);
float scaleValue = GetScaleValue(fValue, dHorizontalValues[itemScript.scrollViewItemIndex]);
itemScript.UpdateScrollViewItems(xValue, yPositionValue, scaleValue);
}
}
void Update()
{
currentDuration += Time.deltaTime;
if (currentDuration > duration)
{
// 更新完毕设置选中item的对象即可
currentDuration = duration;
if (centerItem != null)
centerItem.SetSelectColor(true);
if (preCenterItem != null)
preCenterItem.SetSelectColor(false);
canChangeItem = true;
}
SortDepth();
float percent = currentDuration / duration;
horizontalValue = Mathf.Lerp(originHorizontalValue, horizontalTargetValue, percent);
UpdateEnhanceScrollView(horizontalValue);
}
///
/// 缩放曲线模拟当前缩放值
///
private float GetScaleValue(float sliderValue, float added)
{
float scaleValue = scaleCurve.Evaluate(sliderValue + added);
return scaleValue;
}
///
/// 位置曲线模拟当前x轴位置
///
private float GetXPosValue(float sliderValue, float added)
{
float evaluateValue = positionCurve.Evaluate(sliderValue + added) * posCurveFactor;
return evaluateValue;
}
public void SortDepth()
{
imageTargets.Sort(new CompareDepthMethod());
for (int i = 0; i < imageTargets.Count; i++)
imageTargets[i].transform.SetSiblingIndex(i);
}
///
/// 用于层级对比接口
///
public class CompareDepthMethod : IComparer
{
public int Compare(Image left, Image right)
{
if (left.transform.localScale.x > right.transform.localScale.x)
return 1;
else if (left.transform.localScale.x < right.transform.localScale.x)
return -1;
else
return 0;
}
}
///
/// 获得当前要移动到中心的Item需要移动的factor间隔数
///
private int GetMoveCurveFactorCount(float targetXPos)
{
int centerIndex = scrollViewItems.Count / 2;
for (int i = 0; i < scrollViewItems.Count;i++ )
{
float factor = (0.5f - dFactor * (centerIndex - i));
float tempPosX = positionCurve.Evaluate(factor) * posCurveFactor;
if (Mathf.Abs(targetXPos - tempPosX) < 0.01f)
return Mathf.Abs(i - centerIndex);
}
return -1;
}
///
/// 设置横向轴参数,根据缩放曲线和位移曲线更新缩放和位置
///
public void SetHorizontalTargetItemIndex(int itemIndex)
{
if (!canChangeItem)
return;
EnhanceItem item = scrollViewItems[itemIndex];
if (centerItem == item)
return;
canChangeItem = false;
preCenterItem = centerItem;
centerItem = item;
// 判断点击的是左侧还是右侧计算ScrollView中心需要移动的value
float centerXValue = positionCurve.Evaluate(0.5f) * posCurveFactor;
bool isRight = false;
if (item.transform.localPosition.x > centerXValue)
isRight = true;
// 差值,计算横向值
int moveIndexCount = GetMoveCurveFactorCount(item.transform.localPosition.x);
if (moveIndexCount == -1)
{
moveIndexCount = 1;
}
float dvalue = 0.0f;
if (isRight)
dvalue = -dFactor * moveIndexCount;
else
dvalue = dFactor * moveIndexCount;
// 更改target数值,平滑移动
horizontalTargetValue += dvalue;
currentDuration = 0.0f;
originHorizontalValue = horizontalValue;
}
///
/// 向右选择角色按钮
///
public void OnBtnRightClick()
{
if (!canChangeItem)
return;
int targetIndex = centerItem.scrollViewItemIndex + 1;
if (targetIndex > scrollViewItems.Count - 1)
targetIndex = 0;
SetHorizontalTargetItemIndex(targetIndex);
}
///
/// 向左选择按钮
///
public void OnBtnLeftClick()
{
if (!canChangeItem)
return;
int targetIndex = centerItem.scrollViewItemIndex - 1;
if (targetIndex < 0)
targetIndex = scrollViewItems.Count - 1;
SetHorizontalTargetItemIndex(targetIndex);
}
}
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class EnhanceItem : MonoBehaviour {
// 在ScrollViewitem中的索引
// 定位当前的位置和缩放
public int scrollViewItemIndex = 0;
public bool inRightArea = false;
private Vector3 targetPos = Vector3.one;
private Vector3 targetScale = Vector3.one;
private Transform mTrs;
private Image mImage;
void Awake()
{
mTrs = this.transform;
mImage = this.GetComponent();
}
void Start()
{
this.gameObject.GetComponent