瀑布流 xWaterfall
预览
介绍
不要子组件排队量高。图用 rpx 写死宽高,文正常折行;按 key 批量量卡高后砌石,视口切片。#item 只开一个出口。五端一层 scroll-view,不要原生子协商。估高占位,进窗只量一次,滚动不再复验、不改已锁高度。App / Web #sticky 到达后 fixed。小程序 using-sticky + position:sticky。
平台兼容
| Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|---|---|---|---|---|---|---|---|
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 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 | `` | number | 2 |
| gutter | `` | string | "8" |
| height | `` | string | "100%" |
| estimateExtra | `` | string | "72" |
| refresh | `` | xWfRefreshFun | () : xWfRefreshFun => { return null } |
| pulldown | `` | xWfRefreshFun | () : xWfRefreshFun => { return null } |
| showScrollbar | `` | boolean | false |
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>