refactor: 删除 mutate-animate 库

This commit is contained in:
unanmed 2026-10-29 20:35:56 +08:00
parent 410e0c2943
commit 67293bf652
11 changed files with 275 additions and 292 deletions

View File

@ -45,7 +45,6 @@
"lodash-es": "^4.17.23",
"lz-string": "^1.5.0",
"maxrects-packer": "^2.7.3",
"mutate-animate": "^1.4.2",
"ogg-opus-decoder": "^1.7.3",
"opus-decoder": "^0.7.11",
"vue": "^3.5.29"

View File

@ -13,7 +13,7 @@ import {
} from '@motajs/render';
// @ts-expect-error render/use.ts 按 D-54 留在实现层,本文件已移入系统层,按 D-68 暂以标注记录,不反向引用、不解耦
import { transitionedColor } from '../use';
import { linear } from 'mutate-animate';
import { linear } from '@motajs/animate';
import { SetupComponentOptions } from '@motajs/system';
export interface InputProps extends DefaultProps, Partial<TextContentProps> {

View File

@ -26,7 +26,7 @@ import {
IRenderItem,
ICustomContainer
} from '@motajs/render';
import { hyper, linear, Transition } from 'mutate-animate';
import { cosh, linear, Transition } from '@motajs/animate';
import { clamp } from 'lodash-es';
// @ts-expect-error render/use.ts 按 D-54 留在实现层,本文件已移入系统层,按 D-68 暂以标注记录,不反向引用、不解耦
import { transitioned } from '../use';
@ -148,7 +148,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
const transition = new Transition();
transition.value.scroll = 0;
transition.value.showScroll = 0;
transition.mode(hyper('sin', 'out')).absolute();
transition.mode(cosh('sin', 'out')).absolute();
//#region 滚动操作

View File

@ -3,7 +3,7 @@ import { logger } from '@motajs/common';
import { computed, defineComponent, onUnmounted, ref } from 'vue';
// @ts-expect-error render/use.ts 按 D-54 留在实现层,本文件已移入系统层,按 D-68 暂以标注记录,不反向引用、不解耦
import { transitioned } from '../use';
import { hyper } from 'mutate-animate';
import { cosh } from '@motajs/animate';
import { debounce } from 'lodash-es';
import { texture } from '../elements';
import { SetupComponentOptions } from '@motajs/system';
@ -45,7 +45,7 @@ export const Tip = defineComponent<TipProps>((props, { expose }) => {
const font = new Font('normal');
const alpha = transitioned(0, 500, hyper('sin', 'in-out'))!;
const alpha = transitioned(0, 500, cosh())!;
const pad = computed(() => props.pad ?? [4, 4]);
const locHeight = computed(() => props.loc[3] ?? 200);
const hidden = computed(() => alpha.ref.value === 0);

View File

@ -5,7 +5,7 @@ import {
} from '@user/data-base';
import { IMapDoorRenderer } from './types';
import { IMapRenderer } from '../types';
import { sleep } from 'mutate-animate';
import { sleep } from '@motajs/common';
import { DOOR_ANIMATE_INTERVAL } from '../../shared';
export class MapDoorRenderer implements IMapDoorRenderer {

View File

@ -1,183 +1,185 @@
import { KeyCode } from '@motajs/client-base';
import { Hotkey, HotkeyData } from '@motajs/system';
// @ts-expect-error 需要重构
import { HeroMover, IMoveController } from '@user/data-state';
import { Ticker } from 'mutate-animate';
import { mainScope } from './hotkey';
// TODO: 需要重构
// import { KeyCode } from '@motajs/client-base';
// import { Hotkey, HotkeyData } from '@motajs/system';
// // @ts-expect-error 需要重构
// import { HeroMover, IMoveController } from '@user/data-state';
// import { mainScope } from './hotkey';
type MoveKey = Record<Dir, HotkeyData>;
type MoveKeyConfig = Record<Dir, string>;
// type MoveKey = Record<Dir, HotkeyData>;
// type MoveKeyConfig = Record<Dir, string>;
export class HeroKeyMover {
/** 当前按下的键 */
private pressedKey: Set<Dir> = new Set();
/** 当前的移动方向 */
private moveDir: Dir = 'down';
/** 当前是否正在使用按键移动 */
private moving: boolean = false;
/** 当前移动的控制器 */
private controller?: IMoveController;
// export class HeroKeyMover {
// /** 当前按下的键 */
// private pressedKey: Set<Dir> = new Set();
// /** 当前的移动方向 */
// private moveDir: Dir = 'down';
// /** 当前是否正在使用按键移动 */
// private moving: boolean = false;
// /** 当前移动的控制器 */
// private controller?: IMoveController;
/** 按键接续ticker */
private ticker = new Ticker();
// /** 按键接续ticker */
// private ticker = new Ticker();
/** 当前移动实例绑定的热键 */
hotkey: Hotkey;
/** 当前热键的移动按键信息 */
hotkeyData: MoveKey;
/** 移动实例 */
mover: HeroMover;
/** 移动可触发的作用域 */
scope: symbol = mainScope;
// /** 当前移动实例绑定的热键 */
// hotkey: Hotkey;
// /** 当前热键的移动按键信息 */
// hotkeyData: MoveKey;
// /** 移动实例 */
// mover: HeroMover;
// /** 移动可触发的作用域 */
// scope: symbol = mainScope;
constructor(hotkey: Hotkey, mover: HeroMover, config?: MoveKeyConfig) {
this.hotkey = hotkey;
this.mover = mover;
hotkey.on('press', this.onPressKey);
hotkey.on('release', this.onReleaseKey);
// constructor(hotkey: Hotkey, mover: HeroMover, config?: MoveKeyConfig) {
// this.hotkey = hotkey;
// this.mover = mover;
// hotkey.on('press', this.onPressKey);
// hotkey.on('release', this.onReleaseKey);
const data = hotkey.data;
// const data = hotkey.data;
this.hotkeyData = {
left: data[config?.left ?? 'moveLeft'],
right: data[config?.right ?? 'moveRight'],
up: data[config?.up ?? 'moveUp'],
down: data[config?.down ?? 'moveDown']
};
// this.hotkeyData = {
// left: data[config?.left ?? 'moveLeft'],
// right: data[config?.right ?? 'moveRight'],
// up: data[config?.up ?? 'moveUp'],
// down: data[config?.down ?? 'moveDown']
// };
// 静止时尝试启动移动
this.ticker.add(() => {
if (!this.moving) {
if (this.pressedKey.size > 0) {
const dir = [...this.pressedKey].at(-1);
if (!dir) return;
this.moveDir = dir;
this.tryStartMove();
}
}
});
}
// // 静止时尝试启动移动
// this.ticker.add(() => {
// if (!this.moving) {
// if (this.pressedKey.size > 0) {
// const dir = [...this.pressedKey].at(-1);
// if (!dir) return;
// this.moveDir = dir;
// this.tryStartMove();
// }
// }
// });
// }
/**
* 按键移动
* @param code 按键码
*/
private onPressKey = (code: KeyCode) => {
if (core.isReplaying() || !core.isPlaying()) return;
core.waitHeroToStop();
if (code === this.hotkeyData.left.key) this.press('left');
else if (code === this.hotkeyData.right.key) this.press('right');
else if (code === this.hotkeyData.up.key) this.press('up');
else if (code === this.hotkeyData.down.key) this.press('down');
};
// /**
// * 按键移动
// * @param code 按键码
// */
// private onPressKey = (code: KeyCode) => {
// if (core.isReplaying() || !core.isPlaying()) return;
// core.waitHeroToStop();
// if (code === this.hotkeyData.left.key) this.press('left');
// else if (code === this.hotkeyData.right.key) this.press('right');
// else if (code === this.hotkeyData.up.key) this.press('up');
// else if (code === this.hotkeyData.down.key) this.press('down');
// };
/**
* 释放按键
* @param code 按键码
*/
private onReleaseKey = (code: KeyCode) => {
if (code === this.hotkeyData.left.key) this.release('left');
else if (code === this.hotkeyData.right.key) this.release('right');
else if (code === this.hotkeyData.up.key) this.release('up');
else if (code === this.hotkeyData.down.key) this.release('down');
};
// /**
// * 释放按键
// * @param code 按键码
// */
// private onReleaseKey = (code: KeyCode) => {
// if (code === this.hotkeyData.left.key) this.release('left');
// else if (code === this.hotkeyData.right.key) this.release('right');
// else if (code === this.hotkeyData.up.key) this.release('up');
// else if (code === this.hotkeyData.down.key) this.release('down');
// };
/**
* 设置按键触发作用域
*/
setScope(scope: symbol) {
this.scope = scope;
}
// /**
// * 设置按键触发作用域
// */
// setScope(scope: symbol) {
// this.scope = scope;
// }
/**
* 按下某个方向键
* @param dir 移动方向
*/
press(dir: Dir) {
if (this.hotkey.scope !== this.scope || core.status.lockControl) return;
this.pressedKey.add(dir);
this.moveDir = dir;
if (!this.moving) {
this.tryStartMove();
}
}
// /**
// * 按下某个方向键
// * @param dir 移动方向
// */
// press(dir: Dir) {
// if (this.hotkey.scope !== this.scope || core.status.lockControl) return;
// this.pressedKey.add(dir);
// this.moveDir = dir;
// if (!this.moving) {
// this.tryStartMove();
// }
// }
/**
* 松开方向键
* @param dir 移动方向
*/
release(dir: Dir) {
this.pressedKey.delete(dir);
if (this.pressedKey.size > 0) {
this.moveDir = [...this.pressedKey][0];
} else {
this.endMove();
}
}
// /**
// * 松开方向键
// * @param dir 移动方向
// */
// release(dir: Dir) {
// this.pressedKey.delete(dir);
// if (this.pressedKey.size > 0) {
// this.moveDir = [...this.pressedKey][0];
// } else {
// this.endMove();
// }
// }
/**
* 尝试开始移动
* @returns 是否成功开始移动
*/
tryStartMove() {
if (this.moving || core.status.lockControl) return false;
// /**
// * 尝试开始移动
// * @returns 是否成功开始移动
// */
// tryStartMove() {
// if (this.moving || core.status.lockControl) return false;
this.mover.oneStep(this.moveDir);
const controller = this.mover.startMove(false, false, false, true);
if (!controller) return false;
// this.mover.oneStep(this.moveDir);
// const controller = this.mover.startMove(false, false, false, true);
// if (!controller) return false;
this.controller = controller;
controller.onEnd.then(() => {
this.moving = false;
this.controller = void 0;
this.mover.off('stepEnd', this.onStepEnd);
});
this.moving = true;
// this.controller = controller;
// controller.onEnd.then(() => {
// this.moving = false;
// this.controller = void 0;
// this.mover.off('stepEnd', this.onStepEnd);
// });
// this.moving = true;
this.mover.on('stepEnd', this.onStepEnd);
return true;
}
// this.mover.on('stepEnd', this.onStepEnd);
// return true;
// }
/**
* 停止本次按键移动
*/
endMove() {
this.controller?.stop();
}
// /**
// * 停止本次按键移动
// */
// endMove() {
// this.controller?.stop();
// }
/**
* 移动结束
*/
private onStepEnd = () => {
const con = this.controller;
if (!con) return;
// /**
// * 移动结束
// */
// private onStepEnd = () => {
// const con = this.controller;
// if (!con) return;
// 被禁止操作时
if (core.status.lockControl) {
con.stop();
return;
}
// // 被禁止操作时
// if (core.status.lockControl) {
// con.stop();
// return;
// }
// 未移动时
if (!this.moving) {
con.stop();
return;
}
// // 未移动时
// if (!this.moving) {
// con.stop();
// return;
// }
// 尝试移动
if (this.pressedKey.size > 0) {
if (con.queue.length === 0) {
con.push({ type: 'dir', value: this.moveDir });
}
} else {
con.stop();
}
};
// // 尝试移动
// if (this.pressedKey.size > 0) {
// if (con.queue.length === 0) {
// con.push({ type: 'dir', value: this.moveDir });
// }
// } else {
// con.stop();
// }
// };
destroy() {
this.hotkey.off('press', this.onPressKey);
this.hotkey.off('release', this.onReleaseKey);
this.mover.off('stepEnd', this.onStepEnd);
this.ticker.destroy();
}
}
// destroy() {
// this.hotkey.off('press', this.onPressKey);
// this.hotkey.off('release', this.onReleaseKey);
// this.mover.off('stepEnd', this.onStepEnd);
// this.ticker.destroy();
// }
// }
export {};

View File

@ -7,7 +7,6 @@ import * as RenderVue from '@motajs/render-vue';
import * as System from '@motajs/system';
import * as UserClientBase from '@user/client-base';
import * as ClientModules from '@user/client-modules';
import * as MutateAnimate from 'mutate-animate';
import * as Vue from 'vue';
import * as Lodash from 'lodash-es';
@ -20,7 +19,6 @@ export function create() {
Mota.register('@motajs/system', System);
Mota.register('@user/client-base', UserClientBase);
Mota.register('@user/client-modules', ClientModules);
Mota.register('MutateAnimate', MutateAnimate);
Mota.register('Vue', Vue);
Mota.register('Lodash', Lodash);
}

View File

@ -13,7 +13,6 @@ import type * as DataBase from '@user/data-base';
import type * as DataSystem from '@user/data-system';
import type * as DataState from '@user/data-state';
// ---------- 必要的第三方库
import type * as MutateAnimate from 'mutate-animate';
import type * as Vue from 'vue';
import type * as Lodash from 'lodash-es';
@ -33,7 +32,6 @@ interface ModuleInterface {
'@user/data-system': typeof DataSystem;
'@user/data-state': typeof DataState;
// ---------- 必要的第三方库
MutateAnimate: typeof MutateAnimate;
Vue: typeof Vue;
Lodash: typeof Lodash;
}

View File

@ -208,9 +208,6 @@ importers:
maxrects-packer:
specifier: ^2.7.3
version: 2.7.3
mutate-animate:
specifier: ^1.4.2
version: 1.4.2
ogg-opus-decoder:
specifier: ^1.7.3
version: 1.7.3
@ -4966,9 +4963,6 @@ packages:
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
mutate-animate@1.4.2:
resolution: {integrity: sha512-Ioy3fMZ+lW3ua+BKsbfM8S8dCpGxKucVnM+Llq+rCuhnVrv8rOPXDJ2qe/vk5FemFFp9lmtw1QPsFNx8sE0VAA==}
nan@2.25.0:
resolution: {integrity: sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==}
@ -11349,8 +11343,6 @@ snapshots:
muggle-string@0.4.1: {}
mutate-animate@1.4.2: {}
nan@2.25.0: {}
nanoid@3.3.11: {}

View File

@ -1,6 +1,6 @@
import { GameUI, SetupComponentOptions } from '@motajs/system';
import { computed, defineComponent, ref } from 'vue';
import { Font } from '@motajs/render';
import { ElementLocator, Font } from '@motajs/render';
import { Scroll, TextContent } from '@user/client-base';
import { IHeroItemState, IHeroState } from '@user/data-base';
import { IHeroAttr } from '@user/data-common';
@ -100,6 +100,11 @@ export const ToolBox = defineComponent<ToolBoxProps>(props => {
/** 关闭当前道具栏界面 */
const exit = () => props.controller.close(props.instance);
/** 生成道具行的点击处理器,点击后按再点规则选中或使用该道具 */
const getItemClick = (item: IHeroItemState<IHeroAttr>) => () => {
clickItem(item);
};
return () => {
const current = selectedItem.value;
return (
@ -121,110 +126,102 @@ export const ToolBox = defineComponent<ToolBoxProps>(props => {
fillStyle="gold"
/>
<Scroll noscroll loc={[0, LIST_TOP, COLUMN_WIDTH, LIST_HEIGHT]}>
{/* 每行行底、图标、名称与数量,直接作为 Scroll 子项 */}
{permItems.value.map((item, i) => {
const y = i * ROW_HEIGHT;
const isSelected = item.num === selected.value;
return (
<container
key={item.num}
loc={[
0,
i * ROW_HEIGHT,
COLUMN_WIDTH,
ROW_HEIGHT
]}
const cellLoc: ElementLocator = [
4,
y + 2,
COLUMN_WIDTH - 8,
ROW_HEIGHT - 4
];
return [
<g-rectr
loc={cellLoc}
circle={[4]}
fill
fillStyle={
isSelected
? 'rgba(255, 204, 0, 0.28)'
: 'rgba(255, 255, 255, 0.05)'
}
cursor="pointer"
onClick={() => clickItem(item)}
>
<g-rectr
loc={[
4,
2,
COLUMN_WIDTH - 8,
ROW_HEIGHT - 4
]}
circle={[4]}
fill
fillStyle={
isSelected
? 'rgba(255, 204, 0, 0.28)'
: 'rgba(255, 255, 255, 0.05)'
}
/>
<icon
icon={item.num}
loc={[10, 7, ROW_ICON_SIZE, ROW_ICON_SIZE]}
/>
<text
text={item.raw.name}
loc={[50, ROW_HEIGHT / 2]}
anc={[0, 0.5]}
font={itemFont}
fillStyle="#eeeeee"
/>
<text
text={core.formatBigNumber(item.count)}
loc={[COLUMN_WIDTH - 8, ROW_HEIGHT / 2]}
anc={[1, 0.5]}
font={itemFont}
fillStyle="#cccccc"
/>
</container>
);
onClick={getItemClick(item)}
/>,
<icon
icon={item.num}
loc={[10, y + 7, ROW_ICON_SIZE, ROW_ICON_SIZE]}
noevent
/>,
<text
text={item.raw.name}
loc={[50, y + ROW_HEIGHT / 2]}
anc={[0, 0.5]}
font={itemFont}
fillStyle="#eeeeee"
noevent
/>,
<text
text={core.formatBigNumber(item.count)}
loc={[COLUMN_WIDTH - 8, y + ROW_HEIGHT / 2]}
anc={[1, 0.5]}
font={itemFont}
fillStyle="#cccccc"
noevent
/>
];
})}
</Scroll>
<Scroll
noscroll
loc={[COLUMN_WIDTH, LIST_TOP, COLUMN_WIDTH, LIST_HEIGHT]}
>
{/* 每行行底、图标、名称与数量,直接作为 Scroll 子项 */}
{consItems.value.map((item, i) => {
const y = i * ROW_HEIGHT;
const isSelected = item.num === selected.value;
return (
<container
key={item.num}
loc={[
0,
i * ROW_HEIGHT,
COLUMN_WIDTH,
ROW_HEIGHT
]}
const cellLoc: ElementLocator = [
4,
y + 2,
COLUMN_WIDTH - 8,
ROW_HEIGHT - 4
];
return [
<g-rectr
loc={cellLoc}
circle={[4]}
fill
fillStyle={
isSelected
? 'rgba(255, 204, 0, 0.28)'
: 'rgba(255, 255, 255, 0.05)'
}
cursor="pointer"
onClick={() => clickItem(item)}
>
<g-rectr
loc={[
4,
2,
COLUMN_WIDTH - 8,
ROW_HEIGHT - 4
]}
circle={[4]}
fill
fillStyle={
isSelected
? 'rgba(255, 204, 0, 0.28)'
: 'rgba(255, 255, 255, 0.05)'
}
/>
<icon
icon={item.num}
loc={[10, 7, ROW_ICON_SIZE, ROW_ICON_SIZE]}
/>
<text
text={item.raw.name}
loc={[50, ROW_HEIGHT / 2]}
anc={[0, 0.5]}
font={itemFont}
fillStyle="#eeeeee"
/>
<text
text={core.formatBigNumber(item.count)}
loc={[COLUMN_WIDTH - 8, ROW_HEIGHT / 2]}
anc={[1, 0.5]}
font={itemFont}
fillStyle="#cccccc"
/>
</container>
);
onClick={getItemClick(item)}
/>,
<icon
icon={item.num}
loc={[10, y + 7, ROW_ICON_SIZE, ROW_ICON_SIZE]}
noevent
/>,
<text
text={item.raw.name}
loc={[50, y + ROW_HEIGHT / 2]}
anc={[0, 0.5]}
font={itemFont}
fillStyle="#eeeeee"
noevent
/>,
<text
text={core.formatBigNumber(item.count)}
loc={[COLUMN_WIDTH - 8, y + ROW_HEIGHT / 2]}
anc={[1, 0.5]}
font={itemFont}
fillStyle="#cccccc"
noevent
/>
];
})}
</Scroll>
{/* 道具名称 */}
@ -236,28 +233,25 @@ export const ToolBox = defineComponent<ToolBoxProps>(props => {
fillStyle="gold"
/>
{/* 道具说明 */}
<text
text="未选择道具"
loc={[INFO_LEFT + 8, DESC_TOP + 4]}
anc={[0, 0]}
font={descFont}
fillStyle="#888888"
hidden={!!current}
/>
<Scroll
noscroll
loc={[INFO_LEFT, DESC_TOP, COLUMN_WIDTH, DESC_HEIGHT]}
>
{!current && (
<text
text="未选择道具"
loc={[8, 4]}
anc={[0, 0]}
font={descFont}
fillStyle="#888888"
/>
)}
{current && (
<TextContent
text={current.raw.text}
width={COLUMN_WIDTH - 16}
loc={[8, 4, COLUMN_WIDTH - 16, DESC_HEIGHT - 8]}
font={descFont}
fillStyle="#dddddd"
/>
)}
<TextContent
text={current ? current.raw.text : ''}
width={COLUMN_WIDTH - 16}
loc={[8, 4, COLUMN_WIDTH - 16, DESC_HEIGHT - 8]}
font={descFont}
fillStyle="#dddddd"
/>
</Scroll>
{/* 右下角退出按钮 */}
<text

View File

@ -283,8 +283,8 @@ interface Utils {
formatBigNumber(x: number | string, onMap?: number | boolean): string;
/**
* @deprecated 可使用,考虑换用 `mutate-animate` 库\
* 变速移动,完全可以用mutate-animate代替
* @deprecated 可使用,考虑换用 `@motajs/animate` 库\
* 变速移动,完全可以用@motajs/animate代替
* @param mode 缓动模式
*/
applyEasing(mode?: EaseMode): (x: number) => number;