Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix tween color #16090

Closed
wants to merge 12 commits into from
114 changes: 103 additions & 11 deletions cocos/tween/tween-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
THE SOFTWARE.
*/

import { warnID, warn, easing } from '../core';
import { warnID, warn, easing, Color, log, Vec2, Vec3, Vec4, Size, Quat, Rect } from '../core';
import { ActionInterval } from './actions/action-interval';
import { ITweenOption } from './export-api';
import { VERSION } from '../core/global-exports';
Expand Down Expand Up @@ -139,6 +139,7 @@ export class TweenAction extends ActionInterval {
if (value.value !== undefined && (value.easing || value.progress)) {
if (typeof value.easing === 'string') {
customEasing = easing[value.easing];
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
if (!customEasing) warnID(1031, value.easing);
} else {
customEasing = value.easing;
Expand All @@ -149,6 +150,7 @@ export class TweenAction extends ActionInterval {

const prop = Object.create(null);
prop.value = value;
prop.type = '';
prop.easing = customEasing;
prop.progress = progress;
this._props[name] = prop;
Expand All @@ -159,6 +161,7 @@ export class TweenAction extends ActionInterval {
}

clone (): TweenAction {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const action = new TweenAction(this._duration, this._originProps, this._opts);
this._cloneDecoration(action);
return action;
Expand All @@ -180,15 +183,77 @@ export class TweenAction extends ActionInterval {
prop.current = _t;
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
prop.end = relative ? _t + value : value;
} else if (typeof _t === 'object') {
} else if (_t instanceof Color) {
if (prop.start == null) {
prop.start = new Color(); prop.current = new Color(); prop.end = new Color();
}
prop.start.set(_t);
prop.current.set(_t);
if (relative) {
Color.add(prop.end, _t, value);
} else {
prop.end.set(value);
}
prop.type = 'color';
} else if (_t instanceof Rect) {
if (prop.start == null) {
prop.start = new Rect(); prop.current = new Rect(); prop.end = new Rect();
}
prop.start.set(_t);
prop.current.set(_t);
if (relative) {
prop.end.xMin = _t.xMin + value.xMin;
prop.end.yMin = _t.yMin + value.yMin;
prop.end.z = _t.z + value.z;
prop.end.w = _t.w + value.w;
} else {
prop.end.xMin = value.xMin;
prop.end.yMin = value.yMin;
prop.end.z = value.z;
prop.end.w = value.w;
}
prop.type = 'rect';
} else if (_t instanceof Quat) {
if (prop.start == null) {
prop.start = new Quat(); prop.current = new Quat(); prop.end = new Quat();
}
prop.start.set(_t);
prop.current.set(_t);
if (relative) {
Quat.multiply(prop.end, _t, value);
} else {
prop.end.set(value);
}
prop.type = 'quat';
} else if (typeof _t === 'object') {
if (_t instanceof Vec2) {
if (prop.start == null) {
prop.start = new Vec2(); prop.current = new Vec2(); prop.end = new Vec2();
}
prop.type = 'vec2';
} else if (_t instanceof Vec3) {
if (prop.start == null) {
prop.start = new Vec3(); prop.current = new Vec3(); prop.end = new Vec3();
}
prop.type = 'vec3';
zxx43 marked this conversation as resolved.
Show resolved Hide resolved
} else if (_t instanceof Vec4) {
if (prop.start == null) {
prop.start = new Vec4(); prop.current = new Vec4(); prop.end = new Vec4();
}
prop.type = 'vec4';
} else if (_t instanceof Size) {
if (prop.start == null) {
prop.start = new Size(); prop.current = new Size(); prop.end = new Size();
}
prop.type = 'size';
} else if (prop.start == null) {
prop.start = {}; prop.current = {}; prop.end = {};
}

for (const k in value) {
// filtering if it not a number
// eslint-disable-next-line no-restricted-globals
if (isNaN(_t[k])) continue;
if (isNaN(_t[k] as number)) continue;
prop.start[k] = _t[k];
prop.current[k] = _t[k];
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
Expand Down Expand Up @@ -217,18 +282,45 @@ export class TweenAction extends ActionInterval {

const start = prop.start;
const end = prop.end;

if (typeof start === 'number') {
prop.current = interpolation(start, end, prop.current, time);
} else if (typeof start === 'object') {
// const value = prop.value;
for (const k in start) {
// if (value[k].easing) {
// time = value[k].easing(t);
// }
// if (value[k].progress) {
// interpolation = value[k].easing(t);
// }
prop.current[k] = interpolation(start[k], end[k], prop.current[k], time);
if (prop.type && prop.type === 'color') {
prop.current.r = interpolation(start.r, end.r, prop.current.r, time);
prop.current.g = interpolation(start.g, end.g, prop.current.g, time);
prop.current.b = interpolation(start.b, end.b, prop.current.b, time);
prop.current.a = interpolation(start.a, end.a, prop.current.a, time);
} else if (prop.type && prop.type === 'rect') {
prop.current.xMin = interpolation(start.xMin, end.xMin, prop.current.xMin, time);
prop.current.yMin = interpolation(start.yMin, end.yMin, prop.current.yMin, time);
prop.current.z = interpolation(start.z, end.z, prop.current.z, time);
prop.current.w = interpolation(start.w, end.w, prop.current.w, time);
} else if (prop.type && prop.type === 'vec2') {
prop.current.x = interpolation(start.x, end.x, prop.current.x, time);
prop.current.y = interpolation(start.y, end.y, prop.current.y, time);
} else if (prop.type && prop.type === 'vec3') {
prop.current.x = interpolation(start.x, end.x, prop.current.x, time);
prop.current.y = interpolation(start.y, end.y, prop.current.y, time);
prop.current.z = interpolation(start.z, end.z, prop.current.z, time);
} else if (prop.type && prop.type === 'vec4') {
prop.current.x = interpolation(start.x, end.x, prop.current.x, time);
prop.current.y = interpolation(start.y, end.y, prop.current.y, time);
prop.current.z = interpolation(start.z, end.z, prop.current.z, time);
prop.current.w = interpolation(start.w, end.w, prop.current.w, time);
} else if (prop.type && prop.type === 'size') {
prop.current.width = interpolation(start.width, end.width, prop.current.width, time);
prop.current.height = interpolation(start.height, end.height, prop.current.height, time);
} else if (prop.type && prop.type === 'quat') {
Quat.slerp(prop.current, start, end, time as number);
zxx43 marked this conversation as resolved.
Show resolved Hide resolved
if (prop.progress) {
warn('Quaternion only support slerp interpolation method.');
}
} else {
for (const k in start) {
prop.current[k] = interpolation(start[k], end[k], prop.current[k], time);
}
}
}

Expand Down
183 changes: 181 additions & 2 deletions tests/tween/tween.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Vec3, System } from "../../cocos/core";
import { tween, Tween, TweenSystem } from "../../cocos/tween";
import { Vec3, System, Color, Quat, Rect, Size, Vec2, Vec4 } from "../../cocos/core";
import { tween, Tween, TweenAction, TweenSystem } from "../../cocos/tween";
import { Node, Scene } from "../../cocos/scene-graph";
import { Component } from "../../cocos/scene-graph/component";
import { game, director } from "../../cocos/game";
import { CSMShadowLayer } from "../../cocos/rendering/shadow/csm-layers";
import { Shadows } from "../../cocos/render-scene/scene";
import { CameraComponent } from "../../cocos/misc";

test('remove actions by tag', function () {
const scene = new Scene('test-tags');
Expand Down Expand Up @@ -38,4 +41,180 @@ test('destroySelf', function () {
game.step();
expect(onDestroy).toBeCalledTimes(1);
director.unregisterSystem(sys);
});

test('type color', function () {
const sys = new TweenSystem();
(TweenSystem.instance as any) = sys;
director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);

const shadow = new Shadows();
shadow.shadowColor.set(200, 100, 50, 250);
const target = Color.TRANSPARENT;
const tweenact = tween(shadow).to(1, {shadowColor: target}, { easing: "bounceOut" });
tweenact.start();

zxx43 marked this conversation as resolved.
Show resolved Hide resolved
for (let i = 0; i < 100; ++i) {
game.step();
}
// @ts-expect-error access private property
const action = tweenact._actions[0] as TweenAction;
// @ts-expect-error access private property
const props = action._props;
for (const property in props) {
const prop = props[property];
expect(prop.current instanceof Color).toBeTruthy();
expect(Color.equals(prop.current, target)).toBeTruthy();
zxx43 marked this conversation as resolved.
Show resolved Hide resolved
}
director.unregisterSystem(sys);
});

test('type quat', function () {
const sys = new TweenSystem();
(TweenSystem.instance as any) = sys;
director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);

const node = new Node();
const target = new Quat(1, 1, 1, 1);
const tweenact = tween(node).to(1, {rotation: target}, { easing: "bounceOut" });
tweenact.start();

for (let i = 0; i < 100; ++i) {
game.step();
}
// @ts-expect-error access private property
const action = tweenact._actions[0] as TweenAction;
// @ts-expect-error access private property
const props = action._props;
for (const property in props) {
const prop = props[property];
expect(prop.current instanceof Quat).toBeTruthy();
expect(Quat.equals(prop.current, target)).toBeTruthy();
}
director.unregisterSystem(sys);
});

test('type rect', function () {
const sys = new TweenSystem();
(TweenSystem.instance as any) = sys;
director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);

const camera = new CameraComponent();
const target = new Rect(1, 1, 10, 20);
const tweenact = tween(camera).to(1, {rect: target}, { easing: "bounceOut" });
tweenact.start();

for (let i = 0; i < 100; ++i) {
game.step();
}
// @ts-expect-error access private property
const action = tweenact._actions[0] as TweenAction;
// @ts-expect-error access private property
const props = action._props;
for (const property in props) {
const prop = props[property];
expect(prop.current instanceof Rect).toBeTruthy();
expect(Rect.equals(prop.current, target)).toBeTruthy();
}
director.unregisterSystem(sys);
});

test('type size', function () {
const sys = new TweenSystem();
(TweenSystem.instance as any) = sys;
director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);

const rect = new Rect();
const target = new Size(800, 600);
const tweenact = tween(rect).to(1, {size: target}, { easing: "bounceOut" });
tweenact.start();

for (let i = 0; i < 100; ++i) {
game.step();
}
// @ts-expect-error access private property
const action = tweenact._actions[0] as TweenAction;
// @ts-expect-error access private property
const props = action._props;
for (const property in props) {
const prop = props[property];
expect(prop.current instanceof Size).toBeTruthy();
expect(Rect.equals(prop.current, target)).toBeTruthy();
}
director.unregisterSystem(sys);
});

test('type vec2', function () {
const sys = new TweenSystem();
(TweenSystem.instance as any) = sys;
director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);

const rect = new Rect();
const target = new Vec2(20, 10);
const tweenact = tween(rect).to(1, {origin: target}, { easing: "bounceOut" });
tweenact.start();

for (let i = 0; i < 100; ++i) {
game.step();
}
// @ts-expect-error access private property
const action = tweenact._actions[0] as TweenAction;
// @ts-expect-error access private property
const props = action._props;
for (const property in props) {
const prop = props[property];
expect(prop.current instanceof Vec2).toBeTruthy();
expect(Vec2.equals(prop.current, target)).toBeTruthy();
}
director.unregisterSystem(sys);
});

test('type vec3', function () {
const sys = new TweenSystem();
(TweenSystem.instance as any) = sys;
director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);

const node = new Node();
const target = new Vec3(10, 20, 30);
const tweenact = tween(node).to(1, {position: target}, { easing: "bounceOut" });
tweenact.start();

for (let i = 0; i < 100; ++i) {
game.step();
}
// @ts-expect-error access private property
const action = tweenact._actions[0] as TweenAction;
// @ts-expect-error access private property
const props = action._props;
for (const property in props) {
const prop = props[property];
expect(prop.current instanceof Vec3).toBeTruthy();
expect(Vec3.equals(prop.current, target)).toBeTruthy();
}
director.unregisterSystem(sys);
});

test('type vec4', function () {
const sys = new TweenSystem();
(TweenSystem.instance as any) = sys;
director.registerSystem(TweenSystem.ID, sys, System.Priority.MEDIUM);

const shadowLayer = new CSMShadowLayer(4);
const target = new Vec4(10, 20, 30, 40);
const tweenact = tween(shadowLayer).to(1, {csmAtlas: target}, { easing: "bounceOut" });
tweenact.start();

for (let i = 0; i < 100; ++i) {
game.step();
}
// @ts-expect-error access private property
const action = tweenact._actions[0] as TweenAction;
// @ts-expect-error access private property
const props = action._props;
for (const property in props) {
const prop = props[property];
expect(prop.current instanceof Vec4).toBeTruthy();
expect(Vec4.equals(prop.current, target)).toBeTruthy();
}
director.unregisterSystem(sys);
});
Loading