Initial commit of akmon project
This commit is contained in:
207
uni_modules/lime-transition/index.ts
Normal file
207
uni_modules/lime-transition/index.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
// @ts-nocheck
|
||||
import { raf } from '@/uni_modules/lime-shared/raf';
|
||||
// #ifndef UNI-APP-X
|
||||
import { watchEffect, ref, nextTick } from '@/uni_modules/lime-shared/vue'
|
||||
// #endif
|
||||
|
||||
export type TransitionEmitStatus = "before-enter" | "enter" | "after-enter" | "before-leave" | "leave" | "after-leave"
|
||||
export type TransitionStatus = '' | 'enter' | 'leave';
|
||||
export type UseTransitionOptions = {
|
||||
element ?: Ref<UniElement | null>,
|
||||
enterClass ?: string,
|
||||
enterActiveClass ?: string,
|
||||
enterToClass ?: string,
|
||||
leaveClass ?: string,
|
||||
leaveActiveClass ?: string,
|
||||
leaveToClass ?: string,
|
||||
appear ?: boolean,
|
||||
defaultName ?: string,
|
||||
name ?: () => string,
|
||||
visible ?: () => boolean,
|
||||
emits ?: (name : TransitionEmitStatus) => void,
|
||||
onNextTick ?: (name : TransitionEmitStatus) => Promise<void>,
|
||||
duration ?: number
|
||||
}
|
||||
|
||||
type ClassNameMap = Map<string, string>;
|
||||
|
||||
export type UseTransitionReturn = {
|
||||
state : Ref<boolean>,
|
||||
display : Ref<boolean>,
|
||||
inited : Ref<boolean>,
|
||||
classes : Ref<string>,
|
||||
name : Ref<string>,
|
||||
finished : () => void,
|
||||
toggle : (v : boolean) => void,
|
||||
}
|
||||
|
||||
export function useTransition(options : UseTransitionOptions) : UseTransitionReturn {
|
||||
const state = ref(false);
|
||||
const display = ref(false);
|
||||
const inited = ref(false);
|
||||
const classes = ref('');
|
||||
const name = ref(options.defaultName ?? 'fade');
|
||||
|
||||
const enterClass = options.enterClass ?? '';
|
||||
const enterActiveClass = options.enterActiveClass ?? '';
|
||||
const enterToClass = options.enterToClass ?? '';
|
||||
const leaveActiveClass = options.leaveActiveClass ?? '';
|
||||
const leaveToClass = options.leaveToClass ?? '';
|
||||
const leaveClass = options.leaveClass ?? '';
|
||||
|
||||
const appear = options.appear ?? false
|
||||
const duration = options.duration ?? 300;
|
||||
|
||||
let status : TransitionStatus = '';
|
||||
let isTransitionEnd = false;
|
||||
let isTransitioning = false;
|
||||
let timeoutId = -1
|
||||
|
||||
const emitEvent = (event : TransitionEmitStatus) => {
|
||||
options.emits?.(event);
|
||||
};
|
||||
|
||||
// 结束
|
||||
const finished = () => {
|
||||
if (isTransitionEnd) return;
|
||||
isTransitionEnd = true;
|
||||
emitEvent(`after-${status}`)
|
||||
if (display.value && !state.value) {
|
||||
display.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const sleep = () : Promise<void> => {
|
||||
return new Promise((resolve) => {
|
||||
nextTick(() => {
|
||||
raf(() => {
|
||||
// #ifdef APP-ANDROID || APP-IOS || APP-HARMONY
|
||||
if (options.element?.value != null) {
|
||||
options.element?.value?.getBoundingClientRectAsync()?.then(res => {
|
||||
resolve()
|
||||
})
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID || APP-IOS || APP-HARMONY
|
||||
resolve()
|
||||
// #endif
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const getClassNames = (name : string) : ClassNameMap => {
|
||||
return new Map<string, string>([
|
||||
['enter', `l-${name}-enter l-${name}-enter-active ${enterClass} ${enterActiveClass}`],
|
||||
['enter-to', `l-${name}-enter-to l-${name}-enter-active ${enterToClass} ${enterActiveClass}`],
|
||||
['leave', `l-${name}-leave l-${name}-leave-active ${leaveClass} ${leaveActiveClass}`],
|
||||
['leave-to', `l-${name}-leave-to l-${name}-leave-active ${leaveToClass} ${leaveActiveClass}`]
|
||||
])
|
||||
};
|
||||
|
||||
const transitionQueue = ref<TransitionStatus[]>([]);
|
||||
|
||||
const performTransition = async (newStatus : TransitionStatus, eventName : TransitionStatus) => {
|
||||
if (status == newStatus) return
|
||||
transitionQueue.value.push(newStatus);
|
||||
if (isTransitioning) return;
|
||||
isTransitioning = true;
|
||||
// 防止因结束 又切换为开始导致闪烁
|
||||
isTransitionEnd = true;
|
||||
while (transitionQueue.value.length > 0) {
|
||||
const currentStatus = transitionQueue.value.shift()!;
|
||||
status = currentStatus;
|
||||
emitEvent(`before-${eventName}`);
|
||||
|
||||
await sleep();
|
||||
await sleep();
|
||||
if (status != currentStatus) continue;
|
||||
|
||||
const classNames = getClassNames(name.value);
|
||||
inited.value = true;
|
||||
display.value = true;
|
||||
classes.value = classNames.get(eventName)!;
|
||||
emitEvent(eventName);
|
||||
|
||||
const executeAfterTick = options.onNextTick?.(eventName);
|
||||
if (executeAfterTick != null) {
|
||||
await executeAfterTick;
|
||||
}
|
||||
|
||||
await sleep();
|
||||
// #ifdef MP
|
||||
await sleep();
|
||||
await sleep();
|
||||
// #endif
|
||||
if (status != currentStatus) continue;
|
||||
classes.value = classNames.get(`${eventName}-to`)!;
|
||||
// isTransitionEnd = false;
|
||||
// 防止最后无法关闭
|
||||
if (status == 'leave') {
|
||||
setTimeout(() => {
|
||||
finished()
|
||||
}, duration)
|
||||
}
|
||||
|
||||
}
|
||||
// 不延迟 会导致快速切换时 因display none 闪烁
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => {
|
||||
if (transitionQueue.value.length == 0 && status == newStatus) {
|
||||
isTransitionEnd = false;
|
||||
}
|
||||
}, duration * 0.8)
|
||||
|
||||
isTransitioning = false;
|
||||
}
|
||||
// 定义进入过渡的函数
|
||||
const enter = () => {
|
||||
performTransition('enter', 'enter');
|
||||
}
|
||||
|
||||
|
||||
const leave = () => {
|
||||
performTransition('leave', 'leave');
|
||||
}
|
||||
|
||||
let init = false;
|
||||
watchEffect(() => {
|
||||
if (options.visible == null) return
|
||||
state.value = options.visible!();
|
||||
if (!appear && !init) {
|
||||
init = true
|
||||
return
|
||||
}
|
||||
|
||||
if (state.value) {
|
||||
enter()
|
||||
} else {
|
||||
leave()
|
||||
}
|
||||
})
|
||||
watchEffect(() => {
|
||||
if (options.name == null) return
|
||||
name.value = options.name!()
|
||||
})
|
||||
|
||||
const toggle = (v : boolean) => {
|
||||
state.value = v
|
||||
if (v) {
|
||||
enter()
|
||||
} else {
|
||||
leave()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
inited,
|
||||
display,
|
||||
classes,
|
||||
name,
|
||||
finished,
|
||||
toggle
|
||||
} as UseTransitionReturn
|
||||
}
|
||||
Reference in New Issue
Block a user