rp_11001/assets/Game/SlotRanking/scripts/VScrollViewItem.ts
TJH 5f380d78e9
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 1m20s
龙虎榜相关
2025-12-26 15:37:21 +08:00

220 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//@ts-ignore
import { _decorator, Component, Node, EventTouch, Vec2, Label, Tween, tween, Vec3, settings, Sorting2D, RichText, sys } from 'cc';
const { ccclass } = _decorator;
const hasSorting2d = Sorting2D !== undefined;
if (!hasSorting2d) {
// console.warn(`❌当前引擎版本不支持Sorting2D组件如果需要请切换到3.8.7及以上版本`);
}
/**
* 更改UI节点的渲染排序层级
* @param sortingNode Node
* @param sortingLayer number
* @param sortingOrder number
*/
export function changeUISortingLayer(sortingNode: Node, sortingLayer: number, sortingOrder?: number) {
if (!hasSorting2d) {
return;
}
let sortingLayers = settings.querySettings('engine', 'sortingLayers') as any[];
//编辑器bug,默认有default,但是读取出来没有,需要自己配置一个后才会有默认数据.
if (!sortingLayers || sortingLayers.length === 0) {
sortingLayers = [{ id: 0, value: 0, name: 'default' }];
}
const result = sortingLayers.find(layer => layer.value === sortingLayer);
//如果没有找到对应的layer,则使用引擎内置默认层,并给出警告
if (!result) {
console.warn(`❌未找到对应的sortingLayer:${sortingLayer},请检查是否已在项目设置中配置该层级。将使用默认层级代替。`);
sortingLayer = sortingLayers[0].value;
}
const sort2d = sortingNode.getComponent(Sorting2D) || sortingNode.addComponent(Sorting2D);
if (sort2d) {
//@ts-ignore
sort2d.sortingLayer = sortingLayer;
if (sortingOrder !== undefined) {
//@ts-ignore
sort2d.sortingOrder = sortingOrder;
}
}
}
/**
* 挂载在每个 item 预制体的根节点上
* 负责处理点击逻辑,通过回调通知父组件
*/
@ccclass('VScrollViewItem')
export class VScrollViewItem extends Component {
/** 当前 item 对应的数据索引 */
public dataIndex: number = -1;
public useItemClickEffect: boolean = true;
/** 点击回调(由 VirtualScrollView 注入) */
public onClickCallback: ((index: number) => void) | null = null;
/** 长按回调(由 VirtualScrollView 注入) */
public onLongPressCallback: ((index: number) => void) | null = null;
/** 长按触发时长(秒) */
public longPressTime: number = 0.6;
private _touchStartNode: Node | null = null;
private _isCanceled: boolean = false;
private _startPos: Vec2 = new Vec2();
private _moveThreshold: number = 40; // 滑动阈值
private _clickThreshold: number = 10; // 点击阈值
private _longPressTimer: number = 0; // 长按计时器
private _isLongPressed: boolean = false; // 是否已触发长按
onLoad() {
// 一次性注册事件,生命周期内不变
this.node.on(Node.EventType.TOUCH_START, this._onTouchStart, this);
this.node.on(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
this.node.on(Node.EventType.TOUCH_END, this._onTouchEnd, this);
this.node.on(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
}
protected start(): void {
// this.onSortLayer();
}
onDestroy() {
// 清理事件
this.node.off(Node.EventType.TOUCH_START, this._onTouchStart, this);
this.node.off(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
this.node.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
this.node.off(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
}
/**
* 将所有子节点的 Label 组件渲染单独排序在一起,并且item的每个lable组件都独立一个orderNumber,以免交错断合批
* @param node
*/
public onSortLayer() {
let orderNumber = 1;
const labels = this.node.getComponentsInChildren(Label);
for (let i = 0; i < labels.length; i++) {
changeUISortingLayer(labels[i].node, 0, orderNumber);
orderNumber++;
}
}
/** 关闭渲染分层 */
public offSortLayer() {
let orderNumber = 0;
const labels = this.node.getComponentsInChildren(Label);
for (let i = 0; i < labels.length; i++) {
changeUISortingLayer(labels[i].node, 0, orderNumber);
// const item = labels[i];
// const sort2d = item.node.getComponent(Sorting2D);
// sort2d && (sort2d.enabled = false);
// orderNumber++;
}
}
/** 外部调用:更新数据索引 */
public setDataIndex(index: number) {
this.dataIndex = index;
}
protected update(dt: number): void {
// 如果正在触摸且未取消,累加长按计时
if (this._touchStartNode && !this._isCanceled && !this._isLongPressed) {
this._longPressTimer += dt;
if (this._longPressTimer >= this.longPressTime) {
this._triggerLongPress();
}
}
}
private _triggerLongPress() {
this._isLongPressed = true;
if (this.onLongPressCallback) {
this.onLongPressCallback(this.dataIndex);
}
// 触发长按后恢复缩放
this._restoreScale();
}
private _onTouchStart(e: EventTouch) {
// console.log("_onTouchStart");
this._touchStartNode = this.node;
this._isCanceled = false;
this._isLongPressed = false;
this._longPressTimer = 0;
e.getLocation(this._startPos);
// 缩放反馈(假设第一个子节点是内容容器)
if (this.useItemClickEffect && this.node.children.length > 0) {
this.node.setScale(0.95, 0.95);
}
}
private _onTouchMove(e: EventTouch) {
if (this._isCanceled) return;
const movePos = e.getLocation();
const dx = movePos.x - this._startPos.x;
const dy = movePos.y - this._startPos.y;
const dist = Math.sqrt(dx * dx + dy * dy);
// 超过阈值认为是滑动,取消点击和长按
if (dist > this._moveThreshold) {
this._isCanceled = true;
this._restoreScale();
this._touchStartNode = null;
}
}
private _onTouchEnd(e: EventTouch) {
if (this._isCanceled) {
this._reset();
return;
}
// 如果已经触发了长按,不再触发点击
if (this._isLongPressed) {
this._reset();
return;
}
this._restoreScale();
const endPos = e.getLocation();
const dx = endPos.x - this._startPos.x;
const dy = endPos.y - this._startPos.y;
const dist = Math.sqrt(dx * dx + dy * dy);
// 移动距离小于阈值才算点击
if (dist < this._clickThreshold && this._touchStartNode === this.node) {
if (this.onClickCallback) {
this.onClickCallback(this.dataIndex);
}
}
this._reset();
}
private _onTouchCancel(e: EventTouch) {
this._restoreScale();
this._reset();
}
private _restoreScale() {
if (this.useItemClickEffect && this.node.children.length > 0) {
this.node.setScale(1.0, 1.0);
}
}
private _reset() {
this._touchStartNode = null;
this._isCanceled = false;
this._longPressTimer = 0;
this._isLongPressed = false;
}
}