import { ref, getCurrentInstance, onMounted, toValue, computed, watch, onBeforeUnmount, onUnmounted, useSlots, reactive, Text, Comment } from 'vue' export function useMounted() { const isMounted = ref(false) // 获取当前组件的实例 const instance = getCurrentInstance() if (instance) { onMounted(() => { isMounted.value = true }, instance) } return isMounted } export function useSupported(callback) { const isMounted = useMounted() return computed(() => { // to trigger the ref isMounted.value return Boolean(callback()) }) } export function useSlotsExist(slotsName = 'default') { const slots = useSlots() // 获取当前组件的所有插槽 // 检查特定名称的插槽是否存在且不为空 const checkSlotsExist = (slotName) => { const slotsContent = slots[slotName]?.() const checkExist = (slotContent) => { if (slotContent.type === Comment) { return false } if (Array.isArray(slotContent.children) && !slotContent.children.length) { return false } if (slotContent.type !== Text) { return true } if (typeof slotContent.children === 'string') { return slotContent.children.trim() !== '' } } if (slotsContent && slotsContent?.length) { const result = slotsContent.some((slotContent) => { return checkExist(slotContent) }) return result } return false } if (Array.isArray(slotsName)) { const slotsExist = reactive({}) slotsName.forEach((slotName) => { const exist = computed(() => checkSlotsExist(slotName)) slotsExist[slotName] = exist // 将一个 ref 赋值给一个 reactive 属性时,该 ref 会自动解包 }) return slotsExist } else { return computed(() => checkSlotsExist(slotsName)) } } export function useMutationObserver( target, callback, options = {} ) { const isSupported = useSupported(() => window && 'MutationObserver' in window) const stopObservation = ref(false) let observer const targets = computed(() => { const targetsValue = toValue(target) if (targetsValue) { if (Array.isArray(targetsValue)) { return targetsValue.map((el) => toValue(el)).filter((el) => el) } else { return [targetsValue] } } return [] }) // 定义清理函数,用于断开 MutationObserver 的连接 const cleanup = () => { if (observer) { observer.disconnect() observer = undefined } } // 初始化 MutationObserver,开始观察目标元素 const observeElements = () => { if (isSupported.value && targets.value.length && !stopObservation.value) { observer = new MutationObserver(callback) if (observer?.observe) { targets.value.forEach((element) => observer.observe(element, options)) } } } // 监听 targets 的变化,当 targets 变化时,重新建立 MutationObserver 观察 watch( () => targets.value, () => { cleanup() observeElements() }, { immediate: true, // 立即触发回调,以便初始状态也被观察 flush: 'post' } ) const stop = () => { stopObservation.value = true cleanup() } const start = () => { stopObservation.value = false observeElements() } // 在组件卸载前清理 MutationObserver onBeforeUnmount(() => cleanup()) return { stop, start } } export function useEventListener(target, event, callback) { // 也可以用字符串形式的 CSS 选择器来寻找目标 DOM 元素 onMounted(() => target.addEventListener(event, callback)) onUnmounted(() => target.removeEventListener(event, callback)) } export function useResizeObserver( target, callback, options = {} ) { const isSupported = useSupported(() => window && 'ResizeObserver' in window) let observer const stopObservation = ref(false) const targets = computed(() => { const targetsValue = toValue(target) if (targetsValue) { if (Array.isArray(targetsValue)) { return targetsValue.map((el) => toValue(el)).filter((el) => el) } else { return [targetsValue] } } return [] }) // 定义清理函数,用于断开 ResizeObserver 的连接 const cleanup = () => { if (observer) { observer.disconnect() observer = undefined } } // 初始化 ResizeObserver,开始观察目标元素 const observeElements = () => { if (isSupported.value && targets.value.length && !stopObservation.value) { observer = new ResizeObserver(callback) if (observer?.observe) { targets.value.forEach((element) => observer.observe(element, options)) } } } // 监听 targets 的变化,当 targets 变化时,重新建立 ResizeObserver 观察 watch( () => targets.value, () => { cleanup() observeElements() }, { immediate: true, // 立即触发回调,以便初始状态也被观察 flush: 'post' } ) const stop = () => { stopObservation.value = true cleanup() } const start = () => { stopObservation.value = false observeElements() } // 在组件卸载前清理 ResizeObserver onBeforeUnmount(() => cleanup()) return { stop, start } } export function rafTimeout(fn, delay, interval = false) { let start = null // 记录动画开始的时间戳 function timeElapse(timestamp) { // 定义动画帧回调函数 /* timestamp参数:与 performance.now() 的返回值相同,它表示 requestAnimationFrame() 开始去执行回调函数的时刻 */ if (!start) { // 如果还没有开始时间,则以当前时间为开始时间 start = timestamp } const elapsed = timestamp - start if (elapsed >= delay) { try { fn() // 执行目标函数 } catch (error) { console.error('Error executing rafTimeout function:', error) } if (interval) { // 如果需要间隔执行,则重置开始时间并继续安排下一次动画帧 start = timestamp raf.id = requestAnimationFrame(timeElapse) } } else { raf.id = requestAnimationFrame(timeElapse) } } // 创建一个对象用于存储动画帧的 ID,并初始化动画帧 const raf = { id: requestAnimationFrame(timeElapse) } return raf } export function cancelRaf(raf = { id }) { if (raf && raf.id && typeof raf.id === 'number') { cancelAnimationFrame(raf.id) } else { console.warn('cancelRaf received an invalid id:', raf) } }