Appearance
BizSeamlessScroll 无缝滚动
requestAnimationFrame 驱动的无缝滚动列表:内容双份拼接 + 半高取模回绕,hover 自动暂停,速度 / 方向可配。用于数据大屏、监控看板的榜单与告警滚动。
基础用法
紧急传感器-01温度超限 82℃
警告传感器-02湿度低于 20%
提示传感器-03离线超过 5 分钟
紧急传感器-04温度超限 82℃
警告传感器-05湿度低于 20%
提示传感器-06离线超过 5 分钟
紧急传感器-07温度超限 82℃
警告传感器-08湿度低于 20%
提示传感器-09离线超过 5 分钟
紧急传感器-10温度超限 82℃
警告传感器-11湿度低于 20%
提示传感器-12离线超过 5 分钟
紧急传感器-01温度超限 82℃
警告传感器-02湿度低于 20%
提示传感器-03离线超过 5 分钟
紧急传感器-04温度超限 82℃
警告传感器-05湿度低于 20%
提示传感器-06离线超过 5 分钟
紧急传感器-07温度超限 82℃
警告传感器-08湿度低于 20%
提示传感器-09离线超过 5 分钟
紧急传感器-10温度超限 82℃
警告传感器-11湿度低于 20%
提示传感器-12离线超过 5 分钟
speed 控制像素速度(px/s),direction 切换向上 / 向下;hover 列表自动暂停;pause() / resume() / reset() 可编程控制。<script setup lang="ts">
/** 基础用法:告警榜单无缝滚动(hover 暂停),方向/速度可调 */
import { ref } from 'vue'
import { ElButton, ElTag } from 'element-plus'
import { BizSeamlessScroll } from 'vue3-ep-biz-lib'
import type { BizSeamlessScrollInstance } from 'vue3-ep-biz-lib'
interface AlertItem {
id: number
level: 'high' | 'mid' | 'low'
device: string
message: string
}
const list: AlertItem[] = Array.from({ length: 12 }, (_unused, i) => ({
id: i + 1,
level: (['high', 'mid', 'low'] as const)[i % 3],
device: `传感器-${String(i + 1).padStart(2, '0')}`,
message: ['温度超限 82℃', '湿度低于 20%', '离线超过 5 分钟'][i % 3],
}))
const scrollRef = ref<BizSeamlessScrollInstance>()
const direction = ref<'up' | 'down'>('up')
const levelMap = {
high: { label: '紧急', type: 'danger' as const },
mid: { label: '警告', type: 'warning' as const },
low: { label: '提示', type: 'info' as const },
}
</script>
<template>
<div>
<BizSeamlessScroll ref="scrollRef" :list="list" :speed="40" :direction="direction" :height="240">
<template #item="{ item }">
<div style="display: flex; gap: 10px; align-items: center; padding: 8px 4px; border-bottom: 1px solid var(--el-border-color-lighter)">
<ElTag size="small" :type="levelMap[item.level].type">{{ levelMap[item.level].label }}</ElTag>
<span style="min-width: 96px; color: var(--el-text-color-regular)">{{ item.device }}</span>
<span style="color: var(--el-text-color-secondary)">{{ item.message }}</span>
</div>
</template>
</BizSeamlessScroll>
<div style="display: flex; gap: 8px; margin-top: 12px">
<ElButton size="small" @click="direction = direction === 'up' ? 'down' : 'up'">切换方向({{ direction }})</ElButton>
<ElButton size="small" @click="scrollRef?.pause()">暂停</ElButton>
<ElButton size="small" @click="scrollRef?.resume()">恢复</ElButton>
</div>
</div>
</template>
显示代码
大屏榜单与实时调速
101 号生产线98.6 kWh↑ 上升
202 号生产线97.2 kWh↓ 下降
303 号生产线95.7 kWh↓ 下降
404 号生产线94.3 kWh↑ 上升
505 号生产线92.9 kWh↓ 下降
606 号生产线91.4 kWh↓ 下降
707 号生产线90 kWh↑ 上升
808 号生产线88.6 kWh↓ 下降
909 号生产线87.2 kWh↓ 下降
1010 号生产线85.7 kWh↑ 上升
1111 号生产线84.3 kWh↓ 下降
1212 号生产线82.9 kWh↓ 下降
1313 号生产线81.4 kWh↑ 上升
1414 号生产线80 kWh↓ 下降
1515 号生产线78.6 kWh↓ 下降
101 号生产线98.6 kWh↑ 上升
202 号生产线97.2 kWh↓ 下降
303 号生产线95.7 kWh↓ 下降
404 号生产线94.3 kWh↑ 上升
505 号生产线92.9 kWh↓ 下降
606 号生产线91.4 kWh↓ 下降
707 号生产线90 kWh↑ 上升
808 号生产线88.6 kWh↓ 下降
909 号生产线87.2 kWh↓ 下降
1010 号生产线85.7 kWh↑ 上升
1111 号生产线84.3 kWh↓ 下降
1212 号生产线82.9 kWh↓ 下降
1313 号生产线81.4 kWh↑ 上升
1414 号生产线80 kWh↓ 下降
1515 号生产线78.6 kWh↓ 下降
速度 30 px/shover 暂停
能耗榜单场景:
speed 绑定 ElSlider 实时调速(10~120 px/s),hover-pause 开关对比悬停暂停;item 插槽按名次渲染徽标(前三名主题色高亮)。<script setup lang="ts">
/** 进阶:数据大屏能耗榜单:ElSlider 实时调速、hover 暂停开关、前三名徽标高亮 */
import { ref } from 'vue'
import { ElSlider, ElSwitch } from 'element-plus'
import { BizSeamlessScroll } from 'vue3-ep-biz-lib'
interface FactoryRank {
rank: number
name: string
energy: number
trend: 'up' | 'down'
}
const list: FactoryRank[] = Array.from({ length: 15 }, (_unused, i) => ({
rank: i + 1,
name: `${String(i + 1).padStart(2, '0')} 号生产线`,
energy: Number((98.6 - i * 1.43).toFixed(1)),
trend: i % 3 === 0 ? 'up' : 'down',
}))
const speed = ref(30)
const hoverPause = ref(true)
</script>
<template>
<div style="max-width: 480px">
<BizSeamlessScroll :list="list" :speed="speed" :height="220" :hover-pause="hoverPause">
<template #item="{ item }">
<div style="display: flex; align-items: center; gap: 10px; padding: 9px 4px; border-bottom: 1px solid var(--el-border-color-lighter)">
<span
:style="{
width: '22px',
height: '22px',
lineHeight: '22px',
textAlign: 'center',
borderRadius: '4px',
fontSize: '12px',
flexShrink: 0,
color: item.rank <= 3 ? '#fff' : 'var(--el-text-color-secondary)',
background:
item.rank === 1 ? 'var(--el-color-danger)' : item.rank === 2 ? 'var(--el-color-warning)' : item.rank === 3 ? 'var(--el-color-primary)' : 'var(--el-fill-color-light)',
}"
>{{ item.rank }}</span>
<span style="flex: 1">{{ item.name }}</span>
<span style="font-variant-numeric: tabular-nums; color: var(--el-text-color-regular)">{{ item.energy }} kWh</span>
<span :style="{ fontSize: '12px', color: item.trend === 'up' ? 'var(--el-color-danger)' : 'var(--el-color-success)' }">
{{ item.trend === 'up' ? '↑ 上升' : '↓ 下降' }}
</span>
</div>
</template>
</BizSeamlessScroll>
<div style="display: flex; align-items: center; gap: 16px; margin-top: 12px">
<span style="font-size: 13px; color: var(--el-text-color-secondary); white-space: nowrap">速度 {{ speed }} px/s</span>
<ElSlider v-model="speed" :min="10" :max="120" :step="10" style="flex: 1" />
<span style="font-size: 13px; color: var(--el-text-color-secondary); white-space: nowrap">hover 暂停</span>
<ElSwitch v-model="hoverPause" />
</div>
</div>
</template>
显示代码
运营公告栏
运营公告
紧急9 月 26 日 02:00-06:00 平台停机升级,期间下单暂停09-23 09:00
常规新版增值税发票申请流程上线,请前往财务中心重新签署授权09-22 18:30
常规十一假期值班表已发布,请相关部门负责人确认排班09-22 14:10
紧急接监管通知,实名信息补录截止 09-30,逾期将限制开票09-21 20:05
常规供应链系统将于本周末切换新对账模板,旧模板自动迁移09-21 10:00
紧急9 月 26 日 02:00-06:00 平台停机升级,期间下单暂停09-23 09:00
常规新版增值税发票申请流程上线,请前往财务中心重新签署授权09-22 18:30
常规十一假期值班表已发布,请相关部门负责人确认排班09-22 14:10
紧急接监管通知,实名信息补录截止 09-30,逾期将限制开票09-21 20:05
常规供应链系统将于本周末切换新对账模板,旧模板自动迁移09-21 10:00
门户公告场景:
direction="down" 新公告从顶部进入、speed=18 低速易读,紧急 / 常规两级标签区分,hover 暂停下细看。<script setup lang="ts">
/** 场景:运营公告栏——低速向下滚动,hover 停下细看 */
import { BizSeamlessScroll } from 'vue3-ep-biz-lib'
interface NoticeItem {
id: number
level: 'urgent' | 'normal'
text: string
time: string
}
const notices: NoticeItem[] = [
{ id: 1, level: 'urgent', text: '9 月 26 日 02:00-06:00 平台停机升级,期间下单暂停', time: '09-23 09:00' },
{ id: 2, level: 'normal', text: '新版增值税发票申请流程上线,请前往财务中心重新签署授权', time: '09-22 18:30' },
{ id: 3, level: 'normal', text: '十一假期值班表已发布,请相关部门负责人确认排班', time: '09-22 14:10' },
{ id: 4, level: 'urgent', text: '接监管通知,实名信息补录截止 09-30,逾期将限制开票', time: '09-21 20:05' },
{ id: 5, level: 'normal', text: '供应链系统将于本周末切换新对账模板,旧模板自动迁移', time: '09-21 10:00' },
]
</script>
<template>
<div style="max-width: 520px; border: 1px solid var(--el-border-color-lighter); border-radius: 6px; padding: 12px 16px">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-weight: 600">
<span
style="width: 8px; height: 8px; border-radius: 50%; background: var(--el-color-danger); animation: demo-notice-blink 1.2s infinite"
/>
运营公告
</div>
<!-- direction down:新公告从顶部进入;速度 18px/s 低速易读 -->
<BizSeamlessScroll :list="notices" direction="down" :speed="18" :height="132">
<template #item="{ item }">
<div style="display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 13px">
<span
:style="{
flexShrink: 0,
padding: '0 6px',
lineHeight: '18px',
borderRadius: '3px',
fontSize: '12px',
color: item.level === 'urgent' ? '#fff' : 'var(--el-text-color-secondary)',
background: item.level === 'urgent' ? 'var(--el-color-danger)' : 'var(--el-fill-color)',
}"
>{{ item.level === 'urgent' ? '紧急' : '常规' }}</span>
<span style="flex: 1; color: var(--el-text-color-regular)">{{ item.text }}</span>
<span style="color: var(--el-text-color-placeholder); font-size: 12px">{{ item.time }}</span>
</div>
</template>
</BizSeamlessScroll>
</div>
</template>
<style>
@keyframes demo-notice-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}
</style>
显示代码
实时销量榜
1热销商品 A已售 500
2热销商品 B已售 463
3热销商品 C已售 426
4热销商品 D已售 389
5热销商品 E已售 352
6热销商品 F已售 315
7热销商品 G已售 278
8热销商品 H已售 241
9热销商品 I已售 204
10热销商品 J已售 167
1热销商品 A已售 500
2热销商品 B已售 463
3热销商品 C已售 426
4热销商品 D已售 389
5热销商品 E已售 352
6热销商品 F已售 315
7热销商品 G已售 278
8热销商品 H已售 241
9热销商品 I已售 204
10热销商品 J已售 167
每 2s 销量刷新并重排名,滚动位置不重置
大促场景:每 2s 数据刷新并重排名,滚动位置不重置;前三名红字标亮。
<script setup lang="ts">
/** 场景:实时销量榜——定时更新数据,滚动不中断 */
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { BizSeamlessScroll } from 'vue3-ep-biz-lib'
interface SaleRow {
rank: number
sku: string
count: number
}
const list = ref<SaleRow[]>(
Array.from({ length: 10 }, (_unused, i) => ({ rank: i + 1, sku: `热销商品 ${String.fromCharCode(65 + i)}`, count: 500 - i * 37 }))
)
let timer: ReturnType<typeof setInterval> | null = null
onMounted(() => {
timer = setInterval(() => {
const next = [...list.value]
const lucky = Math.floor(Math.random() * next.length)
next[lucky] = { ...next[lucky], count: next[lucky].count + Math.ceil(Math.random() * 3) }
list.value = next.sort((a, b) => b.count - a.count).map((row, index) => ({ ...row, rank: index + 1 }))
}, 2000)
})
onBeforeUnmount(() => {
if (timer !== null) clearInterval(timer)
})
</script>
<template>
<div style="max-width: 400px">
<BizSeamlessScroll :list="list" :speed="26" :height="216">
<template #item="{ item }">
<div style="display: flex; align-items: center; gap: 10px; padding: 8px 4px; border-bottom: 1px solid var(--el-border-color-lighter); font-size: 13px">
<span :style="{ width: 20, textAlign: 'center', fontWeight: item.rank <= 3 ? 700 : 400, color: item.rank <= 3 ? 'var(--el-color-danger)' : 'var(--el-text-color-secondary)' }">{{ item.rank }}</span>
<span style="flex: 1">{{ item.sku }}</span>
<span style="font-variant-numeric: tabular-nums; color: var(--el-color-primary)">已售 {{ item.count }}</span>
</div>
</template>
</BizSeamlessScroll>
<div style="margin-top: 8px; font-size: 13px; color: var(--el-text-color-secondary)">每 2s 销量刷新并重排名,滚动位置不重置</div>
</div>
</template>
显示代码
行情看板
代码 / 名称现价涨跌幅
SH600000 贵州燃气8.00-1%
SH600007 宁科生物8.93+0.3%
SH600014 东方碳谷9.86-0.5%
SH600021 华设集团10.79+0.8%
SH600028 联合水务11.72+0%
SH600035 贵州燃气12.65-0.8%
SH600042 宁科生物13.58+0.5%
SH600049 东方碳谷14.51-0.3%
SH600056 华设集团15.44+1%
SH600063 联合水务16.37+0.2%
SH600070 贵州燃气17.30-0.6%
SH600077 宁科生物18.23+0.7%
SH600084 东方碳谷19.16-0.1%
SH600091 华设集团20.09-0.9%
SH600000 贵州燃气8.00-1%
SH600007 宁科生物8.93+0.3%
SH600014 东方碳谷9.86-0.5%
SH600021 华设集团10.79+0.8%
SH600028 联合水务11.72+0%
SH600035 贵州燃气12.65-0.8%
SH600042 宁科生物13.58+0.5%
SH600049 东方碳谷14.51-0.3%
SH600056 华设集团15.44+1%
SH600063 联合水务16.37+0.2%
SH600070 贵州燃气17.30-0.6%
SH600077 宁科生物18.23+0.7%
SH600084 东方碳谷19.16-0.1%
SH600091 华设集团20.09-0.9%
行情场景:代码 / 名称 / 现价 / 涨跌幅四列滚动,红涨绿跌着色,表头固定不滚。
<script setup lang="ts">
/** 场景:行情看板——红涨绿跌多列滚动,hover 暂停细看 */
import { BizSeamlessScroll } from 'vue3-ep-biz-lib'
interface QuoteRow {
code: string
name: string
price: string
change: number
}
const rows: QuoteRow[] = Array.from({ length: 14 }, (_unused, i) => ({
code: `SH${String(600000 + i * 7)}`,
name: ['贵州燃气', '宁科生物', '东方碳谷', '华设集团', '联合水务'][i % 5],
price: (8 + i * 0.93).toFixed(2),
change: Number((((i * 13) % 21 - 10) / 10).toFixed(1)),
}))
</script>
<template>
<div style="max-width: 480px; border: 1px solid var(--el-border-color-lighter); border-radius: 6px; padding: 10px 14px">
<div style="display: flex; font-size: 12px; color: var(--el-text-color-secondary); border-bottom: 1px solid var(--el-border-color-lighter); padding-bottom: 6px">
<span style="flex: 1">代码 / 名称</span>
<span style="width: 80px; text-align: right">现价</span>
<span style="width: 64px; text-align: right">涨跌幅</span>
</div>
<BizSeamlessScroll :list="rows" :speed="34" :height="190">
<template #item="{ item }">
<div style="display: flex; align-items: center; padding: 7px 0; border-bottom: 1px solid var(--el-border-color-extra-light); font-size: 13px">
<span style="flex: 1">
<span style="font-family: monospace; color: var(--el-text-color-secondary)">{{ item.code }}</span>
{{ item.name }}
</span>
<span style="width: 80px; text-align: right; font-variant-numeric: tabular-nums">{{ item.price }}</span>
<span :style="{ width: '64px', textAlign: 'right', color: item.change >= 0 ? 'var(--el-color-danger)' : 'var(--el-color-success)' }">
{{ item.change >= 0 ? '+' : '' }}{{ item.change }}%
</span>
</div>
</template>
</BizSeamlessScroll>
</div>
</template>
显示代码
中奖播报条
🎉 中奖播报
138****2000 刚刚获得 空气炸锅(10:00)
138****2111 刚刚获得 保温杯(11:07)
138****2222 刚刚获得 5 元无门槛券(12:14)
138****2333 刚刚获得 积分 +50(13:21)
138****2444 刚刚获得 空气炸锅(14:28)
138****2555 刚刚获得 保温杯(15:35)
138****2666 刚刚获得 5 元无门槛券(16:42)
138****2777 刚刚获得 积分 +50(17:49)
138****2888 刚刚获得 空气炸锅(18:56)
138****2000 刚刚获得 空气炸锅(10:00)
138****2111 刚刚获得 保温杯(11:07)
138****2222 刚刚获得 5 元无门槛券(12:14)
138****2333 刚刚获得 积分 +50(13:21)
138****2444 刚刚获得 空气炸锅(14:28)
138****2555 刚刚获得 保温杯(15:35)
138****2666 刚刚获得 5 元无门槛券(16:42)
138****2777 刚刚获得 积分 +50(17:49)
138****2888 刚刚获得 空气炸锅(18:56)
营销场景:单行 22px 高、胶囊容器、低速 22px/s 循环播报中奖记录。
<script setup lang="ts">
/** 场景:中奖播报条——单行紧凑、低速循环 */
import { BizSeamlessScroll } from 'vue3-ep-biz-lib'
interface WinRow {
user: string
prize: string
at: string
}
const wins: WinRow[] = Array.from({ length: 9 }, (_unused, i) => ({
user: `138****${String(2000 + i * 111).slice(-4)}`,
prize: ['空气炸锅', '保温杯', '5 元无门槛券', '积分 +50'][i % 4],
at: `${String(10 + i).padStart(2, '0')}:${String((i * 7) % 60).padStart(2, '0')}`,
}))
</script>
<template>
<div style="max-width: 420px; border: 1px solid var(--el-border-color-lighter); border-radius: 999px; padding: 8px 18px; display: flex; align-items: center; gap: 10px">
<span style="flex-shrink: 0; font-size: 12px; color: var(--el-color-warning); font-weight: 600">🎉 中奖播报</span>
<BizSeamlessScroll :list="wins" :speed="22" :height="22">
<template #item="{ item }">
<div style="height: 22px; line-height: 22px; font-size: 13px; color: var(--el-text-color-regular)">
{{ item.user }} 刚刚获得 {{ item.prize }}({{ item.at }})
</div>
</template>
</BizSeamlessScroll>
</div>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| list | T[] | —(必填) | 列表数据(泛型,item 插槽拿到强类型 item) |
| speed | number | 30 | 滚动速度(px/s) |
| direction | 'up' / 'down' | 'up' | 滚动方向 |
| hover-pause | boolean | true | hover 暂停 |
| height | number | — | 容器固定高度(px),超出裁剪滚动 |
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| — | — | 无自定义事件 |
Slots
| 插槽名 | 说明 |
|---|---|
| item | 列表项(作用域:item / index) |
Expose
| 属性/方法 | 说明 |
|---|---|
| pause() | 暂停滚动 |
| resume() | 恢复滚动 |
| reset() | 重置回起点 |