213 lines
7.5 KiB
C#
213 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Pool;
|
|
using UnityEngine.UI;
|
|
using XericLibrary.Runtime.MacroLibrary;
|
|
|
|
#if UNITY_EDITOR
|
|
using XericLibraryEditor.Debug;
|
|
#endif
|
|
#if ODIN_INSPECTOR
|
|
using Sirenix.OdinInspector;
|
|
#endif
|
|
|
|
namespace XericUI.OverrideLayout
|
|
{
|
|
[AddComponentMenu("Xeric UI Vessel/UI/Xeric Scroll Rect", 50)]
|
|
[SelectionBase]
|
|
[ExecuteAlways]
|
|
[DisallowMultipleComponent]
|
|
[RequireComponent(typeof(RectTransform))]
|
|
public class XericScrollRect : ScrollRect
|
|
{
|
|
#region 事件委托
|
|
|
|
// public Action
|
|
|
|
#endregion
|
|
|
|
#region 字段属性
|
|
|
|
/// <summary>
|
|
/// 启用自动剔除功能
|
|
/// </summary>
|
|
public bool enableAutoActiveByMaskVisible = true;
|
|
|
|
/// <summary>
|
|
/// 容器布局
|
|
/// </summary>
|
|
public LayoutGroup layoutGroup;
|
|
|
|
/// <summary>
|
|
/// 容器预加载像素范围
|
|
/// </summary>
|
|
public float preloadMargin = 200f;
|
|
|
|
/// <summary>
|
|
/// 容器加载响应可被察觉的变化长度
|
|
/// </summary>
|
|
public float discernibleDistance = 10f;
|
|
|
|
/// <summary>
|
|
/// 自动剔除更新速率
|
|
/// </summary>
|
|
[Tooltip("当设定满速率时,实际上是期望每帧执行更新")]
|
|
// ReSharper disable once InconsistentNaming
|
|
[Range(0.02f, 1)]
|
|
public float AAMVUpdateRate = .9f;
|
|
|
|
#endregion
|
|
|
|
#region 布局参考
|
|
|
|
private float _aamvUpdateTime;
|
|
|
|
private Vector2 _lastContextSize;
|
|
private Vector2 _lastContextAnchoredPosition;
|
|
|
|
// 脏标记处理率,每次激活脏标记时给予多份权重,每次空处理则返还一部分权重,如果出现标记大于0的情况,说明脏标记处理次数太多
|
|
private float _dirtyTakeRate;
|
|
|
|
private HashSet<Transform> _lastActiveSet;
|
|
|
|
#endregion
|
|
|
|
#region 生命周期
|
|
|
|
#if UNITY_EDITOR
|
|
private Vector3[] vertex1 = new Vector3[4];
|
|
private Vector3[] vertex2 = new Vector3[4];
|
|
private Vector3[] vertex3 = new Vector3[4];
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
var lossyScale = transform.lossyScale;
|
|
var scale = lossyScale * preloadMargin;
|
|
var dis2 = lossyScale * discernibleDistance;
|
|
|
|
content.GetWorldCorners(vertex1);
|
|
viewport.GetWorldCorners(vertex2);
|
|
var rect = new Rect(vertex2[0], new Vector2(vertex2[2].x - vertex2[0].x, vertex2[2].y - vertex2[0].y));
|
|
var viewportMin = rect.min * lossyScale;
|
|
var viewportMax = rect.max * lossyScale;
|
|
// vertex3[0] = new Vector3(vertex2[0].x - scale.x, vertex2[0].y - scale.y, vertex2[0].z);
|
|
// vertex3[1] = new Vector3(vertex2[1].x - scale.x, vertex2[1].y + scale.y, vertex2[1].z);
|
|
// vertex3[2] = new Vector3(vertex2[2].x + scale.x, vertex2[2].y + scale.y, vertex2[2].z);
|
|
// vertex3[3] = new Vector3(vertex2[3].x + scale.x, vertex2[3].y - scale.y, vertex2[3].z);
|
|
|
|
vertex3[0] = new Vector3(rect.min.x - scale.x, rect.min.y - scale.y, vertex2[0].z);
|
|
vertex3[1] = new Vector3(rect.min.x - scale.x, rect.max.y + scale.y, vertex2[1].z);
|
|
vertex3[2] = new Vector3(rect.max.x + scale.x, rect.max.y + scale.y, vertex2[2].z);
|
|
vertex3[3] = new Vector3(rect.max.x + scale.x, rect.min.y - scale.y, vertex2[3].z);
|
|
|
|
Gizmos.color = Color.yellow;
|
|
MacroGizmosDraw.DrawGizmosRectLinkLine(vertex1, vertex3, Color.grey, Color.cyan, true);
|
|
}
|
|
#endif
|
|
|
|
protected override void Awake()
|
|
{
|
|
base.Awake();
|
|
|
|
// 布局
|
|
if (layoutGroup == null)
|
|
layoutGroup = transform.GetComponentInParent<LayoutGroup>();
|
|
if (layoutGroup is XericHorizontalOrVerticalLayoutGroup xericLayoutGroup)
|
|
{
|
|
xericLayoutGroup.layoutIgnoreInactive = false;
|
|
}
|
|
}
|
|
|
|
protected override void LateUpdate()
|
|
{
|
|
if (!enableAutoActiveByMaskVisible)
|
|
{
|
|
base.LateUpdate();
|
|
return;
|
|
}
|
|
|
|
// 计算更新速率
|
|
var targetFrameRate = Application.targetFrameRate;
|
|
var frameAamvRate = Time.deltaTime / Mathf.Max(0.02f, AAMVUpdateRate);
|
|
_dirtyTakeRate = Mathf.Max(_dirtyTakeRate - AAMVUpdateRate, 0);
|
|
if (AAMVUpdateRate < 1)
|
|
{
|
|
if (_aamvUpdateTime < frameAamvRate)
|
|
{
|
|
_aamvUpdateTime += Time.deltaTime;
|
|
base.LateUpdate();
|
|
return;
|
|
}
|
|
_aamvUpdateTime %= frameAamvRate;
|
|
}
|
|
|
|
var sqrDistance = MacroMath.SqrMagnitudeDistance(content.anchoredPosition, _lastContextAnchoredPosition);
|
|
if (sqrDistance > discernibleDistance * discernibleDistance)
|
|
{
|
|
_lastContextAnchoredPosition = content.anchoredPosition;
|
|
SetAAMVDirty();
|
|
}
|
|
|
|
base.LateUpdate();
|
|
}
|
|
|
|
private Vector3[] viewportVertex = new Vector3[4];
|
|
/// <summary>
|
|
/// 自动剔除更新标记
|
|
/// </summary>
|
|
protected void SetAAMVDirty()
|
|
{
|
|
_dirtyTakeRate += AAMVUpdateRate * 2;
|
|
|
|
var nowActiveChildrenSet = HashSetPool<Transform>.Get();
|
|
|
|
// 将视口尺寸转换到content本地空间
|
|
// 手动计算视口在content空间的矩形
|
|
viewport.GetWorldCorners(viewportVertex);
|
|
var viewportRect = new Rect(
|
|
content.InverseTransformPoint(viewportVertex[0]),
|
|
content.InverseTransformPoint(viewportVertex[2]) - content.InverseTransformPoint(viewportVertex[0]));
|
|
var contentRect = content.rect;
|
|
|
|
// 计算预加载边距(考虑content缩放)
|
|
var scaledMargin = preloadMargin / ((content.lossyScale.x + content.lossyScale.y) * 0.5f);
|
|
|
|
// 扩展可见区域边界
|
|
var visibleMin = viewportRect.min - new Vector2(preloadMargin, preloadMargin);
|
|
var visibleMax = viewportRect.max + new Vector2(preloadMargin, preloadMargin);
|
|
|
|
// 获取当前滚动归一化位置
|
|
var scrollPos = new Vector2(
|
|
horizontal ? horizontalScrollbar?.value ?? 0 : 0,
|
|
vertical ? 1 - (verticalScrollbar?.value ?? 1) : 0);
|
|
|
|
var isinit = _lastActiveSet == null;
|
|
|
|
foreach (var child in content.GetChildren())
|
|
{
|
|
var rectTransform = child.RectTransform();
|
|
// 直接使用anchoredPosition进行边界检测
|
|
var childPos = rectTransform.anchoredPosition;
|
|
var isVisible =
|
|
childPos.x >= visibleMin.x &&
|
|
childPos.x <= visibleMax.x &&
|
|
childPos.y >= visibleMin.y &&
|
|
childPos.y <= visibleMax.y;
|
|
var isActive = isinit || _lastActiveSet.Contains(child);
|
|
|
|
if (isVisible)
|
|
nowActiveChildrenSet.Add(child);
|
|
if (isVisible == isActive)
|
|
continue;
|
|
|
|
child.gameObject.SetActive(isVisible);
|
|
}
|
|
|
|
if (!isinit)
|
|
HashSetPool<Transform>.Release(_lastActiveSet);
|
|
_lastActiveSet = nowActiveChildrenSet;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |