import { Tween } from "cc"; import { Component, Enum, EventHandler, Node, quat, Quat, tween, v3, Vec3, _decorator } from "cc"; import { TweenEasing } from "./TweenEasing"; const { ccclass, property } = _decorator; /** * 缓动数据 * @author 袁浩 */ @ccclass("PropsData") export class PropsData { @property({ tooltip: "坐标", type: Vec3 }) public position: Vec3 = new Vec3(); @property({ tooltip: "旋转", type: Vec3 }) public eulerAngles: Vec3 = new Vec3(); @property({ tooltip: "缩放", type: Vec3 }) public scale: Vec3 = new Vec3(1, 1, 1); } /** * 缓动数据 * @author 袁浩 */ @ccclass("TweenData") export class TweenData { @property({ tooltip: "属性数据", type: PropsData }) public propsData: PropsData; @property({ tooltip: "持续时间,单位为秒" }) public duration: number = 0.3; @property({ tooltip: "延迟时间,单位为秒" }) public delay: number = 0; @property({ tooltip: "是否为差值" }) public isOffset: boolean = false; @property({ tooltip: "当缓动动作完成时触发", type: EventHandler }) public onComplete: EventHandler; @property({ tooltip: "当缓动动作启动时触发", type: EventHandler }) public onStart: EventHandler; @property({ tooltip: "当缓动动作更新时触发", type: EventHandler }) public onUpdate: EventHandler; } /** * 缓动组件,目前只支持节点的三个基础属性 * @author 袁浩 */ @ccclass('UITween') export class UITween extends Component { @property({ tooltip: "节点初始属性", type: PropsData }) public fromData: PropsData; @property({ tooltip: "节点目标属性", type: [TweenData] }) public targetData: TweenData[] = []; @property({ tooltip: "要播放缓动动画的目标,不填则是当前节点", type: Node }) public target: Node; @property({ tooltip: "是否在缓动组件运行时自动播放缓动动画" }) public playOnLoad: boolean = false; @property({ tooltip: "缓动效果", type: Enum(TweenEasing) }) public tweenEasing: TweenEasing = TweenEasing.linear; @property({ tooltip: "执行次数,为0则一直重复执行" }) public repeat: number = 1; @property({ tooltip: "单次缓动序列执行完毕时触", type: EventHandler }) public onOneRepeatComplete: EventHandler; start() { !this.target && (this.target = this.node); this.target = this.target || this.node; this.currentRepeat = this.repeat; this.playOnLoad && this.play(); } private t: Tween; private currentRepeat: number; public play() { if (this.repeat == 0) { this.t = tween(this.target); this.readyPlay(); this.t = this.t.call(this.play.bind(this)); this.t.start(); } else if (this.currentRepeat) { this.t = tween(this.target); this.readyPlay(); this.t = this.t.call(this.play.bind(this)); this.currentRepeat--; this.t.start(); } else { this.t = null; this.currentRepeat = this.repeat; } } public readyPlay() { let self = this; if (this.fromData) { this.node.position = this.fromData.position.clone(); this.node.scale = this.fromData.scale.clone(); this.node.eulerAngles = this.fromData.eulerAngles.clone(); } if (this.targetData.length) { for (let i = 0; i < this.targetData.length; i++) { const targetData = this.targetData[i]; if (targetData.delay != 0) { this.t = this.t.delay(targetData.delay); } let toData = { position: targetData.propsData.position, scale: targetData.propsData.scale, eulerAngles: targetData.propsData.eulerAngles } if (targetData.isOffset) { toData.position = this.node.position.clone().add(targetData.propsData.position); toData.scale = this.node.scale.clone().add(targetData.propsData.scale); toData.eulerAngles = this.node.eulerAngles.clone().add(targetData.propsData.eulerAngles); } this.t = this.t.to( targetData.duration, toData, { easing: TweenEasing[this.tweenEasing] as any, onComplete: (target: Node) => { targetData.onComplete && targetData.onComplete.emit([target]); }, onStart: (target: Node) => { targetData.onStart && targetData.onStart.emit([target]) }, onUpdate: (target: Node, ratio: number) => { targetData.onUpdate && targetData.onUpdate.emit([target, ratio]) } } ); } } if (this.onOneRepeatComplete) { this.t = this.t.call(() => { self.onOneRepeatComplete.emit([this, this.t]) }); } } }