Skip to content

瀑布流 xWaterfall ​


预览

介绍 ​

不要子组件排队量高。图用 rpx 写死宽高,文正常折行;按 key 批量量卡高后砌石,视口切片。#item 只开一个出口。五端一层 scroll-view,不要原生子协商。估高占位,进窗只量一次,滚动不再复验、不改已锁高度。App / Web #sticky 到达后 fixed。小程序 using-sticky + position:sticky。

平台兼容 ​

HarmonyH5andriodIOS小程序UTSUNIAPP-X SDKversion
☑☑☑️☑️☑️☑️4.76+1.1.18

文件路径 ​

ts

@/uni_modules/tmx-ui/components/x-waterfall/x-waterfall.uvue

使用 ​

ts

<x-waterfall></x-waterfall>

Props 属性 ​

名称说明类型默认值
list``
Array() : any[] => [] as any[]
rowKey``
string"key"
column``
number2
gutter``
string"8"
height``
string"100%"
estimateExtra``
string"72"
refresh``
xWfRefreshFun() : xWfRefreshFun => { return null }
pulldown``
xWfRefreshFun() : xWfRefreshFun => { return null }
showScrollbar``
booleanfalse

Events 事件 ​

名称参数说明
scroll--
refresh--
pulldown--

Slots 插槽 ​

名称说明数据
top--
sticky--
empty--
item--
refresh--

Ref 方法 ​

名称参数返回值说明
refreshSize---
resetScroll---

示例文件路径 ​

json

/pages/zhanshi/waterfall

示例源码 ​

uvue
vue
<template>
	<!-- #ifdef MP-WEIXIN -->
	<page-meta :page-style="`background-color:${xThemeConfigBgColor}`">
		<navigation-bar :background-color="xThemeConfigNavBgColor"
			:front-color="xThemeConfigNavFontColor"></navigation-bar>
	</page-meta>
	<!-- #endif -->

	<view class="page">
		<x-waterfall
			:key="column"
			class="pageList"
			row-key="key"
			:column="column"
			gutter="8"
			height="100%"
			:list="list"
			:pulldown="onPulldown"
			:refresh="onLoadMore"
		>
			<template #top>
				<x-sheet :margin="[0, 0, 0, 8]">
					<x-text font-size="18" class=" text-weight-b mb-8">瀑布流 xWaterfall</x-text>
					<x-text color="#999999" class="mb-12">图用 rpx 写死宽高,不要 100%。文正常折行。向下先推顶部,到顶后滚下面的卡并触底加载。本页 {{ list.length }} 条,{{ column }} 列。</x-text>
					<view class="flex flex-row flex-row-center-between">
						<x-button size="small" class="flex-1" @click="setCols(2)">2列</x-button>
						<x-button size="small" class="ml-5 flex-1" @click="setCols(3)">3列</x-button>
						<x-button size="small" class="ml-5 flex-1" @click="appendRows">追加</x-button>
						<x-button size="small" class="ml-5 flex-1" @click="clearAll">清空</x-button>
					</view>
				</x-sheet>
			</template>
			<template #sticky="{ pinned }">
				<view :class="pinned ? 'tabFull' : 'tabCard'">
					<x-tabs
						v-model="menuId"
						:list="tabList"
						item-width="33.333%"
						height="44"
						:round="pinned ? '0' : '10'"
					></x-tabs>
				</view>
			</template>
			<template #item="{ rows }">
				<view
					v-for="row in rows"
					:id="boxIdOf(row)"
					:key="keyOf(row)"
					class="card"
					:style="wrapStyle(row)"
				>
					<image
						class="cardImg"
						:src="coverOf(dataOf(row))"
						:style="{ width: imgWOf(row), height: imgHOf(dataOf(row)) }"
					></image>
					<x-text class="cardTxt" font-size="14" line-height="1.5">{{ titleOf(dataOf(row)) }}</x-text>
				</view>
			</template>
			<template #empty>
				<x-empty v-if="list.length == 0" :empty="true" :loading="false" btn-label="加载数据" @click="reloadAll"></x-empty>
			</template>
		</x-waterfall>
	</view>
</template>

<script lang="ts" setup>
	import { computed, ref } from "vue"
	import { xConfig, TABS_ITEM_INFO } from "@/uni_modules/tmx-ui"

	type DemoCard = {
		key : string
		title : string
		imgH : string
		height : number
	}

	type CardStyle = {
		position : string
		left : string
		top : string
		width : string
		height : string
		minHeight : string
		overflow : string
		backgroundColor : string
	}

	const pool = "今年国家安全教育日,除传统领域外,网络、数据、人工智能也成为防护重点。群众既是保护对象,也是第一道防线。城市里的摄像头、港口的货柜、实验室的样本,都可能被当成切口。"
	const imgHs : string[] = ["200rpx", "240rpx", "280rpx", "180rpx", "320rpx"]

	function makeRows(from : number, count : number) : DemoCard[] {
		const out : DemoCard[] = []
		for (let i = 0; i < count; i++) {
			const n = from + i
			const start = n * 7 % pool.length
			let len = 18 + (n * 13 % 40)
			if (start + len > pool.length) len = pool.length - start
			if (len < 8) len = 8
			out.push({
				key: n.toString(),
				title: (n + 1).toString() + " " + pool.substring(start, start + len),
				imgH: imgHs[n % imgHs.length],
				height: 0
			})
		}
		return out
	}

	const column = ref<number>(2)
	const menuId = ref<string>("all")
	const tabList = [
		{ id: "all", title: "综合" },
		{ id: "new", title: "新品" },
		{ id: "hot", title: "热门" }
	] as TABS_ITEM_INFO[]
	const list = ref<DemoCard[]>(makeRows(0, 60))

	const cardBg = computed(() : string => {
		if (xConfig.dark == "dark") return xConfig.sheetDarkColor
		return "#ffffff"
	})

	function boxIdOf(row : any) : string {
		return "" + row.boxId
	}

	function keyOf(row : any) : string {
		return "" + row.key
	}

	function dataOf(row : any) : any {
		return row.item
	}

	function imgWOf(row : any) : string {
		const w = row.imgWidth
		if (w == null || w == "") return "200rpx"
		return "" + w
	}

	function imgHOf(item : any) : string {
		const h = item.imgH
		if (h == null || h == "") return "200rpx"
		return "" + h
	}

	function wrapStyle(row : any) : CardStyle {
		const s = row.style
		if (s == null) {
			return {
				position: "absolute",
				left: "0px",
				top: "0px",
				width: "200px",
				height: "auto",
				minHeight: "0px",
				overflow: "visible",
				backgroundColor: cardBg.value
			}
		}
		let h = "" + s.height
		if (h == "0px" || h == "0") h = "auto"
		let minH = "" + s.minHeight
		if (minH == "" || minH == "null" || minH == "undefined") minH = "0px"
		let overflow = "" + s.overflow
		if (overflow == "" || overflow == "null" || overflow == "undefined") overflow = "visible"
		return {
			position: "absolute",
			left: "" + s.left,
			top: "" + s.top,
			width: "" + s.width,
			height: h,
			minHeight: minH,
			overflow: overflow,
			backgroundColor: cardBg.value
		}
	}

	function coverOf(item : any) : string {
		return "https://picsum.photos/400/300?random=" + item.key
	}

	function titleOf(item : any) : string {
		const t = item.title
		if (t == null) return ""
		return "" + t
	}

	function wait(ms : number) : Promise<void> {
		return new Promise<void>((resolve) => {
			setTimeout(() => {
				resolve()
			}, ms)
		})
	}

	function setCols(n : number) : void {
		if (column.value == n) return
		const rows = list.value
		for (let i = 0; i < rows.length; i++) {
			rows[i].height = 0
		}
		column.value = n
	}

	function reloadAll() : void {
		list.value = makeRows(0, 60)
	}

	function appendRows() : void {
		list.value = list.value.concat(makeRows(list.value.length, 12))
	}

	function clearAll() : void {
		list.value = [] as DemoCard[]
	}

	async function onPulldown() : Promise<void> {
		await wait(600)
		list.value = makeRows(0, 60)
	}

	async function onLoadMore() : Promise<void> {
		await wait(500)
		appendRows()
	}
</script>

<style scoped>
	.page {
		flex: 1;
		display: flex;
		flex-direction: column;
		
	}

	.pageList {
		flex: 1;
		min-height: 0;
	}

	.tabCard {
		margin: 0 8px 8px 8px;
		border-radius: 10px;
		overflow: hidden;
	}

	.tabFull {
		margin: 0;
	}

	.card {
		border-radius: 10px;
		/* #ifndef APP */
		overflow: hidden;
		/* #endif */
	}

	.cardImg {
		border-radius: 10px 10px 0 0;
		flex-shrink: 0;
	}

	.cardTxt {
		width: 100%;
		padding: 10px 10px 12px 10px;
		flex-shrink: 0;
	}
</style>
最近更新