|
@@ -12,12 +12,21 @@
|
|
|
<script lang="ts">
|
|
|
import type { PropType } from 'vue';
|
|
|
|
|
|
- import { defineComponent, reactive, onMounted, ref, unref, onUnmounted, toRefs } from 'vue';
|
|
|
+ import {
|
|
|
+ defineComponent,
|
|
|
+ reactive,
|
|
|
+ onMounted,
|
|
|
+ ref,
|
|
|
+ unref,
|
|
|
+ onUnmounted,
|
|
|
+ toRef,
|
|
|
+ toRefs,
|
|
|
+ } from 'vue';
|
|
|
|
|
|
import { Skeleton } from 'ant-design-vue';
|
|
|
import { useRaf } from '/@/hooks/event/useRaf';
|
|
|
import { useTimeout } from '/@/hooks/core/useTimeout';
|
|
|
-
|
|
|
+ import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver';
|
|
|
interface State {
|
|
|
isInit: boolean;
|
|
|
loading: boolean;
|
|
@@ -30,7 +39,7 @@
|
|
|
// 等待时间,如果指定了时间,不论可见与否,在指定时间之后自动加载
|
|
|
timeout: {
|
|
|
type: Number as PropType<number>,
|
|
|
- default: 8000,
|
|
|
+ default: 0,
|
|
|
// default: 8000,
|
|
|
},
|
|
|
// 组件所在的视口,如果组件是在页面容器内滚动,视口就是该容器
|
|
@@ -40,6 +49,7 @@
|
|
|
>,
|
|
|
default: () => null,
|
|
|
},
|
|
|
+
|
|
|
// 预加载阈值, css单位
|
|
|
threshold: {
|
|
|
type: String as PropType<string>,
|
|
@@ -51,6 +61,7 @@
|
|
|
type: String as PropType<'vertical' | 'horizontal'>,
|
|
|
default: 'vertical',
|
|
|
},
|
|
|
+
|
|
|
// 包裹组件的外层容器的标签名
|
|
|
tag: {
|
|
|
type: String as PropType<string>,
|
|
@@ -62,20 +73,14 @@
|
|
|
default: 80,
|
|
|
},
|
|
|
|
|
|
- // // 是否在不可见的时候销毁
|
|
|
- // autoDestory: {
|
|
|
- // type: Boolean as PropType<boolean>,
|
|
|
- // default: false,
|
|
|
- // },
|
|
|
-
|
|
|
// transition name
|
|
|
transitionName: {
|
|
|
type: String as PropType<string>,
|
|
|
default: 'lazy-container',
|
|
|
},
|
|
|
},
|
|
|
- emits: ['before-init', 'init'],
|
|
|
- setup(props, { emit, slots }) {
|
|
|
+ emits: ['init'],
|
|
|
+ setup(props, { emit }) {
|
|
|
const elRef = ref<any>(null);
|
|
|
const state = reactive<State>({
|
|
|
isInit: false,
|
|
@@ -83,17 +88,10 @@
|
|
|
intersectionObserverInstance: null,
|
|
|
});
|
|
|
|
|
|
- immediateInit();
|
|
|
onMounted(() => {
|
|
|
+ immediateInit();
|
|
|
initIntersectionObserver();
|
|
|
});
|
|
|
- onUnmounted(() => {
|
|
|
- // Cancel the observation before the component is destroyed
|
|
|
- if (state.intersectionObserverInstance) {
|
|
|
- const el = unref(elRef);
|
|
|
- state.intersectionObserverInstance.unobserve(el.$el);
|
|
|
- }
|
|
|
- });
|
|
|
|
|
|
// If there is a set delay time, it will be executed immediately
|
|
|
function immediateInit() {
|
|
@@ -105,9 +103,6 @@
|
|
|
}
|
|
|
|
|
|
function init() {
|
|
|
- // At this point, the skeleton component is about to be switched
|
|
|
- emit('before-init');
|
|
|
- // At this point you can prepare to load the resources of the lazy-loaded component
|
|
|
state.loading = true;
|
|
|
|
|
|
requestAnimationFrameFn(() => {
|
|
@@ -120,9 +115,7 @@
|
|
|
// Prevent waiting too long without executing the callback
|
|
|
// Set the maximum waiting time
|
|
|
useTimeout(() => {
|
|
|
- if (state.isInit) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (state.isInit) return;
|
|
|
callback();
|
|
|
}, props.maxWaitingTime || 80);
|
|
|
|
|
@@ -132,12 +125,10 @@
|
|
|
}
|
|
|
|
|
|
function initIntersectionObserver() {
|
|
|
- const { timeout, direction, threshold, viewport } = props;
|
|
|
- if (timeout) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ const { timeout, direction, threshold } = props;
|
|
|
+ if (timeout) return;
|
|
|
// According to the scrolling direction to construct the viewport margin, used to load in advance
|
|
|
- let rootMargin;
|
|
|
+ let rootMargin: string = '0px';
|
|
|
switch (direction) {
|
|
|
case 'vertical':
|
|
|
rootMargin = `${threshold} 0px`;
|
|
@@ -146,35 +137,26 @@
|
|
|
rootMargin = `0px ${threshold}`;
|
|
|
break;
|
|
|
}
|
|
|
+
|
|
|
try {
|
|
|
- // Observe the intersection of the viewport and the component container
|
|
|
- state.intersectionObserverInstance = new window.IntersectionObserver(
|
|
|
- intersectionHandler,
|
|
|
- {
|
|
|
- rootMargin,
|
|
|
- root: viewport,
|
|
|
- threshold: [0, Number.MIN_VALUE, 0.01],
|
|
|
- }
|
|
|
- );
|
|
|
-
|
|
|
- const el = unref(elRef);
|
|
|
-
|
|
|
- state.intersectionObserverInstance.observe(el.$el);
|
|
|
+ const { stop, observer } = useIntersectionObserver({
|
|
|
+ rootMargin,
|
|
|
+ target: toRef(elRef.value, '$el'),
|
|
|
+ onIntersect: (entries: any[]) => {
|
|
|
+ const isIntersecting = entries[0].isIntersecting || entries[0].intersectionRatio;
|
|
|
+ if (isIntersecting) {
|
|
|
+ init();
|
|
|
+ if (observer) {
|
|
|
+ stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ root: toRef(props, 'viewport'),
|
|
|
+ });
|
|
|
} catch (e) {
|
|
|
init();
|
|
|
}
|
|
|
}
|
|
|
- // Cross-condition change handling function
|
|
|
- function intersectionHandler(entries: any[]) {
|
|
|
- const isIntersecting = entries[0].isIntersecting || entries[0].intersectionRatio;
|
|
|
- if (isIntersecting) {
|
|
|
- init();
|
|
|
- if (state.intersectionObserverInstance) {
|
|
|
- const el = unref(elRef);
|
|
|
- state.intersectionObserverInstance.unobserve(el.$el);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
return {
|
|
|
elRef,
|
|
|
...toRefs(state),
|