Skip to content

Commit b6e347c

Browse files
committed
chore: 插件更新
1 parent 6c7f4e2 commit b6e347c

File tree

6 files changed

+452
-2
lines changed

6 files changed

+452
-2
lines changed

‎example/App.vue‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
</template>
1010

1111
<script setup lang="ts">
12-
import WaterfallLayout from './components/WaterfallLayout.vue'
12+
// import WaterfallLayout from './components/WaterfallLayout.vue'
13+
import WaterfallLayout from './components/WaterfallVirtualList.vue'
1314
</script>
1415

1516
<style>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<div style="min-height: 100%; width:100%">
3+
<WaterfallVirtual :list="list">
4+
<template #default="{ item, url, index }">
5+
<div>dwad</div>
6+
</template>
7+
</WaterfallVirtual>
8+
</div>
9+
</template>
10+
11+
<script setup lang="ts">
12+
import { onMounted, ref } from 'vue'
13+
import { getList } from '../api'
14+
import { WaterfallVirtual } from '../../lib/index'
15+
16+
const sizeMap = [
17+
{ width: 400, height: 800 },
18+
{ width: 300, height: 800 },
19+
{ width: 400, height: 500 },
20+
{ width: 400, height: 400 },
21+
]
22+
23+
// 随机返回一个尺寸对象
24+
function getRandomSize() {
25+
const index = Math.floor(Math.random() * sizeMap.length)
26+
return sizeMap[index]
27+
}
28+
29+
const list = ref([])
30+
onMounted(async() => {
31+
const res = await getList({
32+
page: 0,
33+
pageSize: 200,
34+
})
35+
36+
list.value = res.map((item) => {
37+
return {
38+
...item,
39+
size: getRandomSize(),
40+
}
41+
})
42+
})
43+
44+
</script>
45+
46+
<style scoped>
47+
48+
</style>
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
<!--
2+
* @Description:
3+
* @Version: 2.0
4+
* @Author: Yaowen Liu
5+
* @Date: 2021-10-14 10:20:21
6+
* @LastEditors: Yaowen Liu
7+
* @LastEditTime: 2023-08-23 13:19:44
8+
-->
9+
<template>
10+
<div ref="waterfallWrapper" class="waterfall-list" :style="{ height: `${wrapperHeight}px` }">
11+
<div
12+
v-for="(item, index) in list"
13+
:key="getKey(item, index)"
14+
class="waterfall-item"
15+
>
16+
<div class="waterfall-card">
17+
<slot :item="item" :index="index" :url="getRenderURL(item)" />
18+
<slot name="item" :item="item" :index="index" :url="getRenderURL(item)" />
19+
</div>
20+
</div>
21+
</div>
22+
</template>
23+
24+
<script lang="ts">
25+
import type { PropType } from 'vue'
26+
import { defineComponent, provide, ref, watch } from 'vue'
27+
import { useDebounceFn } from '@vueuse/core'
28+
import { useCalculateCols, useLayoutVirtual } from '../use'
29+
import Lazy from '../utils/Lazy'
30+
import type { LazyType } from '../types/lazy'
31+
import { getValue } from '../utils/util'
32+
import type { ViewCard } from '../types/waterfall'
33+
34+
export default defineComponent({
35+
props: {
36+
list: {
37+
type: Array as PropType<ViewCard[]>,
38+
default: () => [],
39+
},
40+
rowKey: {
41+
type: String,
42+
default: 'id',
43+
},
44+
imgSelector: {
45+
type: String,
46+
default: 'src',
47+
},
48+
width: {
49+
type: Number,
50+
default: 200,
51+
},
52+
breakpoints: {
53+
type: Object,
54+
default: () => ({
55+
1200: {
56+
// when wrapper width < 1200
57+
rowPerView: 3,
58+
},
59+
800: {
60+
// when wrapper width < 800
61+
rowPerView: 2,
62+
},
63+
500: {
64+
// when wrapper width < 500
65+
rowPerView: 1,
66+
},
67+
}),
68+
},
69+
gutter: {
70+
type: Number,
71+
default: 10,
72+
},
73+
space: {
74+
type: Number,
75+
default: null,
76+
},
77+
hasAroundGutter: {
78+
type: Boolean,
79+
default: true,
80+
},
81+
posDuration: {
82+
type: Number,
83+
default: 300,
84+
},
85+
animationPrefix: {
86+
type: String,
87+
default: 'animate__animated',
88+
},
89+
animationEffect: {
90+
type: String,
91+
default: 'fadeIn',
92+
},
93+
animationDuration: {
94+
type: Number,
95+
default: 1000,
96+
},
97+
animationDelay: {
98+
type: Number,
99+
default: 300,
100+
},
101+
animationCancel: {
102+
type: Boolean,
103+
default: false,
104+
},
105+
backgroundColor: {
106+
type: String,
107+
default: '#fff',
108+
},
109+
lazyload: {
110+
type: Boolean,
111+
default: true,
112+
},
113+
loadProps: {
114+
type: Object,
115+
default: () => { },
116+
},
117+
crossOrigin: {
118+
type: Boolean,
119+
default: true,
120+
},
121+
delay: {
122+
type: Number,
123+
default: 300,
124+
},
125+
align: {
126+
type: String,
127+
default: 'center',
128+
},
129+
horizontalOrder: {
130+
type: Boolean,
131+
default: false,
132+
},
133+
heightDifference: {
134+
type: Number,
135+
default: 0,
136+
},
137+
},
138+
139+
setup(props, ctx) {
140+
const lazy: LazyType = new Lazy(props.lazyload, props.loadProps, props.crossOrigin)
141+
provide('lazy', lazy)
142+
143+
// 容器块信息
144+
const {
145+
waterfallWrapper,
146+
wrapperWidth,
147+
colWidth,
148+
cols,
149+
offsetX,
150+
} = useCalculateCols(props)
151+
152+
// 容器高度,块定位
153+
const { wrapperHeight, layoutHandle } = useLayoutVirtual(
154+
props,
155+
colWidth,
156+
cols,
157+
offsetX,
158+
waterfallWrapper,
159+
props.horizontalOrder,
160+
props.heightDifference,
161+
)
162+
163+
// 用单层防抖包住全部逻辑
164+
const renderer = useDebounceFn(() => {
165+
// 等一帧,让浏览器先应用新的 colWidth / wrapperWidth
166+
requestAnimationFrame(() => {
167+
layoutHandle().then(() => ctx.emit('afterRender'))
168+
// 🟢 关键:延时再执行一次修正布局
169+
setTimeout(() => {
170+
layoutHandle()
171+
}, props.posDuration + 50) // 延迟略大于动画时间
172+
})
173+
}, props.delay)
174+
175+
// 监听 wrapperWidth、colWidth、list
176+
watch(
177+
() => [wrapperWidth.value, colWidth.value, props.list],
178+
() => {
179+
if (wrapperWidth.value > 0) renderer()
180+
},
181+
{ deep: true },
182+
)
183+
184+
// 尺寸宽度变化防抖触发
185+
const sizeChangeTime = ref(0)
186+
187+
provide('sizeChangeTime', sizeChangeTime)
188+
189+
// 图片加载完成
190+
provide('imgLoaded', renderer)
191+
192+
// 根据选择器获取图片地址
193+
const getRenderURL = (item: ViewCard): string => {
194+
return getValue(item, props.imgSelector)[0]
195+
}
196+
197+
// 获取唯一值
198+
const getKey = (item: ViewCard, index: number): string => {
199+
return item[props.rowKey] || index
200+
}
201+
202+
return {
203+
colWidth,
204+
waterfallWrapper,
205+
wrapperHeight,
206+
getRenderURL,
207+
getKey,
208+
renderer,
209+
}
210+
},
211+
})
212+
</script>
213+
214+
<style scoped>
215+
.waterfall-list {
216+
width: 100%;
217+
position: relative;
218+
overflow: hidden;
219+
background-color: v-bind(backgroundColor);
220+
}
221+
.waterfall-item {
222+
position: absolute;
223+
left: 0;
224+
top: 0;
225+
/* 初始位置设置到屏幕以外,避免懒加载失败 */
226+
transform: translate3d(0, 3000px, 0);
227+
visibility: hidden;
228+
}
229+
230+
/* 初始的入场效果 */
231+
@-webkit-keyframes fadeIn {
232+
0% {
233+
opacity: 0;
234+
}
235+
100% {
236+
opacity: 1;
237+
}
238+
}
239+
@keyframes fadeIn {
240+
0% {
241+
opacity: 0;
242+
}
243+
100% {
244+
opacity: 1;
245+
}
246+
}
247+
.fadeIn {
248+
-webkit-animation-name: fadeIn;
249+
animation-name: fadeIn;
250+
}
251+
</style>

‎lib/index.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import Waterfall from './components/Waterfall.vue'
22
import LazyImg from './components/LazyImg.vue'
3-
export { Waterfall, LazyImg }
3+
import WaterfallVirtual from './components/WaterfallVirtual.vue'
4+
export { WaterfallVirtual, Waterfall, LazyImg }

‎lib/use/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
*/
77
export { useCalculateCols } from './useCalculateCols'
88
export { useLayout } from './useLayout'
9+
export { useLayoutVirtual } from './useLayoutVirtual'

0 commit comments

Comments
 (0)