Skip to content

Commit

Permalink
feat: ✨ notice-bar增加垂直滚动功能
Browse files Browse the repository at this point in the history
Closes: Moonofweisheng#122
  • Loading branch information
jasper-ops committed Apr 6, 2024
1 parent 857c922 commit b811fca
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 44 deletions.
65 changes: 44 additions & 21 deletions docs/component/notice-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
```

## 多文本轮播

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

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

const onNext = (index: number) => {
Expand All @@ -103,35 +105,56 @@ const onNext = (index: number) => {
}
```

## 垂直滚动

1. `direction`传递`vertical`即可开启垂直滚动,目前仅支持一个方向的垂直滚动
2. `text`为数组时才会进行滚动
3. 垂直滚动时会提供`vertical-{index}`的插槽,`text`有几个成员就有几个这样的插槽,索引从 0 开始
4. **插槽内容不应该改变高度,例如设置 height、padding 等可能改变高度的样式,否则会导致滚动不准确**

```html
<demo-block title="垂直滚动">
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="textArray" :speed="0.5" :delay="3" custom-class="space" />
<wd-notice-bar prefix="warn-bold" direction="vertical" text="只有一条消息不会滚动" :speed="0.5" :delay="3" custom-class="space" />
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="['', '这是文本内容,index:1', '这是文本内容,index:2']" :speed="0.5" :delay="3">
<template #vertical-0>
<!-- 插槽内容不应该改变高度,例如设置height、padding等可能改变高度的样式,否则会导致滚动不准确 -->
<view style="font-weight: 700">索引为0的插槽</view>
</template>
</wd-notice-bar>
</demo-block>
```

## Attributes

| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
| ---------------- | -------------------------------------- | ------- | ----------------------- | ------- | -------- |
| text | 设置通知栏文案 | `string` `string[]` | - | - | - |
| type | 设置通知栏类型 | `string` | info / warning / danger | warning | - |
| prefix | 设置左侧图标,使用 icon 章节中的图标名 | `string` | - | - | - |
| scrollable | 是否可以滚动 | `boolean` | - | true | - |
| delay | 滚动动画初始延时,单位 秒(s) | `number` | - | 1 | - |
| speed | 滚动速度,单位 px/s | `number` | - | 50 | - |
| closable | 是否可以关闭 | `boolean` | - | false | - |
| wrapable | 是否换行展示 | `boolean` | - | false | - |
| color | 文字、图标颜色 | `string` | - | - | - |
| background-color | 背景颜色 | `string` | - | - | - |
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
| ---------------- | -------------------------------------- | -------------------------- | ----------------------- | ------------ | -------- |
| text | 设置通知栏文案 | `string` `string[]` | - | - | - |
| type | 设置通知栏类型 | `string` | info / warning / danger | warning | - |
| prefix | 设置左侧图标,使用 icon 章节中的图标名 | `string` | - | - | - |
| scrollable | 是否可以滚动 | `boolean` | - | true | - |
| delay | 滚动动画初始延时,单位 秒(s) | `number` | - | 1 | - |
| speed | 滚动速度,单位 px/s | `number` | - | 50 | - |
| closable | 是否可以关闭 | `boolean` | - | false | - |
| wrapable | 是否换行展示 | `boolean` | - | false | - |
| color | 文字、图标颜色 | `string` | - | - | - |
| background-color | 背景颜色 | `string` | - | - | - |
| direction | 滚动方向 | `NoticeBarScrollDirection` | `horizontal` `vertical` | `horizontal` | - |

## Events

| 事件名称 | 说明 | 参数 | 最低版本 |
| ---------- | -------------- | ---- | -------- |
| close | 关闭按钮点击时 | - | - |
| next | 下一次滚动前触发 | index: `number` | - |
| 事件名称 | 说明 | 参数 | 最低版本 |
| -------- | ---------------- | --------------- | -------- |
| close | 关闭按钮点击时 | - | - |
| next | 下一次滚动前触发 | index: `number` | - |

## Slot

| name | 说明 | 最低版本 |
| ------ | -------- | -------- |
| prefix | 前置图标 | - |
| suffix | 后置插槽 | - |
| name | 说明 | 最低版本 |
| ---------------- | -------------------- | -------- |
| prefix | 前置图标 | - |
| suffix | 后置插槽 | - |
| vertical-\{index\} | 垂直滚动时提供的插槽 | - |

## 外部样式类

Expand Down
20 changes: 19 additions & 1 deletion src/pages/noticeBar/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<demo-block title="基本用法">
<wd-notice-bar text="这是一条消息提示信息这是一条消息提示信息这是一条消息提示信息" prefix="warn-bold" />
</demo-block>

<demo-block title="类型修改">
<wd-notice-bar
type="danger"
Expand All @@ -25,9 +26,11 @@
prefix="check-outline"
/>
</demo-block>

<demo-block title="禁止滚动">
<wd-notice-bar :scrollable="false" text="通知被禁或时段内消息屏蔽可能造成消…" prefix="warn-bold"></wd-notice-bar>
</demo-block>

<demo-block title="插槽">
<wd-notice-bar :scrollable="false">
<template #prefix>
Expand All @@ -39,16 +42,19 @@
</template>
</wd-notice-bar>
</demo-block>

<demo-block title="可关闭的">
<wd-notice-bar text="挂起后,电脑与手机均不会有新客户接入。挂起后,电脑与手机均不会有新客户接入。" closable prefix="warn-bold" />
</demo-block>

<demo-block title="多行展示">
<wd-notice-bar
text="这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息"
wrapable
:scrollable="false"
/>
</demo-block>

<demo-block title="自定义颜色">
<wd-notice-bar
text="这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息"
Expand All @@ -61,6 +67,17 @@
<demo-block title="多文本轮播">
<wd-notice-bar :text="textArray" prefix="check-outline" @next="onNext" />
</demo-block>

<demo-block title="垂直滚动">
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="textArray" :speed="0.5" :delay="3" custom-class="space" />
<wd-notice-bar prefix="warn-bold" direction="vertical" text="只有一条消息不会滚动" :speed="0.5" :delay="3" custom-class="space" />
<wd-notice-bar prefix="warn-bold" direction="vertical" :text="['', '这是文本内容,index:1', '这是文本内容,index:2']" :speed="0.5" :delay="3">
<template #vertical-0>
<!-- 插槽内容不应该改变高度,例如设置height、padding等可能改变高度的样式,否则会导致滚动不准确 -->
<view style="font-weight: 700">索引为0的插槽</view>
</template>
</wd-notice-bar>
</demo-block>
</page-wraper>
</template>
<script lang="ts" setup>
Expand All @@ -71,7 +88,8 @@ const textArray = ref([
'该组件库基于uniapp ->Vue3, ts构建',
'项目地址:https://github.com/Moonofweisheng/wot-design-uni',
'我们的目标是打造最强uniapp组件库',
'诚挚邀请大家共同建设'
'诚挚邀请大家共同建设',
'这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息,这是一条消息提示信息'
])
const onNext = (index: number) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { PropType } from 'vue'
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'

export type NoticeBarType = 'warning' | 'info' | 'danger' | ''
export type NoticeBarScrollDirection = 'horizontal' | 'vertical'

export const noticeBarProps = {
...baseProps,
Expand Down Expand Up @@ -47,5 +48,9 @@ export const noticeBarProps = {
/**
* 背景颜色
*/
backgroundColor: String
backgroundColor: String,
/**
* 滚动方向
*/
direction: makeStringProp<NoticeBarScrollDirection>('horizontal')
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
<slot v-else name="prefix"></slot>
<view class="wd-notice-bar__wrap">
<view class="wd-notice-bar__content" :animation="animation" @transitionend="animationEnd">
<slot>{{ currentText }}</slot>
<template v-if="direction === 'vertical'">
<slot v-for="(item, i) in textArray" :key="item" :name="`vertical-${i}`">
<view>{{ item }}</view>
</slot>
<slot v-if="textArray.length > 1" name="vertical-0">
<view>{{ textArray[0] }}</view>
</slot>
</template>
<slot v-else>{{ currentText }}</slot>
</view>
</view>
<wd-icon v-if="closable" custom-class="wd-notice-bar__suffix" size="18px" name="close-bold" @click="handleClose"></wd-icon>
Expand Down Expand Up @@ -46,6 +54,9 @@ const noticeBarClass = ref<string>('')
const currentIndex = ref(0)
const textArray = computed(() => (Array.isArray(props.text) ? props.text : [props.text]))
const currentText = computed(() => textArray.value[currentIndex.value])
const verticalIndex = ref(0)
const isHorizontal = computed(() => props.direction === 'horizontal')
const isVertical = computed(() => props.direction === 'vertical')
const { proxy } = getCurrentInstance() as any
watch(
Expand All @@ -71,10 +82,17 @@ onBeforeMount(() => {
const emit = defineEmits(['close', 'next'])
function computedClass() {
const { type, wrapable, scrollable } = props
const { type, wrapable, scrollable, direction } = props
let noticeBarClasses: string[] = []
type && noticeBarClasses.push(`is-${type}`)
!wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--ellipse')
if (isHorizontal.value) {
!wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--ellipse')
} else {
noticeBarClasses.push('wd-notice-bar--ellipse')
}
wrapable && !scrollable && noticeBarClasses.push('wd-notice-bar--wrap')
noticeBarClass.value = noticeBarClasses.join(' ')
}
Expand All @@ -83,29 +101,50 @@ function handleClose() {
emit('close')
}
function initAnimation(duration: number, delay: number, translate: number) {
function initAnimation({ duration, delay, translate }: { duration: number; delay: number; translate: number }) {
const ani = uni
.createAnimation({
duration,
delay
})
.translateX(translate)
[isHorizontal.value ? 'translateX' : 'translateY'](translate)
ani.step()
return ani.export()
}
function scroll() {
Promise.all([getRect($wrap, false, proxy), getRect($content, false, proxy)]).then((rects) => {
const [wrapRect, contentRect] = rects
if (!wrapRect || !contentRect || !wrapRect.width || !contentRect.width) return
function queryRect() {
return Promise.all([getRect($wrap, false, proxy), getRect($content, false, proxy)])
}
async function verticalAnimate(height: number) {
const translate = -(height / (textArray.value.length + 1)) * (currentIndex.value + 1)
animation.value = initAnimation({
duration: props.speed * 1000,
delay: props.delay * 1000,
translate
})
}
async function scroll() {
const [wrapRect, contentRect] = await queryRect()
if (!wrapRect.width || !contentRect.width || !contentRect.height) return
wrapWidth.value = wrapRect.width
const wrapWidthTemp = wrapRect.width
const contentWidthTemp = contentRect.width
if (isHorizontal.value) {
if (props.scrollable) {
animation.value = initAnimation((contentWidthTemp / props.speed) * 1000, props.delay * 1000, -contentWidthTemp)
wrapWidth.value = wrapWidthTemp
animation.value = initAnimation({
duration: (contentRect.width / props.speed) * 1000,
delay: props.delay * 1000,
translate: -contentRect.width
})
}
})
} else {
if (textArray.value.length > 1) {
verticalAnimate(contentRect.height)
}
}
}
function next() {
Expand All @@ -118,17 +157,40 @@ function next() {
}
function animationEnd() {
animation.value = initAnimation(0, 0, wrapWidth.value)
if (isHorizontal.value) {
animation.value = initAnimation({
duration: 0,
delay: 0,
translate: wrapWidth.value + 1 // +1容错空间,防止露出来一丢丢
})
} else {
if (++verticalIndex.value >= textArray.value.length) {
verticalIndex.value = 0
animation.value = initAnimation({
duration: 0,
delay: 0,
translate: 0
})
}
}
const timer = setTimeout(() => {
next() // 更换下一条文本
nextTick(() => {
// 因为文本会发生变化,所以contentWidth每一次都需要查询
getRect($content, false, proxy).then((rect) => {
if (!rect) return
animation.value = initAnimation(((wrapWidth.value + rect.width!) / props.speed) * 1000, props.delay * 1000, -rect.width!)
})
nextTick(async () => {
// 因为文本会发生变化,所以每一次都需要查询
const [_, contentRect] = await queryRect()
if (!contentRect.width || !contentRect.height) return
if (isHorizontal.value) {
animation.value = initAnimation({
duration: ((wrapWidth.value + contentRect.width) / props.speed) * 1000,
delay: props.delay * 1000,
translate: -contentRect.width
})
} else {
verticalAnimate(contentRect.height)
}
})
clearTimeout(timer)
Expand Down

0 comments on commit b811fca

Please sign in to comment.