如下图示例,是一个选择日期、时间的日历控件,右侧小时与分钟的部分是两个Scroll Rect
滚动区域组件,滚动到中间高亮部分表示选择,那么如何让滚动停止在合适的位置?避免出现如图所示的停在两个数据项中间的情况。
Calendar
空白占位
这样目的是为了首尾的数据项可以滚动到中间高亮的区域。
ScrollRect
中Content
的Pivot
轴心点设在上方(0.5,1)Pivot(0.5,1)
这样可以根据数据项的AnchorPosition
的y值来设置Content的AnchorPosition
的y值,以便让Content到指定的位置。
AnchorPosition
的高度//找到第一个数据项(0时)
var first = Array.Find(hourLayoutGroup.GetComponentsInChildren<MobileCalendarDataItem>(), m => m.Value == 0);
//起始点(0时的Y值)
float startY = Mathf.Abs((first.transform as RectTransform).anchoredPosition.y);
AnchorPosition
y值的取值范围RectTransform prt = hourLayoutGroup.transform as RectTransform;
//目标点
float targetY = Mathf.Abs(prt.anchoredPosition.y);
//数据项的Y间隔
float deltaY = (hourPrefab.transform as RectTransform).rect.height + hourLayoutGroup.spacing;
//钳制取值范围
targetY = Mathf.Clamp(targetY, startY, startY + 23 * deltaY);
AnchorPosition
y值 - 起始点y值)/ 数据项的y间隔 取整数据项y间隔 = 数据项的高度 + VerticalLayoutGroup
的Spacing
目标AnchorPosition
y值 = 目标数据值 * 数据项y间隔 + 起始点高度
//目标小时
int targetV = Mathf.RoundToInt((targetY - startY) / deltaY);
//最终目标点
targetY = startY + targetV * deltaY;
//DoTween动画滑动至目标点
prt.DOAnchorPosY(targetY, .2f);
Delta Y
最终效果
代码示意:
private void Update()
{
if (hourScrollRect != null)
{
//停止滚动
if (Mathf.Approximately(Mathf.Abs(hourScrollRect.velocity.y), 0f))
{
if (!hourScrollStop)
{
hourScrollStop = true;
//找到第一个数据项(0时)
var first = Array.Find(hourLayoutGroup.GetComponentsInChildren<MobileCalendarDataItem>(), m => m.Value == 0);
//起始点(0时的Y值)
float startY = Mathf.Abs((first.transform as RectTransform).anchoredPosition.y);
RectTransform prt = hourLayoutGroup.transform as RectTransform;
//目标点
float targetY = Mathf.Abs(prt.anchoredPosition.y);
//数据项的Y间隔
float deltaY = (hourPrefab.transform as RectTransform).rect.height + hourLayoutGroup.spacing;
//钳制取值范围
targetY = Mathf.Clamp(targetY, startY, startY + 23 * deltaY);
//目标小时
int targetV = Mathf.RoundToInt((targetY - startY) / deltaY);
//最终目标点
targetY = startY + targetV * deltaY;
//DoTween动画滑动至目标点
prt.DOAnchorPosY(targetY, .2f);
//记录当前小时
currentHour = targetV;
apmText.text = currentHour < 12 ? "AM" : "PM";
}
}
else
{
hourScrollStop = false;
}
}
if (minuteScrollRect != null)
{
//停止滚动
if (Mathf.Approximately(Mathf.Abs(minuteScrollRect.velocity.y), 0f))
{
if (!minuteScrollStop)
{
minuteScrollStop = true;
//找到第一个数据项(0分)
var first = Array.Find(minuteLayoutGroup.GetComponentsInChildren<MobileCalendarDataItem>(), m => m.Value == 0);
//起始点(0分的Y值)
float startY = Mathf.Abs((first.transform as RectTransform).anchoredPosition.y);
RectTransform prt = minuteLayoutGroup.transform as RectTransform;
//目标点
float targetY = Mathf.Abs(prt.anchoredPosition.y);
//数据项的Y间隔
float deltaY = (minutePrefab.transform as RectTransform).rect.height + minuteLayoutGroup.spacing;
//钳制取值范围
targetY = Mathf.Clamp(targetY, startY, startY + 59 * deltaY);
//目标小时
int targetV = Mathf.RoundToInt((targetY - startY) / deltaY);
//最终目标点
targetY = startY + targetV * deltaY;
//DoTween动画滑动至目标点
prt.DOAnchorPosY(targetY, .2f);
//记录当前分钟
currentMinute = targetV;
}
}
else
{
minuteScrollStop = false;
}
}
}