|
@@ -1,249 +0,0 @@
|
|
-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)
|
|
|
|
- }
|
|
|
|
-}
|
|
|