Skip to content

Commit 93d048b

Browse files
jasper-opsMoonofweisheng
authored andcommitted
feat: ✨ notice-bar增加垂直滚动功能
Closes: #122
1 parent 857c922 commit 93d048b

File tree

4 files changed

+152
-44
lines changed

4 files changed

+152
-44
lines changed

docs/component/notice-bar.md

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
```
8181

8282
## 多文本轮播
83+
8384
将一个`string[]`传递给`text`属性,即可开启多文本轮播,并且会在展示下一条文本时触发`next`事件,该事件接收一个`number`参数,代表的是当前展示的文本数组索引
8485

8586
```html
@@ -94,7 +95,8 @@ const textArray = ref([
9495
'该组件库基于uniapp ->Vue3, ts构建',
9596
'项目地址:https://github.com/Moonofweisheng/wot-design-uni',
9697
'我们的目标是打造最强uniapp组件库',
97-
'诚挚邀请大家共同建设'
98+
'诚挚邀请大家共同建设',
99+
'这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息'
98100
])
99101

100102
const onNext = (index: number) => {
@@ -103,35 +105,56 @@ const onNext = (index: number) => {
103105
}
104106
```
105107

108+
## 垂直滚动
109+
110+
1. `direction`传递`vertical`即可开启垂直滚动,目前仅支持一个方向的垂直滚动
111+
2. `text`为数组时才会进行滚动
112+
3. 垂直滚动时会提供`vertical-{index}`的插槽,`text`有几个成员就有几个这样的插槽,索引从 0 开始
113+
4. **插槽内容不应该改变高度,例如设置 height、padding 等可能改变高度的样式,否则会导致滚动不准确**
114+
115+
```html
116+
<demo-block title="垂直滚动">
117+
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="textArray" :speed="0.5" :delay="3" custom-class="space" />
118+
<wd-notice-bar prefix="warn-bold" direction="vertical" text="只有一条消息不会滚动" :speed="0.5" :delay="3" custom-class="space" />
119+
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="['', '这是文本内容,index:1', '这是文本内容,index:2']" :speed="0.5" :delay="3">
120+
<template #vertical-0>
121+
<!-- 插槽内容不应该改变高度,例如设置height、padding等可能改变高度的样式,否则会导致滚动不准确 -->
122+
<view style="font-weight: 700">索引为0的插槽</view>
123+
</template>
124+
</wd-notice-bar>
125+
</demo-block>
126+
```
106127

107128
## Attributes
108129

109-
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
110-
| ---------------- | -------------------------------------- | ------- | ----------------------- | ------- | -------- |
111-
| text | 设置通知栏文案 | `string` `string[]` | - | - | - |
112-
| type | 设置通知栏类型 | `string` | info / warning / danger | warning | - |
113-
| prefix | 设置左侧图标,使用 icon 章节中的图标名 | `string` | - | - | - |
114-
| scrollable | 是否可以滚动 | `boolean` | - | true | - |
115-
| delay | 滚动动画初始延时,单位 秒(s) | `number` | - | 1 | - |
116-
| speed | 滚动速度,单位 px/s | `number` | - | 50 | - |
117-
| closable | 是否可以关闭 | `boolean` | - | false | - |
118-
| wrapable | 是否换行展示 | `boolean` | - | false | - |
119-
| color | 文字、图标颜色 | `string` | - | - | - |
120-
| background-color | 背景颜色 | `string` | - | - | - |
130+
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
131+
| ---------------- | -------------------------------------- | -------------------------- | ----------------------- | ------------ | -------- |
132+
| text | 设置通知栏文案 | `string` `string[]` | - | - | - |
133+
| type | 设置通知栏类型 | `string` | info / warning / danger | warning | - |
134+
| prefix | 设置左侧图标,使用 icon 章节中的图标名 | `string` | - | - | - |
135+
| scrollable | 是否可以滚动 | `boolean` | - | true | - |
136+
| delay | 滚动动画初始延时,单位 秒(s) | `number` | - | 1 | - |
137+
| speed | 滚动速度,单位 px/s | `number` | - | 50 | - |
138+
| closable | 是否可以关闭 | `boolean` | - | false | - |
139+
| wrapable | 是否换行展示 | `boolean` | - | false | - |
140+
| color | 文字、图标颜色 | `string` | - | - | - |
141+
| background-color | 背景颜色 | `string` | - | - | - |
142+
| direction | 滚动方向 | `NoticeBarScrollDirection` | `horizontal` `vertical` | `horizontal` | - |
121143

122144
## Events
123145

124-
| 事件名称 | 说明 | 参数 | 最低版本 |
125-
| ---------- | -------------- | ---- | -------- |
126-
| close | 关闭按钮点击时 | - | - |
127-
| next | 下一次滚动前触发 | index: `number` | - |
146+
| 事件名称 | 说明 | 参数 | 最低版本 |
147+
| -------- | ---------------- | --------------- | -------- |
148+
| close | 关闭按钮点击时 | - | - |
149+
| next | 下一次滚动前触发 | index: `number` | - |
128150

129151
## Slot
130152

131-
| name | 说明 | 最低版本 |
132-
| ------ | -------- | -------- |
133-
| prefix | 前置图标 | - |
134-
| suffix | 后置插槽 | - |
153+
| name | 说明 | 最低版本 |
154+
| ---------------- | -------------------- | -------- |
155+
| prefix | 前置图标 | - |
156+
| suffix | 后置插槽 | - |
157+
| vertical-\{index\} | 垂直滚动时提供的插槽 | - |
135158

136159
## 外部样式类
137160

src/pages/noticeBar/Index.vue

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<demo-block title="基本用法">
1313
<wd-notice-bar text="这是一条消息提示信息这是一条消息提示信息这是一条消息提示信息" prefix="warn-bold" />
1414
</demo-block>
15+
1516
<demo-block title="类型修改">
1617
<wd-notice-bar
1718
type="danger"
@@ -25,9 +26,11 @@
2526
prefix="check-outline"
2627
/>
2728
</demo-block>
29+
2830
<demo-block title="禁止滚动">
2931
<wd-notice-bar :scrollable="false" text="通知被禁或时段内消息屏蔽可能造成消…" prefix="warn-bold"></wd-notice-bar>
3032
</demo-block>
33+
3134
<demo-block title="插槽">
3235
<wd-notice-bar :scrollable="false">
3336
<template #prefix>
@@ -39,16 +42,19 @@
3942
</template>
4043
</wd-notice-bar>
4144
</demo-block>
45+
4246
<demo-block title="可关闭的">
4347
<wd-notice-bar text="挂起后,电脑与手机均不会有新客户接入。挂起后,电脑与手机均不会有新客户接入。" closable prefix="warn-bold" />
4448
</demo-block>
49+
4550
<demo-block title="多行展示">
4651
<wd-notice-bar
4752
text="这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息"
4853
wrapable
4954
:scrollable="false"
5055
/>
5156
</demo-block>
57+
5258
<demo-block title="自定义颜色">
5359
<wd-notice-bar
5460
text="这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息"
@@ -61,6 +67,17 @@
6167
<demo-block title="多文本轮播">
6268
<wd-notice-bar :text="textArray" prefix="check-outline" @next="onNext" />
6369
</demo-block>
70+
71+
<demo-block title="垂直滚动">
72+
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="textArray" :speed="0.5" :delay="3" custom-class="space" />
73+
<wd-notice-bar prefix="warn-bold" direction="vertical" text="只有一条消息不会滚动" :speed="0.5" :delay="3" custom-class="space" />
74+
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="['', '这是文本内容,index:1', '这是文本内容,index:2']" :speed="0.5" :delay="3">
75+
<template #vertical-0>
76+
<!-- 插槽内容不应该改变高度,例如设置height、padding等可能改变高度的样式,否则会导致滚动不准确 -->
77+
<view style="font-weight: 700">索引为0的插槽</view>
78+
</template>
79+
</wd-notice-bar>
80+
</demo-block>
6481
</page-wraper>
6582
</template>
6683
<script lang="ts" setup>
@@ -71,7 +88,8 @@ const textArray = ref([
7188
'该组件库基于uniapp ->Vue3, ts构建',
7289
'项目地址:https://github.com/Moonofweisheng/wot-design-uni',
7390
'我们的目标是打造最强uniapp组件库',
74-
'诚挚邀请大家共同建设'
91+
'诚挚邀请大家共同建设',
92+
'这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息'
7593
])
7694
7795
const onNext = (index: number) => {

src/uni_modules/wot-design-uni/components/wd-notice-bar/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { PropType } from 'vue'
22
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
33

44
export type NoticeBarType = 'warning' | 'info' | 'danger' | ''
5+
export type NoticeBarScrollDirection = 'horizontal' | 'vertical'
56

67
export const noticeBarProps = {
78
...baseProps,
@@ -47,5 +48,9 @@ export const noticeBarProps = {
4748
/**
4849
* 背景颜色
4950
*/
50-
backgroundColor: String
51+
backgroundColor: String,
52+
/**
53+
* 滚动方向
54+
*/
55+
direction: makeStringProp<NoticeBarScrollDirection>('horizontal')
5156
}

src/uni_modules/wot-design-uni/components/wd-notice-bar/wd-notice-bar.vue

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@
88
<slot v-else name="prefix"></slot>
99
<view class="wd-notice-bar__wrap">
1010
<view class="wd-notice-bar__content" :animation="animation" @transitionend="animationEnd">
11-
<slot>{{ currentText }}</slot>
11+
<template v-if="direction === 'vertical'">
12+
<slot v-for="(item, i) in textArray" :key="item" :name="`vertical-${i}`">
13+
<view>{{ item }}</view>
14+
</slot>
15+
<slot v-if="textArray.length > 1" name="vertical-0">
16+
<view>{{ textArray[0] }}</view>
17+
</slot>
18+
</template>
19+
<slot v-else>{{ currentText }}</slot>
1220
</view>
1321
</view>
1422
<wd-icon v-if="closable" custom-class="wd-notice-bar__suffix" size="18px" name="close-bold" @click="handleClose"></wd-icon>
@@ -46,6 +54,9 @@ const noticeBarClass = ref<string>('')
4654
const currentIndex = ref(0)
4755
const textArray = computed(() => (Array.isArray(props.text) ? props.text : [props.text]))
4856
const currentText = computed(() => textArray.value[currentIndex.value])
57+
const verticalIndex = ref(0)
58+
const isHorizontal = computed(() => props.direction === 'horizontal')
59+
const isVertical = computed(() => props.direction === 'vertical')
4960
5061
const { proxy } = getCurrentInstance() as any
5162
watch(
@@ -71,10 +82,17 @@ onBeforeMount(() => {
7182
const emit = defineEmits(['close', 'next'])
7283
7384
function computedClass() {
74-
const { type, wrapable, scrollable } = props
85+
const { type, wrapable, scrollable, direction } = props
86+
7587
let noticeBarClasses: string[] = []
7688
type && noticeBarClasses.push(`is-${type}`)
77-
!wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--ellipse')
89+
90+
if (isHorizontal.value) {
91+
!wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--ellipse')
92+
} else {
93+
noticeBarClasses.push('wd-notice-bar--ellipse')
94+
}
95+
7896
wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--wrap')
7997
noticeBarClass.value = noticeBarClasses.join(' ')
8098
}
@@ -83,29 +101,50 @@ function handleClose() {
83101
emit('close')
84102
}
85103
86-
function initAnimation(duration: number, delay: number, translate: number) {
104+
function initAnimation({ duration, delay, translate }: { duration: number; delay: number; translate: number }) {
87105
const ani = uni
88106
.createAnimation({
89107
duration,
90108
delay
91109
})
92-
.translateX(translate)
110+
[isHorizontal.value ? 'translateX' : 'translateY'](translate)
93111
ani.step()
94112
return ani.export()
95113
}
96114
97-
function scroll() {
98-
Promise.all([getRect($wrap, false, proxy), getRect($content, false, proxy)]).then((rects) => {
99-
const [wrapRect, contentRect] = rects
100-
if (!wrapRect || !contentRect || !wrapRect.width || !contentRect.width) return
115+
function queryRect() {
116+
return Promise.all([getRect($wrap, false, proxy), getRect($content, false, proxy)])
117+
}
118+
119+
async function verticalAnimate(height: number) {
120+
const translate = -(height / (textArray.value.length + 1)) * (currentIndex.value + 1)
121+
122+
animation.value = initAnimation({
123+
duration: props.speed * 1000,
124+
delay: props.delay * 1000,
125+
translate
126+
})
127+
}
128+
129+
async function scroll() {
130+
const [wrapRect, contentRect] = await queryRect()
131+
if (!wrapRect.width || !contentRect.width || !contentRect.height) return
132+
133+
wrapWidth.value = wrapRect.width
101134
102-
const wrapWidthTemp = wrapRect.width
103-
const contentWidthTemp = contentRect.width
135+
if (isHorizontal.value) {
104136
if (props.scrollable) {
105-
animation.value = initAnimation((contentWidthTemp / props.speed) * 1000, props.delay * 1000, -contentWidthTemp)
106-
wrapWidth.value = wrapWidthTemp
137+
animation.value = initAnimation({
138+
duration: (contentRect.width / props.speed) * 1000,
139+
delay: props.delay * 1000,
140+
translate: -contentRect.width
141+
})
107142
}
108-
})
143+
} else {
144+
if (textArray.value.length > 1) {
145+
verticalAnimate(contentRect.height)
146+
}
147+
}
109148
}
110149
111150
function next() {
@@ -118,17 +157,40 @@ function next() {
118157
}
119158
120159
function animationEnd() {
121-
animation.value = initAnimation(0, 0, wrapWidth.value)
160+
if (isHorizontal.value) {
161+
animation.value = initAnimation({
162+
duration: 0,
163+
delay: 0,
164+
translate: wrapWidth.value + 1 // +1容错空间,防止露出来一丢丢
165+
})
166+
} else {
167+
if (++verticalIndex.value >= textArray.value.length) {
168+
verticalIndex.value = 0
169+
animation.value = initAnimation({
170+
duration: 0,
171+
delay: 0,
172+
translate: 0
173+
})
174+
}
175+
}
122176
123177
const timer = setTimeout(() => {
124178
next() // 更换下一条文本
125179
126-
nextTick(() => {
127-
// 因为文本会发生变化,所以contentWidth每一次都需要查询
128-
getRect($content, false, proxy).then((rect) => {
129-
if (!rect) return
130-
animation.value = initAnimation(((wrapWidth.value + rect.width!) / props.speed) * 1000, props.delay * 1000, -rect.width!)
131-
})
180+
nextTick(async () => {
181+
// 因为文本会发生变化,所以每一次都需要查询
182+
const [_, contentRect] = await queryRect()
183+
if (!contentRect.width || !contentRect.height) return
184+
185+
if (isHorizontal.value) {
186+
animation.value = initAnimation({
187+
duration: ((wrapWidth.value + contentRect.width) / props.speed) * 1000,
188+
delay: props.delay * 1000,
189+
translate: -contentRect.width
190+
})
191+
} else {
192+
verticalAnimate(contentRect.height)
193+
}
132194
})
133195
134196
clearTimeout(timer)

0 commit comments

Comments
 (0)