diff --git a/packages/webgal/public/game/scene/demo_parallel_animation.txt b/packages/webgal/public/game/scene/demo_parallel_animation.txt new file mode 100644 index 000000000..796ac524b --- /dev/null +++ b/packages/webgal/public/game/scene/demo_parallel_animation.txt @@ -0,0 +1,31 @@ +changeBg:WebGalEnter.webp -next; +changeFigure:stand.webp -id=figure01 -transform={"position":{"x":1000,"y":720}}; +;演示setAnimation平行执行 +setAnimation:shockwaveIn -target=figure01 -next +setAnimation:move-front-and-back -target=figure01 -parallel +;演示通过-continue接续执行两个常规setTransform正常运作、不被打断 +setTransform:{"position":{"x":-1000}} -duration=5000 -target=figure01 -continue +setTransform:{"position":{"x":1000}} -duration=5000 -target=figure01 +;演示setTransform平行执行 +setTransform:{"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5} -duration=5000 -target=figure01 -ease=easeOut -next -keep +wait:2000 +setTransform:{"position":{"y":0},"scale":{"y":0.5},"saturation":0} -duration=5000 -target=figure01 -ease=linear -parallel -continue +setTransform:{"position":{"y":-720},"scale":{"y":1},"saturation":1} -duration=5000 -target=figure01 -ease=linear -next +setTransform:{"position":{"x":1000},"scale":{"x":1},"contrast":1} -duration=5000 -target=figure01 -ease=easeIn -parallel; +;演示参数解耦改动后setTempAnimation普通运作是否正常 +setTempAnimation:[{"duration":0}, {"duration":500,"position":{"x":-1000}}, {"duration":500,"position":{"y":720},"scale":{"y":0.5},"saturation":0}, {"duration":500,"position":{"x":-1000, "y":720}}, {"duration":500}, {"duration":500,"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5}] -target=figure01 +setTempAnimation:[{"duration":0}, {"duration":500,"position":{"x":1000}}, {"duration":500,"position":{"y":720}}, {"duration":500,"position":{"x":1000, "y":720}}, {"duration":500}, {"duration":500,"position":{"x":1000}}] -target=figure01 +;演示参数解耦改动后setTransform的-writeDefault参数是否运作正常 +setTransform:{} -writeDefault -target=figure01 -duration=500 +setTransform:{"position":{"x":1000,"y":720}} -target=figure01 -next; +setAnimation:shockwaveOut -target=figure01 -parallel +;演示setTempAnimation平行执行 +setTempAnimation:[{"duration":0},{"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5,"duration":5000,"ease":"easeOut"},{"position":{"x":-1000},"scale":{"x":0.5},"contrast":0.5,"duration":2000},{"position":{"x":600},"scale":{"x":1},"contrast":1,"duration":5000,"ease":"easeIn"},{"duration":2000}] -target=figure01 -next; +setTempAnimation:[{"duration":2000},{"position":{"y":0},"scale":{"y":0.5},"saturation":0,"duration":5000,"ease":"linear"},{"position":{"y":-720},"scale":{"y":1},"saturation":1,"duration":5000,"ease":"linear"},{"duration":2000}] -target=figure01 -parallel -continue; +;演示并行执行多条终止时间点不一致的setTransform +setTransform:{"position":{"x":-1000}} -duration=5000 -next -target=figure01; +setTransform:{"position":{"y":0}} -duration=3000 -parallel -target=figure01; +setTransform:{"position":{"x":1000}} -duration=3000 -next -target=figure01; +setTransform:{"position":{"y":-720}} -duration=5000 -parallel -target=figure01; +changeBg: -next; +changeFigure: -id=figure01 diff --git a/packages/webgal/src/Core/Modules/animationFunctions.ts b/packages/webgal/src/Core/Modules/animationFunctions.ts index f72cf0df5..9c303a227 100644 --- a/packages/webgal/src/Core/Modules/animationFunctions.ts +++ b/packages/webgal/src/Core/Modules/animationFunctions.ts @@ -7,6 +7,8 @@ import { baseTransform } from '@/store/stageInterface'; import { generateTimelineObj } from '@/Core/controller/stage/pixi/animations/timeline'; import { WebGAL } from '@/Core/WebGAL'; import PixiStage, { IAnimationObject } from '@/Core/controller/stage/pixi/PixiController'; +import { IUserAnimation } from './animations'; +import { pickBy } from 'lodash'; import { DEFAULT_BG_IN_DURATION, DEFAULT_BG_OUT_DURATION, @@ -19,12 +21,25 @@ import { stageActions } from '@/store/stageReducer'; export function getAnimationObject(animationName: string, target: string, duration: number, writeDefault: boolean) { const effect = WebGAL.animationManager.getAnimations().find((ani) => ani.name === animationName); if (effect) { + const unionKeys = new Set(); + const unionScaleKeys = new Set(); + const unionPositionKeys = new Set(); + effect.effects.forEach((effect) => { + Object.keys(effect).forEach((k) => unionKeys.add(k)); + if (effect.scale) Object.keys(effect.scale).forEach((k) => unionScaleKeys.add(k)); + if (effect.position) Object.keys(effect.position).forEach((k) => unionPositionKeys.add(k)); + }); const mappedEffects = effect.effects.map((effect) => { const targetSetEffect = webgalStore.getState().stage.effects.find((e) => e.target === target); let newEffect; if (!writeDefault && targetSetEffect && targetSetEffect.transform) { - newEffect = cloneDeep({ ...targetSetEffect.transform, duration: 0, ease: '' }); + const targetScale = pickBy(targetSetEffect.transform.scale || {}, (source, key) => unionScaleKeys.has(key)); + const targetPosition = pickBy(targetSetEffect.transform.position || {}, (s, key) => unionPositionKeys.has(key)); + const originalTransform = { ...pickBy(targetSetEffect.transform, (source, key) => unionKeys.has(key)) }; + originalTransform.scale = targetScale; + originalTransform.position = targetPosition; + newEffect = cloneDeep({ ...originalTransform, duration: 0, ease: '' }); } else { newEffect = cloneDeep({ ...baseTransform, duration: 0, ease: '' }); } diff --git a/packages/webgal/src/Core/Modules/perform/performController.ts b/packages/webgal/src/Core/Modules/perform/performController.ts index f303a64b8..43e24f44b 100644 --- a/packages/webgal/src/Core/Modules/perform/performController.ts +++ b/packages/webgal/src/Core/Modules/perform/performController.ts @@ -18,8 +18,17 @@ export const getRandomPerformName = (): string => { export class PerformController { public performList: Array = []; + /** + * 判断 perform 名称是否匹配(支持前缀匹配,用于清理并行演出) + * 并行演出的 performName 格式为 "baseName#uuid",匹配时需要同时命中精确匹配和前缀匹配 + */ + private matchPerformName(performName: string, name: string): boolean { + return performName === name || performName.startsWith(name + '#'); + } + public arrangeNewPerform(perform: IPerform, script: ISentence, syncPerformState = true) { // 检查演出列表内是否有相同的演出,如果有,一定是出了什么问题 + // 并行演出的 performName 带有唯一后缀,因此不会命中去重 const dupPerformIndex = this.performList.findIndex((p) => p.performName === perform.performName); if (dupPerformIndex > -1) { // 结束并删除全部重复演出 @@ -50,7 +59,7 @@ export class PerformController { // perform.isOver = true; if (!perform.isHoldOn) { // 如果不是保持演出,清除 - this.unmountPerform(perform.performName); + this.softUnmountPerformObject(perform); } }, perform.duration); @@ -64,7 +73,7 @@ export class PerformController { if (!force) { for (let i = 0; i < this.performList.length; i++) { const e = this.performList[i]; - if (!e.isHoldOn && e.performName === name) { + if (!e.isHoldOn && this.matchPerformName(e.performName, name)) { e.stopFunction(); clearTimeout(e.stopTimeout as unknown as number); /** @@ -85,15 +94,18 @@ export class PerformController { } else { for (let i = 0; i < this.performList.length; i++) { const e = this.performList[i]; - if (e.performName === name) { + if (this.matchPerformName(e.performName, name)) { e.stopFunction(); clearTimeout(e.stopTimeout as unknown as number); + /** + * 在演出列表里删除演出对象的操作必须在调用 goNextWhenOver 之前(同上) + */ + this.performList.splice(i, 1); + i--; if (e.goNextWhenOver) { // nextSentence(); this.goNextWhenOver(); } - this.performList.splice(i, 1); - i--; /** * 从状态表里清除演出 */ @@ -103,6 +115,25 @@ export class PerformController { } } + public softUnmountPerformObject(perform: IPerform) { + const idx = this.performList.indexOf(perform); + if (idx < 0) return; + perform.stopFunction(); + clearTimeout(perform.stopTimeout as unknown as number); + /** + * 在演出列表里删除演出对象的操作必须在调用 goNextWhenOver 之前 + * 因为 goNextWhenOver 会调用 nextSentence,而 nextSentence 会清除目前未结束的演出 + * 那么 nextSentence 函数就会删除这个演出,但是此时,在这个上下文,i 已经被确定了 + * 所以 goNextWhenOver 后的代码会多删东西,解决方法就是在调用 goNextWhenOver 前先删掉这个演出对象 + * 此问题对所有 goNextWhenOver 属性为真的演出都有影响,但只有 2 个演出有此问题 + */ + this.performList.splice(idx, 1); + if (perform.goNextWhenOver) { + // nextSentence(); + this.goNextWhenOver(); + } + } + public erasePerformFromState(name: string) { webgalStore.dispatch(stageActions.removePerformByName(name)); } diff --git a/packages/webgal/src/Core/Modules/perform/performInterface.ts b/packages/webgal/src/Core/Modules/perform/performInterface.ts index 8e00b2f90..2b6dda62d 100644 --- a/packages/webgal/src/Core/Modules/perform/performInterface.ts +++ b/packages/webgal/src/Core/Modules/perform/performInterface.ts @@ -23,6 +23,8 @@ export interface IPerform { arrangePerformPromise?: Promise; // 跳过由 nextSentence 函数引发的演出回收 skipNextCollect?: boolean; + // 标记此演出是否为并行演出,允许同一 target 上同时运行多个互不干扰的演出 + isParallel?: boolean; } // next之后,可以被打断的演出会被打断,不能被打断的演出会继续,阻塞next的演出会阻止next被响应。 diff --git a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts index 9bf9333ab..cee74fde8 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/PixiController.ts @@ -10,6 +10,8 @@ import { SCREEN_CONSTANTS } from '@/Core/util/constants'; import { logger } from '@/Core/util/logger'; import { v4 as uuid } from 'uuid'; import { cloneDeep, isEqual } from 'lodash'; +import omitBy from 'lodash/omitBy'; +import isUndefined from 'lodash/isUndefined'; import * as PIXI from 'pixi.js'; import { INSTALLED } from 'pixi.js'; import { GifResource } from './GifResource'; @@ -71,9 +73,9 @@ export default class PixiStage { if (!source) return; const targetScale = target.scale; const targetPosition = target.position; - if (target.scale) Object.assign(targetScale, source.scale); - if (target.position) Object.assign(targetPosition, source.position); - Object.assign(target, source); + if (target.scale) Object.assign(targetScale!, omitBy(source.scale || {}, isUndefined)); + if (target.position) Object.assign(targetPosition!, omitBy(source.position || {}, isUndefined)); + Object.assign(target, omitBy(source, isUndefined)); target.scale = targetScale; target.position = targetPosition; if (convertAlpha) { diff --git a/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts b/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts index 1cf2a38af..dbbe8c0ba 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/generateTransformAnimationObj.ts @@ -1,5 +1,6 @@ import { AnimationFrame } from '@/Core/Modules/animations'; import { webgalStore } from '@/store/store'; +import { has, pickBy } from 'lodash'; import isNull from 'lodash/isNull'; type AnimationObj = Array; @@ -10,6 +11,7 @@ export function generateTransformAnimationObj( applyFrame: AnimationFrame, duration: number | string | boolean | null, ease: string, + writeFullEffect = true, ): AnimationObj { let animationObj; // 获取那个 target 的当前变换 @@ -25,8 +27,21 @@ export function generateTransformAnimationObj( // 找到 effect if (targetEffect) { - const effectWithDuration = { ...targetEffect!.transform!, duration: 0, ease }; - animationObj.unshift(effectWithDuration); + if (writeFullEffect) { + const effectWithDuration = { ...targetEffect!.transform!, duration: 0, ease }; + animationObj.unshift(effectWithDuration); + } else { + const targetScale = pickBy(targetEffect.transform?.scale || {}, (source, key) => has(applyFrame.scale, key)); + const targetPosition = pickBy(targetEffect.transform?.position || {}, (sr, key) => has(applyFrame.position, key)); + const effectWithDuration = { + ...pickBy(targetEffect.transform || {}, (source, key) => has(applyFrame, key)), + duration: 0, + ease, + }; + effectWithDuration.scale = targetScale; + effectWithDuration.position = targetPosition; + animationObj.unshift(effectWithDuration); + } } else { // 应用默认effect,也就是最终的 effect 的 alpha = 0 版本 const effectWithDuration = { ...applyFrame, alpha: 0, duration: 0, ease }; diff --git a/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts b/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts index f0b7198b3..64477f3b2 100644 --- a/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts +++ b/packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts @@ -30,7 +30,7 @@ export function generateTimelineObj( currentDelay += segmentDuration; const { position, scale, ...segmentValues } = segment; // 不能用 scale,因为 popmotion 不能用嵌套 - values.push({ x: position.x, y: position.y, scaleX: scale.x, scaleY: scale.y, ...segmentValues }); + values.push({ x: position?.x, y: position?.y, scaleX: scale?.x, scaleY: scale?.y, ...segmentValues }); // Easing 需要比 values 的长度少一个 if (i > 0) { easeArray.push(stringToEasing(segment.ease)); @@ -71,11 +71,11 @@ export function generateTimelineObj( if (target?.pixiContainer) { // 不能赋值到 position,因为 x 和 y 被 WebGALPixiContainer 代理,而 position 属性没有代理 const { position, scale, ...state } = getStartStateEffect(); - const assignValue = omitBy({ x: position.x, y: position.y, ...state }, isUndefined); + const assignValue = omitBy({ x: position?.x, y: position?.y, ...state }, isUndefined); // @ts-ignore PixiStage.assignTransform(target?.pixiContainer, assignValue); - if (target?.pixiContainer) { - if (!isUndefined(scale.x)) { + if (scale && target?.pixiContainer) { + if (!isUndefined(scale?.x)) { target.pixiContainer.scale.x = scale.x; } if (!isUndefined(scale?.y)) { @@ -98,11 +98,11 @@ export function generateTimelineObj( // 不能赋值到 position,因为 x 和 y 被 WebGALPixiContainer 代理,而 position 属性没有代理 // 不能赋值到 position,因为 x 和 y 被 WebGALPixiContainer 代理,而 position 属性没有代理 const { position, scale, ...state } = getEndStateEffect(); - const assignValue = omitBy({ x: position.x, y: position.y, ...state }, isUndefined); + const assignValue = omitBy({ x: position?.x, y: position?.y, ...state }, isUndefined); // @ts-ignore PixiStage.assignTransform(target?.pixiContainer, assignValue); - if (target?.pixiContainer) { - if (!isUndefined(scale.x)) { + if (scale && target?.pixiContainer) { + if (!isUndefined(scale?.x)) { target.pixiContainer.scale.x = scale.x; } if (!isUndefined(scale?.y)) { diff --git a/packages/webgal/src/Core/gameScripts/setAnimation.ts b/packages/webgal/src/Core/gameScripts/setAnimation.ts index aa68a5bb4..ad8967f01 100644 --- a/packages/webgal/src/Core/gameScripts/setAnimation.ts +++ b/packages/webgal/src/Core/gameScripts/setAnimation.ts @@ -19,12 +19,14 @@ export const setAnimation = (sentence: ISentence): IPerform => { target = target !== '' ? target : 'default_id'; const writeDefault = getBooleanArgByKey(sentence, 'writeDefault') ?? false; const keep = getBooleanArgByKey(sentence, 'keep') ?? false; + const parallel = getBooleanArgByKey(sentence, 'parallel') ?? false; const key = `${target}-${animationName}-${animationDuration}`; const performInitName = `animation-${target}`; + const performName = parallel ? `${performInitName}#${animationName}` : performInitName; let keepAnimationStopped = false; - WebGAL.gameplay.performController.unmountPerform(performInitName, true); + if (!parallel) WebGAL.gameplay.performController.unmountPerform(performInitName, true); let stopFunction; setTimeout(() => { @@ -55,12 +57,13 @@ export const setAnimation = (sentence: ISentence): IPerform => { }; return { - performName: performInitName, + performName: performName, duration: animationDuration, isHoldOn: keep, stopFunction, blockingNext: () => false, blockingAuto: () => !keep, stopTimeout: undefined, // 暂时不用,后面会交给自动清除 + isParallel: parallel, }; }; diff --git a/packages/webgal/src/Core/gameScripts/setTempAnimation.ts b/packages/webgal/src/Core/gameScripts/setTempAnimation.ts index bd6f6964d..aea5bda54 100644 --- a/packages/webgal/src/Core/gameScripts/setTempAnimation.ts +++ b/packages/webgal/src/Core/gameScripts/setTempAnimation.ts @@ -10,13 +10,14 @@ import { baseTransform } from '@/store/stageInterface'; import { IUserAnimation } from '../Modules/animations'; import { getAnimateDuration, getAnimationObject } from '@/Core/Modules/animationFunctions'; import { WebGAL } from '@/Core/WebGAL'; +import { v4 as uuid } from 'uuid'; /** * 设置临时动画 * @param sentence */ export const setTempAnimation = (sentence: ISentence): IPerform => { - const animationName = (Math.random() * 10).toString(16); + const animationName = uuid(); const animationString = sentence.content; let animationObj; try { @@ -30,12 +31,14 @@ export const setTempAnimation = (sentence: ISentence): IPerform => { const target = getStringArgByKey(sentence, 'target') ?? '0'; const writeDefault = getBooleanArgByKey(sentence, 'writeDefault') ?? false; const keep = getBooleanArgByKey(sentence, 'keep') ?? false; + const parallel = getBooleanArgByKey(sentence, 'parallel') ?? false; const key = `${target}-${animationName}-${animationDuration}`; const performInitName = `animation-${target}`; + const performName = parallel ? `${performInitName}#${animationName}` : performInitName; let keepAnimationStopped = false; - WebGAL.gameplay.performController.unmountPerform(performInitName, true); + if (!parallel) WebGAL.gameplay.performController.unmountPerform(performInitName, true); let stopFunction = () => {}; setTimeout(() => { @@ -66,12 +69,13 @@ export const setTempAnimation = (sentence: ISentence): IPerform => { }; return { - performName: performInitName, + performName: performName, duration: animationDuration, isHoldOn: keep, stopFunction, blockingNext: () => false, blockingAuto: () => !keep, stopTimeout: undefined, // 暂时不用,后面会交给自动清除 + isParallel: parallel, }; }; diff --git a/packages/webgal/src/Core/gameScripts/setTransform.ts b/packages/webgal/src/Core/gameScripts/setTransform.ts index 5a53042ae..3268c8adf 100644 --- a/packages/webgal/src/Core/gameScripts/setTransform.ts +++ b/packages/webgal/src/Core/gameScripts/setTransform.ts @@ -11,13 +11,13 @@ import { AnimationFrame, IUserAnimation } from '../Modules/animations'; import { generateTransformAnimationObj } from '@/Core/controller/stage/pixi/animations/generateTransformAnimationObj'; import { WebGAL } from '@/Core/WebGAL'; import { getAnimateDuration, getAnimationObject } from '../Modules/animationFunctions'; - +import { v4 as uuid } from 'uuid'; /** * 设置变换 * @param sentence */ export const setTransform = (sentence: ISentence): IPerform => { - const animationName = (Math.random() * 10).toString(16); + const animationName = uuid(); const animationString = sentence.content; let animationObj: AnimationFrame[]; @@ -26,14 +26,17 @@ export const setTransform = (sentence: ISentence): IPerform => { const writeDefault = getBooleanArgByKey(sentence, 'writeDefault') ?? false; const target = getStringArgByKey(sentence, 'target') ?? '0'; const keep = getBooleanArgByKey(sentence, 'keep') ?? false; + const parallel = getBooleanArgByKey(sentence, 'parallel') ?? false; const performInitName = `animation-${target}`; + const performName = parallel ? `${performInitName}#${animationName}` : performInitName; - WebGAL.gameplay.performController.unmountPerform(performInitName, true); + if (!parallel) WebGAL.gameplay.performController.unmountPerform(performInitName, true); try { const frame = JSON.parse(animationString) as AnimationFrame; - animationObj = generateTransformAnimationObj(target, frame, duration, ease); + // writeDefault时需要完整的当前effect,其他时候不需要 + animationObj = generateTransformAnimationObj(target, frame, duration, ease, writeDefault); console.log('animationObj:', animationObj); } catch (e) { // 解析都错误了,歇逼吧 @@ -43,7 +46,6 @@ export const setTransform = (sentence: ISentence): IPerform => { const newAnimation: IUserAnimation = { name: animationName, effects: animationObj }; WebGAL.animationManager.addAnimation(newAnimation); const animationDuration = getAnimateDuration(animationName); - const key = `${target}-${animationName}-${animationDuration}`; let keepAnimationStopped = false; setTimeout(() => { @@ -74,12 +76,13 @@ export const setTransform = (sentence: ISentence): IPerform => { }; return { - performName: performInitName, + performName: performName, duration: animationDuration, isHoldOn: keep, stopFunction, blockingNext: () => false, blockingAuto: () => !keep, stopTimeout: undefined, // 暂时不用,后面会交给自动清除 + isParallel: parallel, }; }; diff --git a/packages/webgal/src/Stage/MainStage/useSetEffects.ts b/packages/webgal/src/Stage/MainStage/useSetEffects.ts index c5a1790ad..8a096d11c 100644 --- a/packages/webgal/src/Stage/MainStage/useSetEffects.ts +++ b/packages/webgal/src/Stage/MainStage/useSetEffects.ts @@ -2,6 +2,7 @@ import { baseTransform, IEffect, IStageState, ITransform } from '@/store/stageIn import { WebGAL } from '@/Core/WebGAL'; import PixiStage from '@/Core/controller/stage/pixi/PixiController'; +import { isUndefined, omitBy } from 'lodash'; export function setStageObjectEffects(stageState: IStageState) { const effects = stageState.effects; @@ -42,5 +43,5 @@ function convertTransform(transform: ITransform | undefined) { return {}; } const { position, ...rest } = transform; - return { ...rest, x: position.x, y: position.y }; + return omitBy({ ...rest, x: position?.x, y: position?.y }, isUndefined); } diff --git a/packages/webgal/src/store/stageInterface.ts b/packages/webgal/src/store/stageInterface.ts index fd319d71f..995b455ba 100644 --- a/packages/webgal/src/store/stageInterface.ts +++ b/packages/webgal/src/store/stageInterface.ts @@ -25,47 +25,47 @@ export interface IChooseItem { } export interface ITransform { - alpha: number; - scale: { - x: number; - y: number; + alpha?: number; + scale?: { + x?: number; + y?: number; }; // pivot: { // x: number; // y: number; // }; - position: { - x: number; - y: number; + position?: { + x?: number; + y?: number; }; - rotation: number; - blur: number; - brightness: number; - contrast: number; - saturation: number; - gamma: number; - colorRed: number; - colorGreen: number; - colorBlue: number; - bevel: number; - bevelThickness: number; - bevelRotation: number; - bevelSoftness: number; - bevelRed: number; - bevelGreen: number; - bevelBlue: number; - bloom: number; - bloomBrightness: number; - bloomBlur: number; - bloomThreshold: number; - oldFilm: number; - dotFilm: number; - reflectionFilm: number; - glitchFilm: number; - rgbFilm: number; - godrayFilm: number; - shockwaveFilter: number; - radiusAlphaFilter: number; + rotation?: number; + blur?: number; + brightness?: number; + contrast?: number; + saturation?: number; + gamma?: number; + colorRed?: number; + colorGreen?: number; + colorBlue?: number; + bevel?: number; + bevelThickness?: number; + bevelRotation?: number; + bevelSoftness?: number; + bevelRed?: number; + bevelGreen?: number; + bevelBlue?: number; + bloom?: number; + bloomBrightness?: number; + bloomBlur?: number; + bloomThreshold?: number; + oldFilm?: number; + dotFilm?: number; + reflectionFilm?: number; + glitchFilm?: number; + rgbFilm?: number; + godrayFilm?: number; + shockwaveFilter?: number; + radiusAlphaFilter?: number; } /** diff --git a/packages/webgal/src/store/stageReducer.ts b/packages/webgal/src/store/stageReducer.ts index 57e1ffe42..cb3600945 100644 --- a/packages/webgal/src/store/stageReducer.ts +++ b/packages/webgal/src/store/stageReducer.ts @@ -24,6 +24,7 @@ import cloneDeep from 'lodash/cloneDeep'; import { commandType } from '@/Core/controller/scene/sceneInterface'; import { STAGE_KEYS } from '@/Core/constants'; import { baseBlinkParam, baseFocusParam } from '@/Core/live2DCore'; +import { isUndefined, omitBy } from 'lodash'; // 初始化舞台数据 @@ -124,12 +125,22 @@ const stageSlice = createSlice({ const effectIndex = state.effects.findIndex((e) => e.target === target); if (effectIndex >= 0) { // Update the existing effect - state.effects[effectIndex].transform = transform; + if (!state.effects[effectIndex].transform) { + state.effects[effectIndex].transform = transform; + } else if (transform) { + const targetScale = state.effects[effectIndex].transform!.scale || {}; + const targetPosition = state.effects[effectIndex].transform!.position || {}; + if (transform.scale) Object.assign(targetScale, omitBy(transform.scale, isUndefined)); + if (transform.position) Object.assign(targetPosition, omitBy(transform.position, isUndefined)); + Object.assign(state.effects[effectIndex].transform!, omitBy(transform, isUndefined)); + state.effects[effectIndex].transform!.scale = targetScale; + state.effects[effectIndex].transform!.position = targetPosition; + } } else { - // Add a new effect + // Add a new effect, use baseTransform as default to ensure completeness state.effects.push({ target, - transform, + transform: transform ? { ...baseTransform, ...transform } : { ...baseTransform }, }); } }, @@ -202,9 +213,10 @@ const stageSlice = createSlice({ state.PerformList.push(action.payload); }, removePerformByName: (state, action: PayloadAction) => { + const name = action.payload; for (let i = 0; i < state.PerformList.length; i++) { const performItem: IRunPerform = state.PerformList[i]; - if (performItem.id === action.payload) { + if (performItem.id === name || performItem.id.startsWith(name + '#')) { state.PerformList.splice(i, 1); i--; }