import { DefaultProps } from '@motajs/render-vue'; import { SetupComponentOptions } from '@motajs/system-ui'; import { clamp, isNil } from 'lodash-es'; import { computed, defineComponent, onMounted, ref, watch } from 'vue'; import { Scroll, ScrollExpose } from './scroll'; import { Font } from '@motajs/render-style'; import { MotaOffscreenCanvas2D } from '@motajs/render-core'; export interface FloorSelectorProps extends DefaultProps { floors: FloorIds[]; now?: number; } export type FloorSelectorEmits = { /** * 点击关闭按钮时触发 */ close: () => void; /** * 当选中的楼层改变时触发 * @param floor 楼层索引 * @param floorId 楼层 id */ update: (floor: number, floorId: FloorIds) => void; 'update:now': (value: number) => void; }; const floorSelectorProps = { props: ['floors', 'now'], emits: ['close', 'update', 'update:now'] } satisfies SetupComponentOptions< FloorSelectorProps, FloorSelectorEmits, keyof FloorSelectorEmits >; export const FloorSelector = defineComponent< FloorSelectorProps, FloorSelectorEmits, keyof FloorSelectorEmits >((props, { emit }) => { const listFont = new Font(Font.defaultFamily, 12); /** 当前选中楼层,不反向 */ const now = ref(props.now ?? 0); /** 当前鼠标选中楼层,反向 */ const selList = ref(0); const scrollRef = ref(); const floors = computed(() => props.floors.toReversed()); const floorId = computed(() => floors.value[now.value]); const floorName = computed(() => core.floors[floorId.value].title); watch( () => props.now, value => { if (!isNil(value)) { changeTo(value); } } ); let gradient: CanvasGradient | null = null; const getGradient = (ctx: CanvasRenderingContext2D) => { if (gradient) return gradient; gradient = ctx.createLinearGradient(0, 0, 0, 200); gradient.addColorStop(0, 'rgba(255,255,255,0)'); gradient.addColorStop(0.2, 'rgba(255,255,255,1)'); gradient.addColorStop(0.8, 'rgba(255,255,255,1)'); gradient.addColorStop(1, 'rgba(255,255,255,0)'); return gradient; }; const renderMask = (canvas: MotaOffscreenCanvas2D) => { const { ctx } = canvas; const gradient = getGradient(ctx); ctx.fillStyle = gradient; ctx.fillRect(0, 0, 144, 200); }; const changeTo = (index: number, time: number = 500) => { // 取反是为了符合 2.x 的操作习惯 const res = clamp(index, 0, floors.value.length - 1); const reversed = floors.value.length - res - 1; now.value = res; selList.value = reversed; const y = reversed * 24; scrollRef.value?.scrollTo(y, time); emit('update', now.value, floorId.value); emit('update:now', now.value); }; const changeFloor = (delta: number) => { changeTo(now.value + delta); }; const enterList = (index: number) => { selList.value = index; }; const close = () => { emit('close'); }; onMounted(() => { changeTo(now.value, 0); }); return () => ( changeFloor(10)} /> changeFloor(1)} /> changeFloor(-1)} /> changeFloor(-10)} /> {floors.value.map((v, i, a) => { const floor = core.floors[v]; const reversed = a.length - i - 1; const nowFloor = a.length - now.value - 1; const highlight = nowFloor === i || selList.value === i; const color = highlight ? '#fff' : '#aaa'; const fill = highlight ? '#fff' : '#000'; return ( enterList(i)} onLeave={() => enterList(nowFloor)} onClick={() => changeTo(reversed)} /> ); })} ); }, floorSelectorProps);