提交 6a9bd686 编写于 作者: V vben

chore: remove LazyContainer

上级 6890dd72
import type { Ref } from 'vue';
import { onBeforeUpdate, ref } from 'vue';
import { onBeforeUpdate, shallowRef } from 'vue';
export function useRefs(): [Ref<HTMLElement[]>, (index: number) => (el: HTMLElement) => void] {
const refs = ref([]) as Ref<HTMLElement[]>;
const refs = shallowRef([]) as Ref<HTMLElement[]>;
onBeforeUpdate(() => {
refs.value = [];
......
import { withInstall } from '/@/utils';
import collapseContainer from './src/collapse/CollapseContainer.vue';
import scrollContainer from './src/ScrollContainer.vue';
import lazyContainer from './src/LazyContainer.vue';
export const CollapseContainer = withInstall(collapseContainer);
export const ScrollContainer = withInstall(scrollContainer);
export const LazyContainer = withInstall(lazyContainer);
export * from './src/typing';
<template>
<transition-group
class="h-full w-full"
v-bind="$attrs"
ref="elRef"
:name="transitionName"
:tag="tag"
mode="out-in"
>
<div key="component" v-if="isInit">
<slot :loading="loading"></slot>
</div>
<div key="skeleton" v-else>
<slot name="skeleton" v-if="$slots.skeleton"></slot>
<Skeleton v-else />
</div>
</transition-group>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, reactive, onMounted, ref, toRef, toRefs } from 'vue';
import { useTimeoutFn } from '@vben/hooks';
import { Skeleton } from 'ant-design-vue';
import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver';
interface State {
isInit: boolean;
loading: boolean;
intersectionObserverInstance: IntersectionObserver | null;
}
const props = {
/**
* Waiting time, if the time is specified, whether visible or not, it will be automatically loaded after the specified time
*/
timeout: { type: Number },
/**
* The viewport where the component is located.
* If the component is scrolling in the page container, the viewport is the container
*/
viewport: {
type: (typeof window !== 'undefined' ? window.HTMLElement : Object) as PropType<HTMLElement>,
default: () => null,
},
/**
* Preload threshold, css unit
*/
threshold: { type: String, default: '0px' },
/**
* The scroll direction of the viewport, vertical represents the vertical direction, horizontal represents the horizontal direction
*/
direction: {
type: String,
default: 'vertical',
validator: (v) => ['vertical', 'horizontal'].includes(v),
},
/**
* The label name of the outer container that wraps the component
*/
tag: { type: String, default: 'div' },
maxWaitingTime: { type: Number, default: 80 },
/**
* transition name
*/
transitionName: { type: String, default: 'lazy-container' },
};
export default defineComponent({
name: 'LazyContainer',
components: { Skeleton },
inheritAttrs: false,
props,
emits: ['init'],
setup(props, { emit }) {
const elRef = ref();
const state = reactive<State>({
isInit: false,
loading: false,
intersectionObserverInstance: null,
});
onMounted(() => {
immediateInit();
initIntersectionObserver();
});
// If there is a set delay time, it will be executed immediately
function immediateInit() {
const { timeout } = props;
timeout &&
useTimeoutFn(() => {
init();
}, timeout);
}
function init() {
state.loading = true;
useTimeoutFn(() => {
if (state.isInit) return;
state.isInit = true;
emit('init');
}, props.maxWaitingTime || 80);
}
function initIntersectionObserver() {
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 = '0px';
switch (direction) {
case 'vertical':
rootMargin = `${threshold} 0px`;
break;
case 'horizontal':
rootMargin = `0px ${threshold}`;
break;
}
try {
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();
}
}
return {
elRef,
...toRefs(state),
};
},
});
</script>
import { Ref, watchEffect, ref } from 'vue';
interface IntersectionObserverProps {
target: Ref<Element | null | undefined>;
root?: Ref<any>;
onIntersect: IntersectionObserverCallback;
rootMargin?: string;
threshold?: number;
}
export function useIntersectionObserver({
target,
root,
onIntersect,
rootMargin = '0px',
threshold = 0.1,
}: IntersectionObserverProps) {
let cleanup = () => {};
const observer: Ref<Nullable<IntersectionObserver>> = ref(null);
const stopEffect = watchEffect(() => {
cleanup();
observer.value = new IntersectionObserver(onIntersect, {
root: root ? root.value : null,
rootMargin,
threshold,
});
const current = target.value;
current && observer.value.observe(current);
cleanup = () => {
if (observer.value) {
observer.value.disconnect();
target.value && observer.value.unobserve(target.value);
}
};
});
return {
observer,
stop: () => {
cleanup();
stopEffect();
},
};
}
import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal';
import { Modal, message as Message, notification } from 'ant-design-vue';
import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue';
import { NotificationArgsProps, ConfigProps } from 'ant-design-vue/lib/notification';
import { useI18n } from './useI18n';
import { isString } from '/@/utils/is';
......
......@@ -29,10 +29,6 @@ export default {
drawer: 'Drawer',
desc: 'Desc',
lazy: 'Lazy',
lazyBasic: 'Basic',
lazyTransition: 'Animation',
verify: 'Verify',
verifyDrag: 'Drag ',
verifyRotate: 'Picture Restore',
......
......@@ -28,10 +28,6 @@ export default {
drawer: '抽屉扩展',
desc: '详情组件',
lazy: '懒加载组件',
lazyBasic: '基础示例',
lazyTransition: '动画效果',
verify: '验证组件',
verifyDrag: '拖拽校验',
verifyRotate: '图片还原',
......
......@@ -470,33 +470,6 @@ const comp: AppRouteModule = {
},
},
{
path: 'lazy',
name: 'LazyDemo',
component: getParentLayout('LazyDemo'),
redirect: '/comp/lazy/basic',
meta: {
title: t('routes.demo.comp.lazy'),
},
children: [
{
path: 'basic',
name: 'BasicLazyDemo',
component: () => import('/@/views/demo/comp/lazy/index.vue'),
meta: {
title: t('routes.demo.comp.lazyBasic'),
},
},
{
path: 'transition',
name: 'BasicTransitionDemo',
component: () => import('/@/views/demo/comp/lazy/Transition.vue'),
meta: {
title: t('routes.demo.comp.lazyTransition'),
},
},
],
},
{
path: 'verify',
name: 'VerifyDemo',
......
<template>
<Card hoverable :style="{ width: '240px', background: '#fff' }">
<template #cover>
<img alt="example" src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png" />
</template>
<CardMeta title="懒加载组件" />
</Card>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Card } from 'ant-design-vue';
export default defineComponent({
components: { CardMeta: Card.Meta, Card },
setup() {
return {};
},
});
</script>
<template>
<PageWrapper title="懒加载自定义动画示例" content="懒加载组件显示动画">
<div class="lazy-base-demo-wrap">
<h1>向下滚动</h1>
<div class="lazy-base-demo-box">
<LazyContainer transitionName="custom">
<TargetContent />
</LazyContainer>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import TargetContent from './TargetContent.vue';
import { LazyContainer } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { LazyContainer, TargetContent, PageWrapper },
});
</script>
<style lang="less">
.lazy-base-demo {
&-wrap {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 50%;
height: 2000px;
margin: 20px auto;
background-color: @component-background;
text-align: center;
}
&-box {
width: 300px;
height: 300px;
}
h1 {
height: 1300px;
margin: 20px 0;
}
}
.custom-enter {
transform: scale(0.4) translate(100%);
opacity: 0;
}
.custom-enter-to {
opacity: 1;
}
.custom-enter-active {
position: absolute;
top: 0;
width: 100%;
transition: all 0.5s;
}
.custom-leave {
opacity: 1;
}
.custom-leave-to {
transform: scale(0.4) translate(-100%);
opacity: 0;
}
.custom-leave-active {
transition: all 0.5s;
}
</style>
<template>
<PageWrapper title="懒加载基础示例" content="向下滚动到可见区域才会加载组件">
<div class="lazy-base-demo-wrap">
<h1>向下滚动</h1>
<div class="lazy-base-demo-box">
<LazyContainer>
<TargetContent />
<template #skeleton>
<Skeleton :rows="10" />
</template>
</LazyContainer>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Skeleton } from 'ant-design-vue';
import TargetContent from './TargetContent.vue';
import { LazyContainer } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { LazyContainer, PageWrapper, TargetContent, Skeleton },
});
</script>
<style lang="less">
.lazy-base-demo {
&-wrap {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 50%;
height: 2000px;
margin: 20px auto;
background-color: @component-background;
text-align: center;
}
&-box {
width: 300px;
height: 300px;
}
h1 {
height: 1300px;
margin: 20px 0;
}
}
</style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册