killapp/Assets/Scripts/UI/Pages/HomePage/HomePageDeviceItem.cs

177 lines
6.0 KiB
C#
Raw Normal View History

using DG.Tweening;
using Kill.Managers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Kill.UI.Pages
{
/// <summary>
/// 首页设备列表项 - 显示单个设备信息
/// 支持左滑手势:左滑时菜单向左滑出并显示高斯模糊覆盖层,
/// 滑动超过菜单宽度1/3松手则完整展开否则缩回并隐藏模糊层
/// </summary>
public class HomePageDeviceItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public Text deviceNameText;
public GameObject selectedIndicator;
/// <summary>菜单子物体锚点位于item最右侧左滑时向左滑出</summary>
public RectTransform menuRect;
/// <summary>高斯模糊覆盖层(子物体,覆盖其他内容),菜单出现时显示,缩回时隐藏</summary>
public GameObject blurOverlay;
// 当前设备信息
public DeviceInfo deviceInfo;
/// <summary>当前展开菜单的item全局仅允许一个菜单展开</summary>
private static HomePageDeviceItem openItem;
private float menuWidth;
private float currentOffset;
private float dragStartOffset;
private bool isMenuOpen;
private bool isDragging;
private Vector3 menuInitPos;
private Tweener tweener;
private Button button;
void Awake()
{
button = GetComponent<Button>();
InitSwipe();
}
void OnDisable()
{
// 防止拖拽中被禁用且未恢复的情况
if (button != null) button.interactable = true;
}
void OnDestroy()
{
// 销毁时释放全局展开引用
if (openItem == this)
openItem = null;
}
public void InitDeviceItem(DeviceInfo device)
{
deviceInfo = device;
deviceNameText.text = device.device_name;
selectedIndicator.SetActive(false);
InitSwipe();
}
public void SetSelectedState(bool isSelected)
{
// 设置选中状态
selectedIndicator.SetActive(isSelected);
}
/// <summary>
/// 初始化滑动数据:记录菜单初始位置、菜单宽度,重置为收起状态
/// </summary>
private void InitSwipe()
{
tweener?.Kill();
if (menuRect == null) return;
menuWidth = menuRect.sizeDelta.x;
menuInitPos = menuRect.anchoredPosition3D;
isMenuOpen = false;
currentOffset = 0;
ApplyMenuOffset(0);
if (blurOverlay != null) blurOverlay.SetActive(false);
}
public void OnBeginDrag(PointerEventData eventData)
{
if (menuRect == null || menuWidth <= 0) return;
// 拖拽滑动时禁用按钮点击,避免与菜单滑动同时触发
if (button != null) button.interactable = false;
// 其他item的菜单展开时先收起保证全局只有一个菜单
if (openItem != null && openItem != this)
openItem.CloseMenu();
isDragging = true;
// 菜单开始出现,显示高斯模糊覆盖层
if (blurOverlay != null) blurOverlay.SetActive(true);
currentOffset = isMenuOpen ? -menuWidth : 0;
dragStartOffset = currentOffset;
ApplyMenuOffset(currentOffset);
}
public void OnDrag(PointerEventData eventData)
{
if (!isDragging) return;
// 累加位移并限制在 [-菜单宽度, 0],只允许左滑露出菜单
currentOffset += eventData.delta.x;
currentOffset = Mathf.Clamp(currentOffset, -menuWidth, 0);
ApplyMenuOffset(currentOffset);
}
public void OnEndDrag(PointerEventData eventData)
{
if (!isDragging) return;
isDragging = false;
// 恢复按钮点击
if (button != null) button.interactable = true;
// 右滑超过菜单宽度的1/5则直接缩回方便关闭已展开的菜单
float dragDistance = currentOffset - dragStartOffset;
if (dragDistance > menuWidth / 5f)
{
AnimateTo(0);
return;
}
// 左滑展示超过菜单宽度的1/3则完整滑出否则缩回
float target = currentOffset < -menuWidth / 3f ? -menuWidth : 0;
AnimateTo(target);
}
/// <summary>
/// 收起菜单(供菜单按钮点击等外部调用)
/// </summary>
public void CloseMenu()
{
if (!isMenuOpen) return;
AnimateTo(0);
}
/// <summary>
/// 将菜单平移到指定偏移
/// </summary>
private void ApplyMenuOffset(float offset)
{
Vector3 pos = menuInitPos;
pos.x += offset;
menuRect.anchoredPosition3D = pos;
}
/// <summary>
/// 平滑动画到目标偏移
/// </summary>
private void AnimateTo(float target)
{
tweener?.Kill();
bool open = target < 0;
if (open)
{
// 已有其他item的菜单展开时先收起保证全局只有一个菜单
if (openItem != null && openItem != this)
openItem.CloseMenu();
openItem = this;
}
else if (openItem == this)
{
openItem = null;
}
isMenuOpen = open;
if (!isMenuOpen && blurOverlay != null)
blurOverlay.SetActive(false); // 菜单缩回时隐藏模糊层
tweener = DOTween.To(() => currentOffset, v =>
{
currentOffset = v;
ApplyMenuOffset(v);
}, target, 0.25f).SetEase(Ease.OutCubic);
}
}
}