feat: 滚动条渐变

This commit is contained in:
unanmed 2025-02-22 21:30:55 +08:00
parent 6e990fa926
commit c5101c9b29
3 changed files with 96 additions and 20 deletions

View File

@ -19,9 +19,10 @@ import {
Transform
} from '@/core/render';
import { MotaOffscreenCanvas2D } from '@/core/fx/canvas2d';
import { hyper, Transition } from 'mutate-animate';
import { hyper, linear, Transition } from 'mutate-animate';
import { clamp } from 'lodash-es';
import { IActionEvent, IWheelEvent, MouseType } from '@/core/render/event';
import { transitioned } from '../use';
export const enum ScrollDirection {
Horizontal,
@ -61,7 +62,7 @@ const SCROLL_MIN_LENGTH = 20;
/** 滚动条图示的宽度 */
const SCROLL_WIDTH = 10;
/** 滚动条的颜色 */
const SCROLL_COLOR = '#ddd';
const SCROLL_COLOR = '255,255,255';
/**
* {@link ScrollProps} {@link ScrollExpose}
@ -100,11 +101,20 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
const content = ref<Container>();
const scroll = ref<Sprite>();
const scrollAlpha = transitioned(0.5, 100, linear())!;
const width = computed(() => props.loc[2] ?? 200);
const height = computed(() => props.loc[3] ?? 200);
const direction = computed(() =>
props.hor ? ScrollDirection.Horizontal : ScrollDirection.Vertical
);
const scrollColor = computed(
() => `rgba(${SCROLL_COLOR},${scrollAlpha.ref.value ?? 0.5})`
);
watch(scrollColor, () => {
scroll.value?.update();
});
/** 滚动内容的当前位置 */
let contentPos = 0;
@ -301,7 +311,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
const ctx = canvas.ctx;
ctx.lineCap = 'round';
ctx.lineWidth = 3;
ctx.strokeStyle = SCROLL_COLOR;
ctx.strokeStyle = scrollColor.value;
ctx.beginPath();
const scroll = transition.value.scroll;
if (direction.value === ScrollDirection.Horizontal) {
@ -435,6 +445,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
} else {
scrollMutate = true;
}
scrollAlpha.set(0.9);
};
const moveScroll = (ev: IActionEvent) => {
@ -461,6 +472,7 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
};
const upScroll = (ev: IActionEvent) => {
scrollAlpha.set(0.7);
if (!scrollMutate) return;
const pos = getPos(ev);
if (pos < scrollPos) {
@ -470,6 +482,14 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
}
};
const enter = () => {
scrollAlpha.set(0.7);
};
const leave = () => {
scrollAlpha.set(0.5);
};
//#endregion
onMounted(() => {
@ -508,6 +528,8 @@ export const Scroll = defineComponent<ScrollProps, {}, string, ScrollSlots>(
onDown={downScroll}
onUp={upScroll}
zIndex={10}
onEnter={enter}
onLeave={leave}
></sprite>
</container>
);

View File

@ -3,7 +3,6 @@ import { defineComponent } from 'vue';
import { SetupComponentOptions } from '../components';
import { ElementLocator } from '@/core/render';
import { Scroll } from '../components/scroll';
import { Page } from '../components/page';
export interface ILeftHeroStatus {
hp: number;
@ -171,21 +170,6 @@ export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
<container loc={p.loc}>
<g-rect loc={[0, 0, p.loc[2], p.loc[3]]} stroke></g-rect>
<Scroll loc={[0, 0, 180, 100]}></Scroll>
<Page loc={[0, 200, 180, 100]} pages={3}>
{(page: number) => {
switch (page) {
case 1: {
return <text text="测试"></text>;
}
case 2: {
return <text text="测试2"></text>;
}
case 3: {
return <text text="测试3"></text>;
}
}
}}
</Page>
</container>
);
};

View File

@ -1,4 +1,12 @@
import { onMounted, onUnmounted } from 'vue';
import { TimingFn, Transition } from 'mutate-animate';
import {
ComponentInternalInstance,
getCurrentInstance,
onMounted,
onUnmounted,
ref,
Ref
} from 'vue';
export const enum Orientation {
/** 横屏 */
@ -58,3 +66,65 @@ export function onLoaded(hook: () => void) {
hook();
}
}
export interface ITransitionedController {
readonly ref: Ref<number>;
readonly value: number;
set(value: number): void;
}
class RenderTransition implements ITransitionedController {
private static key: number = 0;
private readonly key: string = `$${RenderTransition.key++}`;
public readonly ref: Ref<number>;
set value(v: number) {
this.transition.transition(this.key, v);
}
get value() {
return this.transition.value[this.key];
}
constructor(
value: number,
public readonly transition: Transition,
public readonly time: number,
public readonly curve: TimingFn
) {
this.ref = ref(value);
transition.ticker.add(() => {
this.ref.value = transition.value[this.key];
});
}
set(value: number): void {
this.transition
.time(this.time)
.mode(this.curve)
.transition(this.key, value);
}
}
const transitionMap = new Map<ComponentInternalInstance, Transition>();
export function transitioned(
value: number,
time: number,
curve: TimingFn
): ITransitionedController | null {
const instance = getCurrentInstance();
if (!instance) return null;
if (!transitionMap.has(instance)) {
const tran = new Transition();
transitionMap.set(instance, tran);
onUnmounted(() => {
transitionMap.delete(instance);
tran.ticker.destroy();
});
}
const tran = transitionMap.get(instance);
if (!tran) return null;
return new RenderTransition(value, tran, time, curve);
}