效果
转动轮播图效果
代码
using DG.Tweening;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class RotaryFigure2D : MonoBehaviour,IDragHandler,IEndDragHandler
{
public GameObject prefab;
public int num = 5;
public float spacing = 1;//间距
public float scaleMax = 1;//缩放最大值
public float scaleMin = 0.5f;//缩放最小值
Rect rect;//预制体的属性
float L;//周长
float r;//半径
float ang;//每个角弧度
float dragDistance = 0;//拖拽距离
List<GameObject> temps = new List<GameObject>();
List<Transform> sorts = new List<Transform>();
private void Start()
{
rect = prefab.GetComponent<RectTransform>().rect;
//计算周长
L = (rect.width + spacing) * num;
//计算半径
r = L / (2 * Mathf.PI);
//计算每个角的弧度
ang = 2 * Mathf.PI / num;
Move();
}
public void Move()
{
//计算拖动的弧度(周长与拖拽距离的比值乘2PI)
float dragAng = (dragDistance / L) * (2 * Mathf.PI);
for (int i = 0; i < num; i++)
{
if(temps.Count<=i)
{
GameObject temp = Instantiate(prefab, transform);
temp.name = i.ToString();
temp.GetComponent<Image>().color = Random.ColorHSV();
temps.Add(temp);
sorts.Add(temp.transform);
}
//计算位置
float x = Mathf.Sin(ang * i+ dragAng) * r;
float z= Mathf.Cos(ang * i+ dragAng) * r;
//z+r求出此元素相对于整个直径的位置,再除以直径,获取它相对于这个直径的位置作为判断远近的根据
//由于应该越小的数离得越近,根据近大远小,用1减去该值来取反
//用该比值乘最大值与最小值之间的区间值,来算出实际缩放区间
//用算出的实际缩放区间加上缩放最小值来算出实际缩放
float scale=(1-(z+r)/ (2 * r)) * (scaleMax - scaleMin) + scaleMin;
temps[i].transform.localPosition = new Vector3(x, 0, 0);
temps[i].transform.localScale = new Vector3(scale, scale, scale);
}
//根据缩放排序
sorts.Sort((a, b) =>
{
return a.localScale.x.CompareTo(b.localScale.x);
});
//根据缩放修改渲染层级
for (int i = 0; i < sorts.Count; i++)
{
sorts[i].SetSiblingIndex(i);
}
}
public void OnDrag(PointerEventData eventData)
{
dragDistance -= eventData.delta.x;
Move();
}
public void OnEndDrag(PointerEventData eventData)
{
//惯性
//根据停止拖动时的速度,到速度归零前,使其继续转动一小段时间
DOTween.To((float a) =>
{
dragDistance -= a;
Move();
}, eventData.delta.x, 0, Mathf.Abs(eventData.delta.x / 100)).OnComplete(() =>//惯性结束后调用
{
//指定转到最大的id
MoveTo(int.Parse(sorts[sorts.Count - 1].name));
});
}
//对齐(指定位置)
public void MoveTo(int n)
{
//计算正常移动个数
int a = int.Parse(sorts[sorts.Count - 1].name) - n;
//计算反向移动个数
int b = num - Mathf.Abs(a);
//找到正反两个方向中个数少的方向
int c = Mathf.Abs(a) < b ? Mathf.Abs(a) : b;
//判断正常移动时是顺时针还是逆时针,a是顺时针b就是逆时针,a是逆时针b就是顺时针
if ((a < 0 && c == Mathf.Abs(a)) || (a > 0 && c == b))
{
c = -c;
}
//计算指定位置和最大的之间的弧度
float cutAng = c* ang;
//对齐最大的
//通过最大的坐标求弧度
float moveAng = Mathf.Asin(sorts[sorts.Count - 1].localPosition.x / r);
//把差值弧度加到对齐最大弧度上,算出需要移动的弧度
moveAng += cutAng;
//因为咱们使用距离转动,所以要把弧度转换成移动距离
//移动的距离 = 移动的弧度与2PI的比值乘周长
float moveDistance = moveAng / (2 * Mathf.PI) * L;
//让它自己缓慢转动过去
DOTween.To((float a) =>
{
dragDistance = a;
Move();
}, dragDistance, dragDistance + moveDistance, Mathf.Abs(moveDistance / 100)).OnComplete(() =>
{
//在这获取最终选择的数据
sorts[sorts.Count - 1].GetComponent<Image>();
});
}
}
操作
创建一个Image,透明度调为0,做一个Image预制体,放入代码里
思路
1、移动效果
封装一个移动方法
通过需要移动的距离计算出需要移动的弧度
遍历每个元素,通过Sin,Cos计算出元素当前弧度应该处于的位置
根据算出的z轴值决定元素大小,近大远小
改变元素的位置和大小
把元素在一个集合里,进行从小到大排序,并按顺序设置层级,越大层级越大
2、拖动效果
在拖动接口中,根据拖动距离计算需要移动的距离,再通过实时调用移动方法实现转动效果
3、惯性效果
在拖动结束接口中,使用DOTween插件里的To方法,调用移动方法,实现惯性效果
在惯性结束后,可调用对齐方法,实现居中效果
4、对齐指定元素效果
根据需要对齐的元素编号,和目前处于最前方元素编号进行计算得出顺时针和逆时针需要移动的个数,比较出最少移动的个数
用最少移动的个数算出对齐元素与最前方元素的差值弧度
计算出最前方的弧度,与插值弧度相加,算出需要移动的距离
使用DOTween插件的To方法使其慢慢转动到指定位置
在对齐完成后可获取它的一些数据,进行其他操作