mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-11 15:47:06 +08:00
feat: Scroll 组件
This commit is contained in:
parent
ac5eefdb02
commit
6238c8f00a
@ -2,7 +2,7 @@ import { isNil } from 'lodash-es';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { Ticker, TickerFn } from 'mutate-animate';
|
||||
import { Transform } from './transform';
|
||||
import { ITransformUpdatable, Transform } from './transform';
|
||||
import { logger } from '../common/logger';
|
||||
import { ElementNamespace, ComponentInternalInstance } from 'vue';
|
||||
import { transformCanvas } from './utils';
|
||||
@ -189,6 +189,7 @@ export interface ERenderItemEvent extends ERenderItemActionEvent {
|
||||
beforeRender: [transform: Transform];
|
||||
afterRender: [transform: Transform];
|
||||
destroy: [];
|
||||
transform: [item: RenderItem, transform: Transform];
|
||||
}
|
||||
|
||||
interface TickerDelegation {
|
||||
@ -211,7 +212,8 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
IRenderFrame,
|
||||
IRenderTickerSupport,
|
||||
IRenderChildable,
|
||||
IRenderVueSupport
|
||||
IRenderVueSupport,
|
||||
ITransformUpdatable
|
||||
{
|
||||
/** 渲染的全局ticker */
|
||||
static ticker: Ticker = new Ticker();
|
||||
@ -519,6 +521,8 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 功能方法
|
||||
|
||||
/**
|
||||
* 获取当前元素的绝对位置(不建议使用,因为应当很少会有获取绝对位置的需求)
|
||||
*/
|
||||
@ -535,6 +539,28 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取到可以包围这个元素的最小矩形
|
||||
*/
|
||||
getBoundingRect(): DOMRectReadOnly {
|
||||
if (this.type === 'absolute') {
|
||||
return new DOMRectReadOnly(0, 0, this.width, this.height);
|
||||
}
|
||||
const tran = this.transformFallThrough
|
||||
? this.fallTransform
|
||||
: this._transform;
|
||||
if (!tran) return new DOMRectReadOnly(0, 0, this.width, this.height);
|
||||
const [x1, y1] = tran.transformed(0, 0);
|
||||
const [x2, y2] = tran.transformed(this.width, 0);
|
||||
const [x3, y3] = tran.transformed(0, this.height);
|
||||
const [x4, y4] = tran.transformed(this.width, this.height);
|
||||
const left = Math.min(x1, x2, x3, x4);
|
||||
const right = Math.max(x1, x2, x3, x4);
|
||||
const top = Math.min(y1, y2, y3, y4);
|
||||
const bottom = Math.max(y1, y2, y3, y4);
|
||||
return new DOMRectReadOnly(left, top, right - left, bottom - top);
|
||||
}
|
||||
|
||||
update(item: RenderItem<any> = this): void {
|
||||
if (this.cacheDirty) return;
|
||||
this.cacheDirty = true;
|
||||
@ -542,6 +568,13 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
this.parent?.update(item);
|
||||
}
|
||||
|
||||
updateTransform() {
|
||||
this.update();
|
||||
this.emit('transform', this, this._transform);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 动画帧与 ticker
|
||||
|
||||
requestBeforeFrame(fn: () => void): void {
|
||||
@ -639,6 +672,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
this.checkRoot();
|
||||
this._root?.connect(this);
|
||||
this.canvases.forEach(v => v.activate());
|
||||
this._transform.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -653,6 +687,7 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
parent.requestSort();
|
||||
parent.update();
|
||||
this.canvases.forEach(v => v.deactivate());
|
||||
this._transform.bind();
|
||||
if (!success) return false;
|
||||
this._root?.disconnect(this);
|
||||
this._root = void 0;
|
||||
@ -1115,6 +1150,8 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
||||
this.emit('destroy');
|
||||
this.removeAllListeners();
|
||||
this.cache.delete();
|
||||
this.canvases.forEach(v => v.delete());
|
||||
this.canvases.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,6 @@ export const { createApp, render } = createRenderer<RenderItem, RenderItem>({
|
||||
|
||||
remove: function (el: RenderItem<ERenderItemEvent>): void {
|
||||
el.remove();
|
||||
el.destroy();
|
||||
},
|
||||
|
||||
createElement: function (
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { gameKey, Hotkey } from '@/core/main/custom/hotkey';
|
||||
import { Animation, Ticker, Transition } from 'mutate-animate';
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { ERenderItemEvent, RenderItem } from '../item';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
const ticker = new Ticker();
|
||||
|
||||
@ -60,3 +62,13 @@ export function useKey(noScope: boolean = false): KeyUsing {
|
||||
return [gameKey, sym];
|
||||
}
|
||||
}
|
||||
|
||||
export function onEvent<
|
||||
T extends ERenderItemEvent,
|
||||
K extends EventEmitter.EventNames<T>
|
||||
>(item: RenderItem<T>, key: K, listener: EventEmitter.EventListener<T, K>) {
|
||||
item.on(key, listener);
|
||||
onUnmounted(() => {
|
||||
item.off(key, listener);
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { mat3, ReadonlyMat3, ReadonlyVec3, vec2, vec3 } from 'gl-matrix';
|
||||
|
||||
export interface ITransformUpdatable {
|
||||
update(): void;
|
||||
updateTransform?(): void;
|
||||
}
|
||||
|
||||
export class Transform {
|
||||
@ -48,7 +48,7 @@ export class Transform {
|
||||
this.scaleX *= x;
|
||||
this.scaleY *= y;
|
||||
this.modified = true;
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,7 +59,7 @@ export class Transform {
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.modified = true;
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,7 +73,7 @@ export class Transform {
|
||||
this.rad -= n * Math.PI * 2;
|
||||
}
|
||||
this.modified = true;
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +84,7 @@ export class Transform {
|
||||
this.scaleX = x;
|
||||
this.scaleY = y;
|
||||
this.modified = true;
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,7 +95,7 @@ export class Transform {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.modified = true;
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +105,7 @@ export class Transform {
|
||||
mat3.rotate(this.mat, this.mat, rad - this.rad);
|
||||
this.rad = rad;
|
||||
this.modified = true;
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,7 +131,7 @@ export class Transform {
|
||||
mat3.fromValues(a, b, 0, c, d, 0, e, f, 1)
|
||||
);
|
||||
this.calAttributes();
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,7 +153,7 @@ export class Transform {
|
||||
) {
|
||||
mat3.set(this.mat, a, b, 0, c, d, 0, e, f, 1);
|
||||
this.calAttributes();
|
||||
this.bindedObject?.update();
|
||||
this.bindedObject?.updateTransform?.();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ElementLocator, Sprite } from '@/core/render';
|
||||
import { defineComponent, onMounted, ref, watch } from 'vue';
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
import { SetupComponentOptions } from './types';
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
|
||||
interface ProgressProps {
|
||||
/** 进度条的位置 */
|
||||
@ -20,23 +21,21 @@ const progressProps = {
|
||||
export const Progress = defineComponent<ProgressProps>(props => {
|
||||
const element = ref<Sprite>();
|
||||
|
||||
onMounted(() => {
|
||||
element.value?.setRenderFn(canvas => {
|
||||
const { ctx } = canvas;
|
||||
const width = props.loc[2] ?? 200;
|
||||
const height = props.loc[3] ?? 200;
|
||||
ctx.fillStyle = props.background ?? 'gray';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.fillStyle = props.success ?? 'green';
|
||||
ctx.fillRect(0, 0, width * props.progress, height);
|
||||
});
|
||||
});
|
||||
const render = (canvas: MotaOffscreenCanvas2D) => {
|
||||
const { ctx } = canvas;
|
||||
const width = props.loc[2] ?? 200;
|
||||
const height = props.loc[3] ?? 200;
|
||||
ctx.fillStyle = props.background ?? 'gray';
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
ctx.fillStyle = props.success ?? 'green';
|
||||
ctx.fillRect(0, 0, width * props.progress, height);
|
||||
};
|
||||
|
||||
watch(props, () => {
|
||||
element.value?.update();
|
||||
});
|
||||
|
||||
return () => {
|
||||
return <sprite ref={element} loc={props.loc}></sprite>;
|
||||
return <sprite ref={element} loc={props.loc} render={render}></sprite>;
|
||||
};
|
||||
}, progressProps);
|
||||
|
351
src/module/render/components/scroll.tsx
Normal file
351
src/module/render/components/scroll.tsx
Normal file
@ -0,0 +1,351 @@
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
reactive,
|
||||
ref,
|
||||
SlotsType,
|
||||
VNode,
|
||||
watch
|
||||
} from 'vue';
|
||||
import { SetupComponentOptions } from './types';
|
||||
import {
|
||||
Container,
|
||||
ContainerProps,
|
||||
ElementLocator,
|
||||
RenderItem,
|
||||
Sprite,
|
||||
SpriteProps
|
||||
} from '@/core/render';
|
||||
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
|
||||
import { hyper, Transition } from 'mutate-animate';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { IActionEvent, IWheelEvent, MouseType } from '@/core/render/event';
|
||||
|
||||
export const enum ScrollDirection {
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
|
||||
interface ScrollProps {
|
||||
direction: ScrollDirection;
|
||||
loc: ElementLocator;
|
||||
noscroll?: boolean;
|
||||
/**
|
||||
* 滚动到最下方(最右方)时的填充大小,如果默认的高度计算方式有误,
|
||||
* 那么可以调整此参数来修复错误
|
||||
*/
|
||||
padHeight?: number;
|
||||
}
|
||||
|
||||
type ScrollSlots = SlotsType<{
|
||||
default: () => VNode | VNode[];
|
||||
}>;
|
||||
|
||||
const scrollProps = {
|
||||
props: ['direction', 'noscroll']
|
||||
} satisfies SetupComponentOptions<ScrollProps, {}, string, ScrollSlots>;
|
||||
|
||||
/** 滚动条图示的最短长度 */
|
||||
const SCROLL_MIN_LENGTH = 20;
|
||||
/** 滚动条图示的宽度 */
|
||||
const SCROLL_WIDTH = 10;
|
||||
|
||||
export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
|
||||
(props, { slots }) => {
|
||||
const scrollProps: SpriteProps = reactive({
|
||||
loc: [0, 0, 0, 0]
|
||||
});
|
||||
const contentProps: ContainerProps = reactive({
|
||||
loc: [0, 0, 0, 0]
|
||||
});
|
||||
|
||||
const listenedChild: Set<RenderItem> = new Set();
|
||||
const areaMap: Map<RenderItem, [number, number]> = new Map();
|
||||
const content = ref<Container>();
|
||||
const scroll = ref<Sprite>();
|
||||
|
||||
const width = computed(() => props.loc[2] ?? 200);
|
||||
const height = computed(() => props.loc[3] ?? 200);
|
||||
|
||||
let showScroll = 0;
|
||||
let nowScroll = 0;
|
||||
let maxLength = 0;
|
||||
let scrollLength = SCROLL_MIN_LENGTH;
|
||||
|
||||
const transition = new Transition();
|
||||
transition.value.scroll = 0;
|
||||
transition.mode(hyper('sin', 'out')).absolute();
|
||||
|
||||
transition.ticker.add(() => {
|
||||
if (transition.value.scroll !== nowScroll) {
|
||||
showScroll = transition.value.scroll;
|
||||
scroll.value?.update();
|
||||
content.value?.update();
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.loc,
|
||||
value => {
|
||||
const width = value[2] ?? 200;
|
||||
const height = value[3] ?? 200;
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
props.loc = [0, height - SCROLL_WIDTH, width, SCROLL_WIDTH];
|
||||
} else {
|
||||
props.loc = [width - SCROLL_WIDTH, 0, SCROLL_WIDTH, height];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 滚动到目标值
|
||||
* @param time 动画时长
|
||||
*/
|
||||
const scrollTo = (y: number, time: number = 1) => {
|
||||
const target = clamp(y, 0, maxLength);
|
||||
transition.time(time).transition('scroll', target);
|
||||
nowScroll = y;
|
||||
};
|
||||
|
||||
/**
|
||||
* 计算一个元素会在画面上显示的区域
|
||||
*/
|
||||
const getArea = (item: RenderItem, rect: DOMRectReadOnly) => {
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
areaMap.set(item, [rect.left - width.value, rect.right]);
|
||||
} else {
|
||||
areaMap.set(item, [rect.top - height.value, rect.bottom]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查一个元素是否需要显示,不需要则隐藏
|
||||
*/
|
||||
const checkItem = (item: RenderItem) => {
|
||||
const area = areaMap.get(item);
|
||||
if (!area) {
|
||||
item.show();
|
||||
return;
|
||||
}
|
||||
const [min, max] = area;
|
||||
if (nowScroll > min - 10 && nowScroll < max + 10) {
|
||||
item.show();
|
||||
} else {
|
||||
item.hide();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 当一个元素的矩阵发生变换时执行,检查其显示区域
|
||||
*/
|
||||
const onTransform = (item: RenderItem) => {
|
||||
const rect = item.getBoundingRect();
|
||||
getArea(item, rect);
|
||||
checkItem(item);
|
||||
};
|
||||
|
||||
const updateScroll = () => {
|
||||
if (!content.value) return;
|
||||
let max = 0;
|
||||
listenedChild.forEach(v => v.off('transform', onTransform));
|
||||
listenedChild.clear();
|
||||
areaMap.clear();
|
||||
content.value.children.forEach(v => {
|
||||
const rect = v.getBoundingRect();
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
if (rect.right > max) {
|
||||
max = rect.right;
|
||||
}
|
||||
} else {
|
||||
if (rect.bottom > max) {
|
||||
max = rect.bottom;
|
||||
}
|
||||
}
|
||||
v.on('transform', onTransform);
|
||||
listenedChild.add(v);
|
||||
});
|
||||
maxLength = max + (props.padHeight ?? 0);
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
scrollLength = Math.max(
|
||||
SCROLL_MIN_LENGTH,
|
||||
(width.value / max) * width.value
|
||||
);
|
||||
const h = props.noscroll
|
||||
? height.value
|
||||
: height.value - SCROLL_WIDTH;
|
||||
contentProps.loc = [-showScroll, 0, width.value, h];
|
||||
} else {
|
||||
scrollLength = clamp(
|
||||
(height.value / max) * height.value,
|
||||
SCROLL_MIN_LENGTH,
|
||||
height.value - 10
|
||||
);
|
||||
const w = props.noscroll
|
||||
? width.value
|
||||
: width.value - SCROLL_WIDTH;
|
||||
contentProps.loc = [0, -showScroll, w, height.value];
|
||||
}
|
||||
scroll.value?.update();
|
||||
};
|
||||
|
||||
onUpdated(updateScroll);
|
||||
onMounted(updateScroll);
|
||||
onUnmounted(() => {
|
||||
listenedChild.forEach(v => v.off('transform', onTransform));
|
||||
});
|
||||
|
||||
const drawScroll = (canvas: MotaOffscreenCanvas2D) => {
|
||||
if (props.noscroll) return;
|
||||
const ctx = canvas.ctx;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineWidth = 6;
|
||||
ctx.strokeStyle = '#fff';
|
||||
ctx.beginPath();
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
ctx.moveTo(nowScroll + 5, 5);
|
||||
ctx.lineTo(nowScroll + scrollLength + 5, 5);
|
||||
} else {
|
||||
ctx.moveTo(5, nowScroll + 5);
|
||||
ctx.lineTo(5, nowScroll + scrollLength + 5);
|
||||
}
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
const wheel = (ev: IWheelEvent) => {
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
if (ev.wheelX !== 0) {
|
||||
scrollTo(nowScroll + ev.wheelX, 300);
|
||||
} else if (ev.wheelY !== 0) {
|
||||
scrollTo(nowScroll + ev.wheelY, 300);
|
||||
}
|
||||
} else {
|
||||
scrollTo(nowScroll + ev.wheelY, 300);
|
||||
}
|
||||
};
|
||||
|
||||
const getPos = (ev: IActionEvent) => {
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
return ev.offsetX;
|
||||
} else {
|
||||
return ev.offsetY;
|
||||
}
|
||||
};
|
||||
|
||||
let identifier: number = -1;
|
||||
let lastPos: number = 0;
|
||||
const down = (ev: IActionEvent) => {
|
||||
identifier = ev.identifier;
|
||||
lastPos = getPos(ev);
|
||||
};
|
||||
|
||||
const move = (ev: IActionEvent) => {
|
||||
if (ev.identifier !== identifier) return;
|
||||
let pos = 0;
|
||||
if (ev.touch) {
|
||||
pos = getPos(ev);
|
||||
} else {
|
||||
if (ev.buttons & MouseType.Left) {
|
||||
pos = getPos(ev);
|
||||
}
|
||||
}
|
||||
const movement = pos - lastPos;
|
||||
scrollTo(nowScroll + movement, 1);
|
||||
lastPos = pos;
|
||||
};
|
||||
|
||||
let scrollBefore = 0;
|
||||
let scrollIdentifier = -1;
|
||||
let scrollDownPos = 0;
|
||||
let scrollMutate = false;
|
||||
let scrollPin = 0;
|
||||
|
||||
/**
|
||||
* 获取点击滚动条时,垂直于滚动条方向的位置
|
||||
*/
|
||||
const getScrollPin = (ev: IActionEvent) => {
|
||||
if (props.direction === ScrollDirection.Horizontal) {
|
||||
return ev.absoluteY;
|
||||
} else {
|
||||
return ev.absoluteX;
|
||||
}
|
||||
};
|
||||
|
||||
const downScroll = (ev: IActionEvent) => {
|
||||
scrollBefore = nowScroll;
|
||||
scrollIdentifier = ev.identifier;
|
||||
const pos = getPos(ev);
|
||||
// 计算点击在了滚动条的哪个位置
|
||||
const sEnd = nowScroll + scrollLength;
|
||||
if (pos >= nowScroll && pos <= sEnd) {
|
||||
scrollDownPos = pos - nowScroll;
|
||||
scrollMutate = false;
|
||||
scrollPin = getScrollPin(ev);
|
||||
} else {
|
||||
scrollMutate = true;
|
||||
}
|
||||
};
|
||||
|
||||
const moveScroll = (ev: IActionEvent) => {
|
||||
if (ev.identifier !== scrollIdentifier) return;
|
||||
const pos = getPos(ev);
|
||||
const scrollPos = pos - scrollDownPos;
|
||||
let deltaPin = 0;
|
||||
let threshold = 0;
|
||||
if (ev.touch) {
|
||||
const pin = getScrollPin(ev);
|
||||
deltaPin = Math.abs(pin - scrollPin);
|
||||
threshold = 200;
|
||||
} else {
|
||||
const pin = getScrollPin(ev);
|
||||
deltaPin = Math.abs(pin - scrollPin);
|
||||
threshold = 100;
|
||||
}
|
||||
if (deltaPin > threshold) {
|
||||
scrollTo(scrollBefore, 1);
|
||||
} else {
|
||||
scrollTo(scrollPos, 1);
|
||||
}
|
||||
};
|
||||
|
||||
const upScroll = (ev: IActionEvent) => {
|
||||
if (!scrollMutate) return;
|
||||
const pos = getPos(ev);
|
||||
if (pos < nowScroll) {
|
||||
scrollTo(pos - 50);
|
||||
} else {
|
||||
scrollTo(pos + 50);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
scroll.value?.root?.on('move', move);
|
||||
scroll.value?.root?.on('move', moveScroll);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
scroll.value?.root?.off('move', move);
|
||||
scroll.value?.root?.off('move', moveScroll);
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<container loc={props.loc} onWheel={wheel}>
|
||||
<container {...contentProps} ref={content} onDown={down}>
|
||||
{slots.default()}
|
||||
</container>
|
||||
<sprite
|
||||
{...scrollProps}
|
||||
ref={scroll}
|
||||
render={drawScroll}
|
||||
onDown={downScroll}
|
||||
onUp={upScroll}
|
||||
></sprite>
|
||||
</container>
|
||||
);
|
||||
};
|
||||
},
|
||||
scrollProps
|
||||
);
|
@ -122,11 +122,7 @@ export const LeftStatusBar = defineComponent<StatusBarProps<ILeftHeroStatus>>(
|
||||
<image image={mdefIcon} loc={iconLoc(3)}></image>
|
||||
<text text={f(s.mdef)} loc={textLoc(3)} font={font1}></text>
|
||||
<image image={moneyIcon} loc={iconLoc(4)}></image>
|
||||
<text
|
||||
text={f(s.money)}
|
||||
loc={textLoc(4)}
|
||||
font={font1}
|
||||
></text>
|
||||
<text text={f(s.money)} loc={textLoc(4)} font={font1} />
|
||||
<image image={expIcon} loc={iconLoc(5)}></image>
|
||||
<text text={f(s.exp)} loc={textLoc(5)} font={font1}></text>
|
||||
<text
|
||||
|
Loading…
Reference in New Issue
Block a user