123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- 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)
- }
- }
|