-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix(component): 素材瀑布流布局样式 * fix(component): 新增空瀑布流组件 * feat(component): 瀑布流增加样式 * feat(component): 修改返回数据 * feat(component): 新增瀑布流组件 * feat(component): 修改素材列表页面 * feat(component): getPageData接口修改 * feat(component): 修改瀑布流样式 * feat(component): 修改渲染层 * feat(component): 删除冗余代码 * feat(component): 重构逻辑 * feat(component): 移除注释 * feat(component): 加入loading组件 * feat(component): 加入Message组件 * feat(component): 增加瀑布流组件props * feat(component): 完成关键词瀑布流查询 * feat(component): 删除冗余 * feat(component): 修改瀑布流布局
- Loading branch information
1 parent
3730b60
commit 78d2ec2
Showing
3 changed files
with
249 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
<template> | ||
<div class="masonry-container" ref="containerRef" @scroll="handleScroll"> | ||
<div class="masonry-list"> | ||
<div | ||
class="masonry-item" | ||
v-for="(item, index) in dataState.cardList" | ||
:key="item.id" | ||
:style="{ | ||
width: `${dataState.cardPos[index].width}px`, | ||
height: `${dataState.cardPos[index].height}px`, | ||
transform: `translate(${dataState.cardPos[index].x}px, ${dataState.cardPos[index].y}px)`, | ||
}" | ||
> | ||
<slot name="item" :item="item" :index="index"></slot> | ||
</div> | ||
</div> | ||
<Spin size="large" fix :show="dataState.loading"></Spin> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
interface IVirtualWaterFallProps { | ||
gap: number; | ||
column: number; | ||
bottom: number; | ||
pageSize: number; | ||
request?: (page: number, pageSize: number) => Promise<ICardItem[]>; | ||
} | ||
interface ICardItem { | ||
id: number | string; | ||
url: string; | ||
width: number; | ||
height: number; | ||
[key: string]: any; | ||
} | ||
interface ICardPos { | ||
width: number; | ||
height: number; | ||
x: number; | ||
y: number; | ||
} | ||
const props = defineProps<IVirtualWaterFallProps>(); | ||
defineSlots<{ | ||
item(props: { item: ICardItem; index: number }): any; | ||
}>(); | ||
const dataState = reactive({ | ||
isFinish: false, | ||
page: 1, | ||
cardWidth: 0, | ||
cardList: [] as ICardItem[], | ||
cardPos: [] as ICardPos[], | ||
loading: false, | ||
columnHeight: new Array(props.column).fill(0) as number[], | ||
}); | ||
const containerRef = ref<HTMLDivElement | null>(null); | ||
const getCardList = async (page: number, pageSize: number) => { | ||
if (dataState.isFinish) { | ||
return; | ||
} | ||
dataState.loading = true; | ||
const list = await props.request(page, pageSize); | ||
dataState.page++; | ||
dataState.loading = false; | ||
if (!list.length) { | ||
dataState.isFinish = true; | ||
return; | ||
} | ||
dataState.cardList = [...dataState.cardList, ...list]; | ||
computedCardPos(list); | ||
}; | ||
const computedWidth = async () => { | ||
const containerWidth = containerRef.value.clientWidth; | ||
dataState.cardWidth = (containerWidth - props.gap * (props.column - 1)) / props.column; | ||
await getCardList(dataState.page, props.pageSize); | ||
}; | ||
const getkeyWordSearch = async () => { | ||
dataState.cardList = []; | ||
dataState.page = 1; | ||
dataState.cardPos = []; | ||
if (dataState.isFinish) { | ||
dataState.isFinish = false; | ||
} | ||
await getCardList(dataState.page, props.pageSize); | ||
}; | ||
const init = async () => { | ||
if (containerRef.value) { | ||
await computedWidth(); | ||
} | ||
}; | ||
const minColumn = computed(() => { | ||
let minIndex = -1, | ||
minHeight = Infinity; | ||
dataState.columnHeight.forEach((item, index) => { | ||
if (item < minHeight) { | ||
minHeight = item; | ||
minIndex = index; | ||
} | ||
}); | ||
return { | ||
minIndex, | ||
minHeight, | ||
}; | ||
}); | ||
const computedCardPos = (list: ICardItem[]) => { | ||
list.forEach((item, index) => { | ||
const cardHeight = Math.floor((item.height * dataState.cardWidth) / item.width); | ||
if (index < props.column && dataState.cardList.length <= props.pageSize) { | ||
dataState.cardPos.push({ | ||
width: dataState.cardWidth, | ||
height: cardHeight, | ||
x: index ? index * (dataState.cardWidth + props.gap) : 0, | ||
y: 0, | ||
}); | ||
dataState.columnHeight[index] = cardHeight + props.gap; | ||
} else { | ||
const { minIndex, minHeight } = minColumn.value; | ||
dataState.cardPos.push({ | ||
width: dataState.cardWidth, | ||
height: cardHeight, | ||
x: minIndex ? minIndex * (dataState.cardWidth + props.gap) : 0, | ||
y: minHeight, | ||
}); | ||
dataState.columnHeight[minIndex] += cardHeight + props.gap; | ||
} | ||
}); | ||
}; | ||
const handleScroll = rafThrottle(() => { | ||
const { scrollTop, clientHeight, scrollHeight } = containerRef.value!; | ||
const bottom = scrollHeight - clientHeight - scrollTop; | ||
if (bottom <= props.bottom) { | ||
!dataState.loading && getCardList(dataState.page, props.pageSize); | ||
} | ||
}, 50); | ||
function rafThrottle(fn) { | ||
let lock = false; | ||
return function (this: any, ...args: any[]) { | ||
if (lock) return; | ||
lock = true; | ||
window.requestAnimationFrame(() => { | ||
fn.apply(this, args); | ||
lock = false; | ||
}); | ||
}; | ||
} | ||
onMounted(() => { | ||
init(); | ||
}); | ||
defineExpose({ | ||
getkeyWordSearch, | ||
}); | ||
</script> | ||
|
||
<style scoped lang="less"> | ||
.masonry { | ||
&-container { | ||
width: 100%; | ||
height: 100%; | ||
overflow-y: scroll; | ||
overflow-x: hidden; | ||
} | ||
&-list { | ||
position: relative; | ||
width: 100%; | ||
} | ||
&-item { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
box-sizing: border-box; | ||
} | ||
} | ||
:deep(.ivu-message) { | ||
margin-top: 50%; | ||
.ivu-message-notice { | ||
text-align: left; | ||
padding-left: 20%; | ||
} | ||
} | ||
:deep(.ivu-divider) { | ||
position: absolute; | ||
top: 95%; | ||
.ivu-divider-horizontal.ivu-divider-with-text-center:before { | ||
width: 30%; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters