Files
Cocos3.8.5/assets/script/QuickTween/QuickTween.ts
ZhouXiao 487c68994d feat: add builder configuration file
feat: add cocos-service configuration file
feat: add device configuration file
feat: add engine configuration file
feat: add information configuration file
feat: add program configuration file
feat: add project configuration file
feat: add TypeScript configuration file
2025-12-22 11:42:51 +08:00

408 lines
14 KiB
TypeScript

import { Component, Node, Vec3, tween, Quat, Sprite, Color, math, easing, Camera, ITweenOption, IPunchTweenOption, IShakeTweenOption, Label } from 'cc';
import { calcPunchData, calcShakeData, quadraticCurve } from './Util';
//////////////////////
// Transform
//////////////////////
Node.prototype.qtPosition = function (to: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { position: to }, opts);
}
Node.prototype.qtPositionX = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.position;
return tween(this).to(duration, { position: new Vec3(to, startPos.y, startPos.z) }, opts);
}
Node.prototype.qtPositionY = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.position;
return tween(this).to(duration, { position: new Vec3(startPos.x, to, startPos.z) }, opts);
}
Node.prototype.qtPositionZ = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.position;
return tween(this).to(duration, { position: new Vec3(startPos.x, startPos.y, to) }, opts);
}
Node.prototype.qtWorldPosition = function (to: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { worldPosition: to }, opts);
}
Node.prototype.qtWorldPositionX = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.worldPosition;
return tween(this).to(duration, { worldPosition: new Vec3(to, startPos.y, startPos.z) }, opts);
}
Node.prototype.qtWorldPositionY = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.worldPosition;
return tween(this).to(duration, { worldPosition: new Vec3(startPos.x, to, startPos.z) }, opts);
}
Node.prototype.qtWorldPositionZ = function (to: number, duration: number, opts?: ITweenOption) {
const startPos = this.worldPosition;
return tween(this).to(duration, { worldPosition: new Vec3(startPos.x, startPos.y, to) }, opts);
}
Node.prototype.qtRotation = function (to: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { eulerAngles: to }, opts);
}
Node.prototype.qtRotationQuat = function (to: Quat, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { rotation: to }, opts);
}
Node.prototype.qtScale = function (to: Vec3 | number, duration: number, opts?: ITweenOption) {
let toScale = to;
if (!(to instanceof Vec3)) {
toScale = new Vec3(to, to, to);
}
return tween(this).to(duration, { scale: toScale }, opts);
}
Node.prototype.qtScaleX = function (to: number, duration: number, opts?: ITweenOption) {
const startScale = this.scale;
return tween(this).to(duration, { scale: new Vec3(to, startScale.y, startScale.z) }, opts);
}
Node.prototype.qtScaleY = function (to: number, duration: number, opts?: ITweenOption) {
const startScale = this.scale;
return tween(this).to(duration, { scale: new Vec3(startScale.x, to, startScale.z) }, opts);
}
Node.prototype.qtScaleZ = function (to: number, duration: number, opts?: ITweenOption) {
const startScale = this.scale;
return tween(this).to(duration, { scale: new Vec3(startScale.x, startScale.y, to) }, opts);
}
Node.prototype.qtPunchPosition = function (punch: Vec3, duration: number, opts?: IPunchTweenOption) {
const vibrato = opts?.vibrato ?? 3;
const elasticity = opts?.elasticity ?? 0.5;
const { tos, durations } = calcPunchData(this.position.clone(), punch, duration, vibrato, elasticity);
const punchTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
punchTween.then(tween().to(d, { position: to }, tweenOpts));
});
return punchTween.union();
}
Node.prototype.qtPunchRotation = function (punch: Vec3, duration: number, opts?: IPunchTweenOption) {
const vibrato = opts?.vibrato ?? 3;
const elasticity = opts?.elasticity ?? 0.5;
const { tos, durations } = calcPunchData(this.rotation.clone(), punch, duration, vibrato, elasticity);
const punchTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
punchTween.then(tween().to(d, { eulerAngles: to }, tweenOpts));
});
return punchTween.union();
}
Node.prototype.qtPunchScale = function (punch: Vec3, duration: number, opts?: IPunchTweenOption) {
const vibrato = opts?.vibrato ?? 3;
const elasticity = opts?.elasticity ?? 0.5;
const { tos, durations } = calcPunchData(this.scale.clone(), punch, duration, vibrato, elasticity);
const punchTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
punchTween.then(tween().to(d, { scale: to }, tweenOpts));
});
return punchTween.union();
}
Node.prototype.qtJumpPosition = function (to: Vec3, jumpHeight: number, jumpNum: number, duration: number, opts?: ITweenOption) {
const tweenPos = new Vec3();
const jumpTween = tween(this);
const totalNum = jumpNum * 2;
// 初始化选项对象,确保即使没有传入选项也不会出错
const options = opts || {};
this.jumpY = 0;
let startPosY = 0;
const yUpTween = tween().to(duration / totalNum, { jumpY: jumpHeight }, {
onStart: (target: Node) => {
startPosY = target.position.y;
target.jumpY = 0;
},
onUpdate: (target: Node, ratio) => {
tweenPos.set(target.position);
tweenPos.y = startPosY + target.jumpY;
target.position = tweenPos;
},
onComplete: (target: Node) => {
target.jumpY = 0;
}, easing: 'quadOut'
}).to(duration / totalNum, { jumpY: jumpHeight }, {
onStart: (target: Node) => {
startPosY = target.position.y;
},
onUpdate: (target: Node, ratio) => {
tweenPos.set(target.position);
tweenPos.y = startPosY - target.jumpY;
target.position = tweenPos;
},
onComplete: (target: Node) => {
target.jumpY = 0;
}, easing: 'quadIn',
}).union().repeat(jumpNum);
this.jumpOffsetY = 0;
let offsetY = 0;
const offsetYTween = tween().to(duration, { jumpOffsetY: to.y - this.position.y }, {
onStart: (target: Node) => {
offsetY = to.y - target.position.y;
target.jumpOffsetY = 0;
},
onUpdate: (target: Node, ratio) => {
const interpOffsetY = easing.quadOut(ratio) * offsetY;
tweenPos.set(target.position);
tweenPos.y += interpOffsetY;
target.position = tweenPos;
},
onComplete: (target: Node) => {
target.jumpOffsetY = 0;
}, easing: 'quadOut'
});
this.jumpX = this.position.x;
this.jumpZ = this.position.z;
const xzTween = tween().to(duration, { jumpX: to.x, jumpZ: to.z }, {
// 使用可选链运算符或者默认值
onStart: options.onStart,
onUpdate: (target: Node, ratio) => {
tweenPos.set(target.position);
tweenPos.x = target.jumpX;
tweenPos.z = target.jumpZ;
target.position = tweenPos;
options.onUpdate?.();
},
onComplete: (target: Node) => {
// delete target.jumpX;
// delete target.jumpY;
// delete target.jumpZ;
// delete target.jumpOffsetY;
target.jumpX = target.position.x;
target.jumpZ = target.position.z;
options.onComplete?.();
}
})
jumpTween.parallel(yUpTween, offsetYTween, xzTween);
return jumpTween;
}
Node.prototype.qtShakePosition = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const options = opts || {};
const vibrato = options?.vibrato ?? 10;
const randomness = options?.randomness ?? 90;
const fadeOut = options?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.position.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
const shakeTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: options.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: options.onComplete
}
}
shakeTween.then(tween().to(d, { position: to }, tweenOpts));
});
return shakeTween.union();
}
Node.prototype.qtShakeRotation = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const vibrato = opts?.vibrato ?? 10;
const randomness = opts?.randomness ?? 90;
const fadeOut = opts?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.eulerAngles.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
const shakeTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
shakeTween.then(tween().to(d, { eulerAngles: to }, tweenOpts));
});
return shakeTween.union();
}
Node.prototype.qtShakeScale = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const vibrato = opts?.vibrato ?? 10;
const randomness = opts?.randomness ?? 90;
const fadeOut = opts?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.scale.clone(), duration, toStrength, vibrato, randomness, false, vectorBased, fadeOut)
const shakeTween = tween(this);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
shakeTween.then(tween().to(d, { scale: to }, tweenOpts));
});
return shakeTween.union();
}
Node.prototype.qtQuadraticCurve = function (p1: Vec3, cp: Vec3, p2: Vec3, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { position: p2, easing: opts?.easing }, {
onUpdate(target, ratio) {
target.setPosition(quadraticCurve(ratio, p1, cp, p2));
},
onComplete: opts?.onComplete,
onStart: opts?.onStart
}).union();
}
//////////////////////
// Sprite
//////////////////////
// good color lerp
// https://www.alanzucconi.com/2016/01/06/colour-interpolation/
Sprite.prototype.qtColor = function (to: Color, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { color: to }, opts);
}
Sprite.prototype.qtOpacity = function (to: number, duration: number, opts?: ITweenOption) {
const startColor = this.color.clone();
const tempColor = new Color();
return tween(this).to(duration, { color: new Color(startColor.r, startColor.g, startColor.b, to) }, {
onStart: opts.onStart,
onUpdate: (target: { _val: number }, ratio: number) => {
const lerpA = startColor.a + (to - startColor.a) * ratio
tempColor.set(startColor.r, startColor.g, startColor.b, lerpA);
this.color = tempColor;
opts.onUpdate?.();
},
onComplete: opts.onComplete
});
}
//////////////////////
// Label
//////////////////////
Label.prototype.qtColor = function (to: Color, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { color: to }, opts);
}
Label.prototype.qtString = function (to: string, duration: number, opts?: ITweenOption) {
return tween(this).to(duration, { string: to }, opts);
}
//////////////////////
// Camera
//////////////////////
Camera.prototype.qtShakePosition = function (strength: Vec3 | number, duration: number, opts?: IShakeTweenOption) {
const vibrato = opts?.vibrato ?? 10;
const randomness = opts?.randomness ?? 90;
const fadeOut = opts?.fadeOut ?? true;
let toStrength: Vec3;
let vectorBased = false;
if (!(strength instanceof Vec3)) {
toStrength = new Vec3(strength, strength, strength);
} else {
toStrength = strength;
vectorBased = true;
}
const { tos, durations } = calcShakeData(this.node.position.clone(), duration, toStrength, vibrato, randomness, true, vectorBased, fadeOut)
const shakeTween = tween(this.node);
tos.forEach((to, index) => {
const d = durations[index];
let tweenOpts: ITweenOption | undefined;
if (index === 0) {
tweenOpts = {
onStart: opts.onStart
}
} else if (index === tos.length - 1) {
tweenOpts = {
onComplete: opts.onComplete
}
}
shakeTween.then(tween().to(d, { position: to }, tweenOpts));
});
return shakeTween.union();
}