更新记录
1.0.0(2026-09-15) 下载此版本
初版
平台兼容性
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
SL-WX-RankList 横向滚动排行榜组件
组件简介
基于 scroll-view 实现的横向滚动分页式排行榜组件。内置「主列 + peek 提示」布局:每列宽度等于容器宽度 × leftRatio(默认 2/3),右侧自然露出 1 - leftRatio(1/3)部分作为"还有更多"的视觉提示。滑动松手后自动吸附到最近列边界(snap)。H5 / 微信小程序通用。
设计说明:组件只负责横向分页骨架(按
rowsPerColumn自动分列、列宽计算、snap 吸附),每张卡片的内容完全由调用方通过默认插槽自定义,灵活适配「小说榜」「主播榜」「热销榜」等不同业务场景。
目录结构
components/SL-WX-RankList
└── SL-WX-RankList.vue
核心特性
-
主列 + peek 提示布局:每列宽度 = 容器实际宽度 ×
leftRatio,右侧露出1 - leftRatio部分暗示后续内容,无需额外配置即可呈现"分页浏览"的视觉。 -
滑动后自动吸附:松手停止滚动后 150ms 防抖,计算
Math.round(scrollLeft / columnWidth)得到最近列索引,通过scroll-into-view平滑滚动到目标列。动画期间锁住 scroll 事件 450ms,避免抖动。 -
自动分列:根据
rowsPerColumn把list自动切成多列(每列 N 项),无需业务侧预处理。 -
统一卡片高度:所有卡片高度一致(
cardHeight),列内卡片纵向排列,列间通过gap控制间距。 -
内容完全自定义:默认插槽透传
item/index/columnIndex/rowIndex四个作用域参数,调用方自由渲染封面、排名徽章、标题、分类标签、热度数据等。 -
标题栏可选:
title/subtitle可选,传入则在顶部渲染标题与副标题。 -
外部跳转 API:暴露
scrollToColumn(index)方法,可通过$refs控制跳转到指定列。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
list |
Array | [] |
排行榜数据列表,每项结构由业务自定义(通常含 cover / title / value 等字段) |
title |
String | '' |
标题,传入则在顶部渲染标题栏(不传则不渲染标题栏) |
subtitle |
String | '' |
副标题(右上角灰色小字,如更新时间) |
cardHeight |
Number | 280 |
卡片高度(rpx),所有卡片统一高度 |
gap |
Number | 16 |
列间距(rpx) |
rowsPerColumn |
Number | 4 |
每列行数,组件据此自动分列 |
leftRatio |
Number | 2/3 |
主列占比(0~1),主列宽度 = 容器宽度 × leftRatio,右侧露出 1 - leftRatio |
snap |
Boolean | true |
是否启用滑动后吸附到最近列 |
Events
| 事件名 | 回调参数 | 说明 |
|---|---|---|
card-tap |
{ item, index } |
点击卡片时触发,返回当前项数据与全局索引 |
onCardTap({ item, index }) {
console.log('第' + (index + 1) + '位', item)
}
Slots
| 插槽名 | 作用域参数 | 说明 |
|---|---|---|
default |
{ item, index, columnIndex, rowIndex } |
自定义每张卡片内容。item 为当前项数据,index 为全局索引(0 起),columnIndex 为列索引,rowIndex 为列内行索引 |
对外方法
<sl-wx-rank-list ref="rank" :list="list" />
| 方法名 | 参数 | 说明 |
|---|---|---|
scrollToColumn |
index |
跳转到指定列(0 起),带平滑动画 |
this.$refs.rank.scrollToColumn(2) // 跳到第 3 列
使用示例
示例 1:基础排行榜(封面 + 排名徽章 + 标题 + 热度)
<template>
<sl-wx-rank-list
:list="list"
title="热读榜 · 网文精选"
subtitle="09-12 更新"
:card-height="300"
:rows-per-column="4"
:left-ratio="2/3"
@card-tap="onCardTap"
>
<template v-slot:default="{ item, index }">
<view class="rank-card">
<view class="rank-cover-wrap">
<image class="rank-cover" :src="item.cover" mode="aspectFill" />
<view class="rank-badge" :class="badgeClass(index)">
<text>{{ index + 1 }}</text>
</view>
</view>
<view class="rank-info">
<text class="rank-title">{{ item.title }}</text>
<view class="rank-meta">
<view class="rank-tag">{{ item.category }}</view>
<text class="rank-value">{{ item.value }}</text>
</view>
</view>
</view>
</template>
</sl-wx-rank-list>
</template>
<script>
import SlWxRankList from '@/components/SL-WX-RankList/SL-WX-RankList.vue'
export default {
components: { SlWxRankList },
data() {
return {
list: [
{ cover: 'https://picsum.photos/seed/n1/300/400', title: '开局5000万,让我办学校??', category: '都市脑洞', value: '9262万在读' },
{ cover: 'https://picsum.photos/seed/n2/300/400', title: '重生S2:调教电竞圈!', category: '游戏体育', value: '9066万在读' },
{ cover: 'https://picsum.photos/seed/n3/300/400', title: '破产三轮车厂,被我干成万亿巨头', category: '都市种田', value: '8971万在读' },
{ cover: 'https://picsum.photos/seed/n4/300/400', title: '全城围捕:我真不是罪犯啊', category: '悬疑灵异', value: '24个老书虫在看' },
{ cover: 'https://picsum.photos/seed/n5/300/400', title: '诸神要我献祭反手献祭神', category: '都市脑洞', value: '87 追读' },
{ cover: 'https://picsum.photos/seed/n6/300/400', title: '玩家每日一戏出完整版', category: '都市脑洞', value: '84 追读' },
{ cover: 'https://picsum.photos/seed/n7/300/400', title: '修仙:从黑始崛起', category: '东方仙侠', value: '81 追读' },
{ cover: 'https://picsum.photos/seed/n8/300/400', title: '全民部落求活着就能变', category: '游戏体育', value: '81 追读' }
]
}
},
methods: {
onCardTap({ item, index }) {
uni.showToast({ title: `第 ${index + 1} 位 · ${item.title}`, icon: 'none' })
},
badgeClass(index) {
if (index === 0) return 'gold'
if (index === 1) return 'silver'
if (index === 2) return 'bronze'
return ''
}
}
}
</script>
示例 2:调整主列占比(1/2 / 3/4)
<!-- 主列占 1/2,右侧露出 1/2 -->
<sl-wx-rank-list :list="list" :left-ratio="0.5" />
<!-- 主列占 3/4,右侧露出 1/4 -->
<sl-wx-rank-list :list="list" :left-ratio="0.75" />
示例 3:关闭吸附(自由滚动)
<sl-wx-rank-list :list="list" :snap="false" />
示例 4:外部控制跳转
<template>
<view>
<view class="btns">
<button @tap="$refs.rank.scrollToColumn(0)">第 1 列</button>
<button @tap="$refs.rank.scrollToColumn(1)">第 2 列</button>
<button @tap="$refs.rank.scrollToColumn(2)">第 3 列</button>
</view>
<sl-wx-rank-list ref="rank" :list="list" :rows-per-column="4" />
</view>
</template>
示例 5:每列 3 行 + 紧凑布局
<sl-wx-rank-list
:list="list"
:rows-per-column="3"
:card-height="240"
:gap="12"
:left-ratio="0.6"
/>
布局原理简述
-
容器宽度测量:
mounted时通过uni.createSelectorQuery测量.sl-ranklist__scroll的实际宽度(px),兜底使用windowWidth。 -
列宽计算:
columnWidth = floor(containerWidth × leftRatio),保证右列露出1 - leftRatio部分。 -
自动分列:
computed.columns按rowsPerColumn把list切成二维数组,每列 N 项,最后一列可能不满。 -
snap 吸附:
@scroll防抖 150ms,停止后nearestIndex = round(scrollLeft / columnWidth),通过scroll-into-view+ 列 id(sl-ranklist-col-N)平滑滚动。动画期间isAnimating锁住 scroll 事件 450ms 避免冲突。 -
数据变化回首页:
watch list触发后$nextTick自动snapToIndex(0),回到第一列。
注意事项
-
容器宽度依赖测量:组件
mounted后测量容器宽度,若父级容器宽度延迟生效(如v-if切换、动画展开),需在宽度就绪后调用scrollToColumn(0)重新吸附。 -
leftRatio取值范围:建议0.5 ~ 0.8。过小(如 0.3)主列太窄内容拥挤,过大(如 0.9)右侧 peek 提示不明显失去"还有更多"的视觉意义。 -
rowsPerColumn与数据量:列数 =ceil(list.length / rowsPerColumn)。数据量少时列数少,横向滚动距离短;数据量大时建议配合rowsPerColumn = 4 ~ 6控制列数。 -
snap 与自由滚动:
snap = false时组件不再吸附,可自由停在任意位置;适合"连续浏览"场景。 -
卡片高度统一:所有卡片高度一致(
cardHeight),如需差异化高度,请在插槽内通过内部布局(如封面固定高度 + 信息区 flex 撑满)实现视觉差异,不要试图让卡片本身高度不一致。 -
scroll-into-view限制:吸附依赖scroll-into-view+ 元素 id,小程序要求 id 不能以数字开头,组件已统一使用sl-ranklist-col-前缀。

收藏人数:
下载插件并导入HBuilderX
赞赏(0)
下载 108
赞赏 1
下载 12603411
赞赏 1949
赞赏
京公网安备:11010802035340号