提交 3a160a17 编写于 作者: 雪洛's avatar 雪洛

feat: 模板页面增加自行实现长列表示例

上级 2b8972e5
...@@ -1270,6 +1270,14 @@ ...@@ -1270,6 +1270,14 @@
"navigationBarTitleText": "ad", "navigationBarTitleText": "ad",
"backgroundColor": "#F8F8F8" "backgroundColor": "#F8F8F8"
} }
},
{
"path" : "pages/template/custom-long-list/custom-long-list",
"style" :
{
"navigationBarTitleText" : "",
"enablePullDownRefresh" : false
}
} }
// #endif // #endif
], ],
......
...@@ -65,18 +65,18 @@ ...@@ -65,18 +65,18 @@
{ {
name: '顶部搜索框随时下移长列表', name: '顶部搜索框随时下移长列表',
url: 'long-list' url: 'long-list'
}, },
// #ifdef APP-ANDROID || WEB // #ifdef APP-ANDROID || WEB
{ {
name: '顶部banner长列表', name: '顶部banner长列表',
url: 'long-list2' url: 'long-list2'
}, },
// #endif // #endif
// #ifdef APP-IOS // #ifdef APP-IOS
{ {
name: '顶部banner长列表', name: '顶部banner长列表',
url: 'long-list-nested' url: 'long-list-nested'
}, },
// #endif // #endif
] as Page[], ] as Page[],
}, },
...@@ -110,21 +110,28 @@ ...@@ -110,21 +110,28 @@
}, },
] as Page[], ] as Page[],
}, },
// #ifdef APP {
id: 'custom-long-list',
url: 'custom-long-list',
name: '自行实现长列表组件',
open: false,
pages: [] as Page[],
},
// #ifdef APP
{ {
id: 'custom-refresher', id: 'custom-refresher',
url: 'custom-refresher', url: 'custom-refresher',
name: '自定义下拉刷新', name: '自定义下拉刷新',
open: false, open: false,
pages: [] as Page[], pages: [] as Page[],
}, },
{ {
id: 'pull-zoom-image', id: 'pull-zoom-image',
url: 'pull-zoom-image', url: 'pull-zoom-image',
name: '下拉缩放顶部封面图', name: '下拉缩放顶部封面图',
open: false, open: false,
pages: [] as Page[], pages: [] as Page[],
}, },
// #endif // #endif
{ {
id: 'swiper-vertical-video', id: 'swiper-vertical-video',
...@@ -132,7 +139,7 @@ ...@@ -132,7 +139,7 @@
name: '竖滑视频', name: '竖滑视频',
open: false, open: false,
pages: [] as Page[], pages: [] as Page[],
}, },
// #ifdef APP // #ifdef APP
{ {
id: 'scroll-sticky', id: 'scroll-sticky',
...@@ -140,7 +147,7 @@ ...@@ -140,7 +147,7 @@
name: 'scroll-view自定义滚动吸顶', name: 'scroll-view自定义滚动吸顶',
open: false, open: false,
pages: [] as Page[], pages: [] as Page[],
}, },
// #endif // #endif
{ {
id: 'half-screen', id: 'half-screen',
...@@ -170,7 +177,7 @@ ...@@ -170,7 +177,7 @@
open: false, open: false,
enable: true, enable: true,
pages: [] as Page[], pages: [] as Page[],
}, },
// #ifdef APP // #ifdef APP
{ {
id: 'calendar', id: 'calendar',
...@@ -190,16 +197,16 @@ ...@@ -190,16 +197,16 @@
name: '分享示例', name: '分享示例',
open: false, open: false,
pages: [] as Page[], pages: [] as Page[],
}, },
// #endif // #endif
// #ifdef WEB // #ifdef WEB
{ {
id: 'browser-canvas', id: 'browser-canvas',
url: 'browser-canvas', url: 'browser-canvas',
name: '如何使用浏览器 canvas', name: '如何使用浏览器 canvas',
open: false, open: false,
pages: [] as Page[], pages: [] as Page[],
}, },
// #endif // #endif
] as ListItem[], ] as ListItem[],
arrowUpIcon: '/static/icons/arrow-up.png', arrowUpIcon: '/static/icons/arrow-up.png',
...@@ -244,4 +251,4 @@ ...@@ -244,4 +251,4 @@
}, },
}, },
} }
</script> </script>
<template>
<view ref="box" class="custom-list-item-box">
<slot></slot>
</view>
</template>
<script>
export default {
name: "custom-list-item",
props: {
item: {
type: Object as PropType<any>,
required: true
}
},
inject: {
cachedSize: {
type: Map as PropType<Map<any, number>>,
default: () : Map<any, number> => {
return new Map<any, number>()
}
},
},
mounted() {
nextTick(() => {
uni.createSelectorQuery().in(this).select('.custom-list-item-box').boundingClientRect().exec((ret) => {
(this.cachedSize as Map<any, number>).set(this.item, (ret[0] as NodeInfo).height!)
})
})
}
}
</script>
<style>
</style>
<template>
<scroll-view class="custom-list-view-scoll-view" v-bind="$attrs" ref="scroll" @scroll="onScroll">
<view class="custom-list-view-placeholder" :style="{ height: placeholderHeight + 'px' }">
<view class="custom-list-view-container" :style="{ top: containerTop + 'px' }">
<slot :items="items"></slot>
</view>
</view>
</scroll-view>
</template>
<script>
/**
* 使用限制
* - 容器大小变动时未刷新缓存的子元素大小
* - 不支持设置初始滚动位置
* - list数据内每一项不可以是基础类型
* - item不支持设置margin,会导致计算位置不准确
*/
export default {
name: "custom-list-view",
props: {
list: {
type: Array as PropType<any[]>,
default: [] as any[]
}
},
watch: {
list: {
handler(list : any[]) {
this.cachedSize.forEach((_ : number, key : any) => {
if (!list.includes(key)) {
this.cachedSize.delete(key)
}
})
},
deep: true
}
},
data() {
return {
items: [] as any[],
containerTop: 0,
scrollElementHeight: 0,
placeholderHeight: 0,
offsetThreshold: [0, 0, 0, 0], // -5, -3, 3, 5 屏对应的offset
cachedSize: new Map<any, number>(),
initialized: false,
hasDefaultSize: false,
defaultItemSize: 40,
};
},
provide() {
return {
cachedSize: this.cachedSize
}
},
created() {
this.placeholderHeight = this.list.length * this.defaultItemSize
},
mounted() {
nextTick(() => {
uni.createSelectorQuery().in(this).select('.custom-list-view-scoll-view').boundingClientRect().exec((ret) => {
this.scrollElementHeight = (ret[0] as NodeInfo).height!
this.rearrange(0)
this.initialized = true
})
})
},
methods: {
onScroll(e : UniScrollEvent) {
if (!this.initialized) {
return
}
const scrollTop = e.detail.scrollTop
if (scrollTop < this.offsetThreshold[1] || scrollTop > this.offsetThreshold[2]) {
this.rearrange(scrollTop)
}
},
rearrange(scrollTop : number) {
this.offsetThreshold[0] = Math.max(scrollTop - this.scrollElementHeight * 5, 0)
this.offsetThreshold[1] = Math.max(scrollTop - this.scrollElementHeight * 3, 0)
this.offsetThreshold[2] = Math.min(scrollTop + this.scrollElementHeight * 4, this.placeholderHeight)
this.offsetThreshold[3] = Math.min(scrollTop + this.scrollElementHeight * 6, this.placeholderHeight)
const items = [] as any[]
let tempTotalHeight = 0
let containerTop = 0
for (let i = 0; i < this.list.length; i++) {
const item = this.list[i]
let itemSize = this.defaultItemSize
const cachedItemSize = this.cachedSize.get(item)
if (cachedItemSize != null) {
itemSize = cachedItemSize
if (!this.hasDefaultSize) {
this.defaultItemSize = itemSize
this.hasDefaultSize = true
}
}
tempTotalHeight += itemSize
if (tempTotalHeight >= this.offsetThreshold[0] && tempTotalHeight <= this.offsetThreshold[3]) {
items.push(item)
} else if (tempTotalHeight < this.offsetThreshold[0]) {
containerTop = tempTotalHeight
}
}
this.placeholderHeight = tempTotalHeight
this.items = items
this.containerTop = containerTop
}
}
}
</script>
<style>
</style>
<template>
<custom-list-view style="flex: 1;" :list="list" @scrolltoupper="scrolltoupper" @scroll="scroll">
<template v-slot:default="{items}">
<custom-list-item v-for="item in (items as Item[])" :item="item" :key="item.id" style="padding: 5px; 10px">
<view style="background-color: aqua;">{{item.title}}</view>
<view>{{item.content}}</view>
</custom-list-item>
</template>
</custom-list-view>
</template>
<script>
type Item = {
id : number
title : string
content : string
}
import CustomListView from "./custom-list-view"
import CustomListItem from "./custom-list-item"
export default {
components: {
CustomListView,
CustomListItem
},
data() {
return {
list: [] as Item[]
}
},
created() {
for (let i = 0; i < 1000; i++) {
this.list.push({
id: i,
title: `title-` + i,
content: `Content-${i}: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`
} as Item)
}
},
methods: {
scrolltoupper() {
console.log('scroll top upper')
},
scroll(e : UniScrollEvent) {
console.log('scroll', e)
}
}
}
</script>
<style>
</style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册