import { _decorator, CCInteger, clamp, Component, Enum, EventTouch, game, Node, Rect, UITransform, v3, Vec2, Vec3 } from 'cc'; const { ccclass, property } = _decorator; enum MoveType { Vertical, Horizontal, } @ccclass('MoveContent') export class MoveContent extends Component { /** 当顶部超出时触发 */ static EVENT_ON_TOP = "MoveContent.EVENT_ON_TOP"; /** 当底部超出时触发 */ static EVENT_ON_BOTTOM = "MoveContent.EVENT_ON_BOTTOM"; /** 当左侧超出时触发 */ static EVENT_ON_LEFT = "MoveContent.EVENT_ON_LEFT"; /** 当右侧超出时触发 */ static EVENT_ON_RIGHT = "MoveContent.EVENT_ON_RIGHT"; /** 每次验证边界都会触发 */ static EVENT_ON_CHECKBOUNDARY = "MoveContent.EVENT_ON_CHECKBOUNDARY"; @property({ type: Enum(MoveType) }) moveType: MoveType = MoveType.Vertical; @property({ type: Node }) content: Node = null; @property({ type: CCInteger }) top = 0; @property({ type: CCInteger }) bottom = 0; @property({ type: CCInteger }) left = 0; @property({ type: CCInteger }) right = 0; protected onEnable(): void { 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.onTouchEnd, this); this.scheduleOnce(() => { this.checkBoundary(); }); } protected onDisable(): void { 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.onTouchEnd, this); this.startAutoMove = false; } protected update(dt: number): void { if (this.startAutoMove) { if (this.moveDir < 0) { this.moveOffset += dt * 10; if (this.moveOffset >= 0) this.startAutoMove = false; } else { this.moveOffset -= dt * 10; if (this.moveOffset <= 0) this.startAutoMove = false; } this.move(); } } protected lateUpdate(dt: number): void { this.canEmit = true; } startMove = false; moveOffset = 0; moveDir = 1; startPos = null; startTime = 0; startAutoMove = false; touchDistance = 0; canEmit = true; onTouchStart(event: EventTouch) { this.moveOffset = 0; this.startMove = true; this.startAutoMove = false; this.startPos = event.getUILocation(); this.startTime = game.totalTime; } onTouchMove(event: EventTouch) { if (this.startMove) { let pos = event.getDelta(); //计算滑动方向 this.moveOffset = this.moveType == MoveType.Vertical ? pos.y : pos.x; this.moveDir = this.moveOffset > 0 ? 1 : -1; this.move(); } } onTouchEnd(event: EventTouch) { this.startMove = false; let pos = event.getUILocation(); let endTime = game.totalTime; let duration = endTime - this.startTime; this.startAutoMove = true; this.touchDistance = Vec2.distance(pos, this.startPos); this.moveOffset = this.moveOffset / duration * 500; } move() { let x = 0, y = 0; if (this.moveType == MoveType.Vertical) y = this.moveOffset; if (this.moveType == MoveType.Horizontal) x = this.moveOffset; this.content.position = this.content.position.add(new Vec3(x, y, 0)); this.checkBoundary(); } checkBoundary() { let boundary = this.getBoundary(); let x = 0, y = 0; if (this.moveType == MoveType.Vertical) { if (boundary.yMin > -this.node.h * this.node.anchor_y + this.bottom + this.node.worldPosition.y) { y = (-this.node.h * this.node.anchor_y + this.bottom) - boundary.yMin + this.node.worldPosition.y; this.node.emit(MoveContent.EVENT_ON_BOTTOM, boundary); } if (boundary.yMax < this.node.h * (1 - this.node.anchor_y) - this.top + this.node.worldPosition.y) { y = (this.node.h * (1 - this.node.anchor_y) - this.top) - boundary.yMax + this.node.worldPosition.y; this.node.emit(MoveContent.EVENT_ON_TOP, boundary); } } else { if (boundary.xMax < this.node.w * (1 - this.node.anchor_x) - this.right + this.node.worldPosition.x) { x = boundary.xMax - (this.node.w * (1 - this.node.anchor_x) - this.right); this.node.emit(MoveContent.EVENT_ON_RIGHT, boundary); } if (boundary.xMin > -this.node.w * this.node.anchor_x + this.left + this.node.worldPosition.x) { x = boundary.xMin - (-this.node.w * this.node.anchor_x + this.left); this.node.emit(MoveContent.EVENT_ON_LEFT, boundary); } } if (x != 0 || y != 0) { this.content.position = this.content.position.add(new Vec3(x, y, 0)); } this.node.emit(MoveContent.EVENT_ON_CHECKBOUNDARY, boundary); this.canEmit = false; if (this.canEmit) { } return this._boundaryRect; } getBoundary() { this._boundaryRect.set(0, 0, 0, 0); for (let i = 0; i < this.content.children.length; i++) { let c = this.content.children[i]; if ((i == 0) || (c.worldPosition.y + c.h * (1 - c.anchor_y) > this._boundaryRect.yMax)) { this._boundaryRect.yMax = c.worldPosition.y + c.h * (1 - c.anchor_y); this._topNode = c; } if ((i == 0) || (c.worldPosition.y - c.h * c.anchor_y < this._boundaryRect.yMin)) { this._boundaryRect.yMin = c.worldPosition.y - c.h * c.anchor_y; this._bottomNode = c; } if ((i == 0) || (c.worldPosition.x + c.w * (1 - c.anchor_x) > this._boundaryRect.xMax)) { this._boundaryRect.xMax = c.worldPosition.x + c.w * (1 - c.anchor_x); this._rightNode = c; } if ((i == 0) || (c.worldPosition.x - c.w * c.anchor_x < this._boundaryRect.xMin)) { this._boundaryRect.xMin = c.worldPosition.x - c.w * c.anchor_x; this._leftNode = c; } } return this._boundaryRect; } private _boundaryRect = new Rect(); private _topNode: Node = null; private _bottomNode: Node = null; private _leftNode: Node = null; private _rightNode: Node = null; moveToChild(node: Node) { if (this.content.children.indexOf(node) < 0) return; // 获取节点的世界坐标 let worldPos = node.getWorldPosition(); // 转换为content节点的本地坐标系统 let localPos = this.content.getComponent(UITransform).convertToNodeSpaceAR(worldPos); // 计算目标位置:将节点移动到视图中心 let targetX = this.moveType == MoveType.Vertical ? 0 : -localPos.x; let targetY = this.moveType == MoveType.Vertical ? -localPos.y : 0; // 应用移动并检查边界 this.content.setPosition(targetX, targetY, 0); this.checkBoundary(); } moveToXY(x: number = 0, y: number = 0) { let pso = this.content.position; x = x == 0 || this.moveType == MoveType.Vertical ? pso.x : x; y = y == 0 || this.moveType == MoveType.Horizontal ? pso.y : y; this.content.position = v3(x, y, 0); this.checkBoundary(); } }