import { TweenEasing } from "./TweenEasing"; const { ccclass, property, executeInEditMode, playOnFocus } = cc._decorator; /** * 缓动数据 * @author 薛鸿潇 */ @ccclass("PropsData") export class PropsData { @property({ displayName: "坐标" }) public position: cc.Vec2 = cc.Vec2.ZERO; @property({ displayName: "旋转" }) public angle: number = 0; @property({ displayName: "缩放" }) public scale: number = 1; } /** * 缓动数据 * @author 薛鸿潇 */ @ccclass("TweenData") export class TweenData { @property({ displayName: "属性数据", type: PropsData }) public propsData: PropsData = new PropsData(); @property({ displayName: "持续时间", tooltip: '单位:秒' }) public duration: number = 0.3; @property({ displayName: "延迟时间", tooltip: '单位:秒' }) public delay: number = 0; @property({ displayName: "是否为差值" }) public isOffset: boolean = false; @property({ displayName: '完成回调', tooltip: "缓动完成时触发", type: cc.Component.EventHandler }) public onComplete: cc.Component.EventHandler = new cc.Component.EventHandler(); @property({ displayName: '开始回调', tooltip: "缓动启动时触发", type: cc.Component.EventHandler }) public onStart: cc.Component.EventHandler = new cc.Component.EventHandler(); @property({ displayName: '过程回调', tooltip: "缓动更新时触发", type: cc.Component.EventHandler }) public onUpdate: cc.Component.EventHandler = new cc.Component.EventHandler(); } /** * 缓动组件,目前只支持节点的三个基础属性 * @author 薛鸿潇 */ @ccclass @executeInEditMode() @playOnFocus() export class UITween extends cc.Component { @property({ displayName: "节点初始属性", type: PropsData }) public fromData: PropsData = new PropsData(); @property({ displayName: "节点目标属性", type: TweenData }) public targetData: TweenData[] = []; @property({ displayName: '缓动节点', tooltip: "要播放缓动动画的目标,不填则是当前节点", type: cc.Node }) public target: cc.Node = null; @property({ displayName: "缓动效果", type: cc.Enum(TweenEasing) }) public tweenEasing: TweenEasing = TweenEasing.linear; @property({ displayName: '执行次数', tooltip: "为0则一直重复执行" }) public repeat: number = 1; @property({ displayName: "单次完成回调", tooltip: '单次缓动序列执行完毕时触发', type: cc.Component.EventHandler }) public onOneRepeatComplete: cc.Component.EventHandler = new cc.Component.EventHandler(); @property({ displayName: "自动播放" }) public playOnLoad: boolean = false; private _edit_play: boolean = false; @property({ displayName: "编辑器预览" }) get edit_play(): boolean { if (this._edit_play && !this.edit_playing) { this.edit_playing = true; this.play(); } return this._edit_play; } set edit_play(v: boolean) { this._edit_play = v; if (this.target && v) { this.target.stopAllActions(); } if (!v) this.edit_playing = false; } /** 编辑器是否正在播放 */ private edit_playing = false; public start() { !this.target && (this.target = this.node); this.target = this.target || this.node; this.currentRepeat = this.repeat; if (CC_EDITOR) { this._edit_play && this.play(); } else { this.playOnLoad && this.play(); } } private t: cc.Tween; /** 剩余播放次数 */ private currentRepeat: number; public play() { if (CC_EDITOR && !this.edit_playing) return; if (this.repeat == 0) { this.t = cc.tween(this.target); this.readyPlay(); this.t = this.t.call(this.play.bind(this)); this.t.start(); } else if (this.currentRepeat) { this.t = cc.tween(this.target); this.readyPlay(); this.t = this.t.call(this.play.bind(this)); this.currentRepeat--; this.t.start(); } else { this.t = null; } } public readyPlay() { let self = this; if (this.fromData) { this.node.setPosition(this.fromData.position.clone()); this.node.scale = this.fromData.scale; this.node.angle = this.fromData.angle; } 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, angle: targetData.propsData.angle } if (targetData.isOffset) { toData.position = this.node.getPosition().add(targetData.propsData.position); toData.scale = this.node.scale + targetData.propsData.scale; toData.angle = this.node.angle + targetData.propsData.angle; } this.t = this.t.to( targetData.duration, toData, { easing: TweenEasing[this.tweenEasing] as any, onComplete: (target: cc.Node) => { targetData.onComplete && targetData.onComplete.emit([target]); this.edit_playing = false; this._edit_play = false; }, onStart: (target: cc.Node) => { targetData.onStart && targetData.onStart.emit([target]) }, onUpdate: (target: cc.Node, ratio: number) => { targetData.onUpdate && targetData.onUpdate.emit([target, ratio]) } } ); } } if (this.onOneRepeatComplete) { this.t = this.t.call(() => { self.onOneRepeatComplete.emit([this, this.t]) }); } } }