mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-05 19:41:42 +08:00
feat(Barrage): add new Barrage component (#11760)
* fix(Swipe): props changed but component didn't * fix(Swipe): target watch windowWidth * Update packages/vant/src/swipe/Swipe.tsx * feat(Barrage): add new Barrage component * feat(Barrage): add will-change * feat: only use transform move * refactor: only use transform * refactor: only use transform * fix(Barrage): many problems * fix(Barrage): many problems * fix(Barrage): many problems * fix(Barrage): many problems * fix(Barrage): many problems * fix(Barrage): many problems --------- Co-authored-by: zhousg <345313727@qq.com> Co-authored-by: neverland <jait.chen@foxmail.com>
This commit is contained in:
parent
5a859ff019
commit
cb0f859a83
166
packages/vant/src/barrage/Barrage.tsx
Normal file
166
packages/vant/src/barrage/Barrage.tsx
Normal file
@ -0,0 +1,166 @@
|
||||
import {
|
||||
defineComponent,
|
||||
onMounted,
|
||||
ref,
|
||||
type ExtractPropTypes,
|
||||
nextTick,
|
||||
watch,
|
||||
} from 'vue';
|
||||
import { useExpose } from '../composables/use-expose';
|
||||
import {
|
||||
createNamespace,
|
||||
makeArrayProp,
|
||||
makeNumberProp,
|
||||
makeNumericProp,
|
||||
truthProp,
|
||||
} from '../utils';
|
||||
import { BarrageExpose } from './types';
|
||||
|
||||
export interface BarrageItem {
|
||||
id: string | number;
|
||||
text: string | number;
|
||||
}
|
||||
|
||||
export const barrageProps = {
|
||||
top: makeNumericProp(10),
|
||||
rows: makeNumericProp(4),
|
||||
speed: makeNumericProp(4000),
|
||||
autoPlay: truthProp,
|
||||
delay: makeNumberProp(300),
|
||||
modelValue: makeArrayProp<BarrageItem>(),
|
||||
};
|
||||
|
||||
export type BarrageProps = ExtractPropTypes<typeof barrageProps>;
|
||||
|
||||
const [name, bem] = createNamespace('barrage');
|
||||
|
||||
export default defineComponent({
|
||||
name,
|
||||
|
||||
props: barrageProps,
|
||||
|
||||
emits: ['update:modelValue'],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const barrageWrapper = ref<HTMLDivElement>();
|
||||
const className = bem('item') as string;
|
||||
const total = ref(0);
|
||||
const barrageItems: HTMLSpanElement[] = [];
|
||||
|
||||
const createBarrageItem = (
|
||||
text: string | number,
|
||||
delay: number = props.delay
|
||||
) => {
|
||||
const item = document.createElement('span');
|
||||
item.className = className;
|
||||
item.innerText = String(text);
|
||||
|
||||
item.style.animationDuration = `${props.speed}ms`;
|
||||
item.style.animationDelay = `${delay}ms`;
|
||||
item.style.animationName = 'van-barrage';
|
||||
item.style.animationTimingFunction = 'linear';
|
||||
|
||||
return item;
|
||||
};
|
||||
|
||||
const isInitBarrage = ref(true);
|
||||
const isPlay = ref(props.autoPlay);
|
||||
|
||||
const appendBarrageItem = ({ id, text }: BarrageItem, i: number) => {
|
||||
const item = createBarrageItem(
|
||||
text,
|
||||
isInitBarrage.value ? i * props.delay : undefined
|
||||
);
|
||||
if (!props.autoPlay && isPlay.value === false) {
|
||||
item.style.animationPlayState = 'paused';
|
||||
}
|
||||
barrageWrapper.value?.append(item);
|
||||
total.value++;
|
||||
|
||||
const top =
|
||||
((total.value - 1) % +props.rows) * item.offsetHeight + +props.top;
|
||||
item.style.top = `${top}px`;
|
||||
item.dataset.id = String(id);
|
||||
barrageItems.push(item);
|
||||
|
||||
item.addEventListener('animationend', () => {
|
||||
emit(
|
||||
'update:modelValue',
|
||||
[...props.modelValue].filter((v) => String(v.id) !== item.dataset.id)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const updateBarrages = (
|
||||
newValue: BarrageItem[],
|
||||
oldValue: BarrageItem[]
|
||||
) => {
|
||||
const map = new Map(oldValue.map((item) => [item.id, item]));
|
||||
|
||||
newValue.forEach((item, i) => {
|
||||
if (map.has(item.id)) {
|
||||
map.delete(item.id);
|
||||
} else {
|
||||
// add
|
||||
appendBarrageItem(item, i);
|
||||
}
|
||||
});
|
||||
|
||||
map.forEach((item) => {
|
||||
// remove
|
||||
const index = barrageItems.findIndex(
|
||||
(span) => span.dataset.id === String(item.id)
|
||||
);
|
||||
if (index > -1) {
|
||||
barrageItems[index].remove();
|
||||
barrageItems.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
isInitBarrage.value = false;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.modelValue.slice(),
|
||||
(newValue, oldValue) => updateBarrages(newValue ?? [], oldValue ?? []),
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const rootStyle = ref<{
|
||||
'--move-distance'?: string;
|
||||
}>({});
|
||||
|
||||
onMounted(async () => {
|
||||
rootStyle.value[
|
||||
'--move-distance'
|
||||
] = `-${barrageWrapper.value?.offsetWidth}px`;
|
||||
await nextTick();
|
||||
updateBarrages(props.modelValue, []);
|
||||
});
|
||||
|
||||
const play = () => {
|
||||
isPlay.value = true;
|
||||
barrageItems.forEach((item) => {
|
||||
item.style.animationPlayState = 'running';
|
||||
});
|
||||
};
|
||||
|
||||
const pause = () => {
|
||||
isPlay.value = false;
|
||||
barrageItems.forEach((item) => {
|
||||
item.style.animationPlayState = 'paused';
|
||||
});
|
||||
};
|
||||
|
||||
useExpose<BarrageExpose>({
|
||||
play,
|
||||
pause,
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div class={bem()} ref={barrageWrapper} style={rootStyle.value}>
|
||||
{slots.default?.()}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
148
packages/vant/src/barrage/README.md
Normal file
148
packages/vant/src/barrage/README.md
Normal file
@ -0,0 +1,148 @@
|
||||
# Barrage
|
||||
|
||||
### Intro
|
||||
|
||||
To realize the critical subtitle function when watching the video.
|
||||
|
||||
### Install
|
||||
|
||||
Register component globally via `app.use`, refer to [Component Registration](#/en-US/advanced-usage#zu-jian-zhu-ce) for more registration ways.
|
||||
|
||||
```js
|
||||
import { createApp } from 'vue';
|
||||
import { Barrage } from 'vant';
|
||||
|
||||
const app = createApp();
|
||||
app.use(Barrage);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```html
|
||||
<van-barrage v-model="list">
|
||||
<div class="video" style="width: 100%; height: 150px"></div>
|
||||
</van-barrage>
|
||||
<van-space style="margin-top: 10px">
|
||||
<van-button @click="add" type="primary" size="small"> barrage </van-button>
|
||||
</van-space>
|
||||
```
|
||||
|
||||
```ts
|
||||
export default {
|
||||
setup() {
|
||||
const defaultList = [
|
||||
{ id: 100, text: 'Lightweight' },
|
||||
{ id: 101, text: 'Customizable' },
|
||||
{ id: 102, text: 'Mobile' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: 'Library' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
];
|
||||
|
||||
const list = ref([...defaultList]);
|
||||
const add = () => {
|
||||
list.value.push({ id: Math.random(), text: 'Barrage' });
|
||||
};
|
||||
return { list, add };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Imitate video barrage
|
||||
|
||||
```html
|
||||
<van-barrage v-model="list" ref="barrage" :auto-play="false">
|
||||
<div class="video" style="width: 100%; height: 150px"></div>
|
||||
</van-barrage>
|
||||
<van-space style="margin-top: 10px">
|
||||
<van-button @click="add" type="primary" size="small" :disabled="!isPlay">
|
||||
barrage
|
||||
</van-button>
|
||||
<van-button @click="toggle()" size="small">
|
||||
{{ isPlay ? 'pause' : 'play' }}
|
||||
</van-button>
|
||||
</van-space>
|
||||
```
|
||||
|
||||
```ts
|
||||
export default {
|
||||
setup() {
|
||||
const defaultList = [
|
||||
{ id: 100, text: 'Lightweight' },
|
||||
{ id: 101, text: 'Customizable' },
|
||||
{ id: 102, text: 'Mobile' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: 'Library' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
];
|
||||
|
||||
const list = ref([...defaultList]);
|
||||
const barrage = ref<BarrageInstance>();
|
||||
const add = () => {
|
||||
list.value.push({ id: Math.random(), text: 'Barrage' });
|
||||
};
|
||||
|
||||
const [isPlay, toggle] = useToggle(false);
|
||||
|
||||
watch(isPlay, () => {
|
||||
if (isPlay.value) barrage.value?.play();
|
||||
else barrage.value?.pause();
|
||||
});
|
||||
|
||||
return { list, barrage, isPlay, toggle, add };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| v-model | Barrage data | _BarrageItem[]_ | - |
|
||||
| auto-play | Whether to play the bullet screen automatically | _boolean_ | `true` |
|
||||
| rows | The number of lines of text | _number \| string_ | `4` |
|
||||
| top | Spacing between the top of the barrage area, unit `px` | _number \| string_ | `10` |
|
||||
| speed | Speed of passing, unit `ms` | _number \| string_ | `4000` |
|
||||
| delay | Barrage animation delay, unit `ms` | _number_ | `300` |
|
||||
|
||||
### Methods
|
||||
|
||||
Use [ref](https://v3.vuejs.org/guide/component-template-refs.html) to get Barrage instance and call instance methods.
|
||||
|
||||
| Name | Description | Attribute | Return value |
|
||||
| ----- | ------------- | --------- | ------------ |
|
||||
| play | Play barrage | - | - |
|
||||
| pause | Pause barrage | - | - |
|
||||
|
||||
### Slots
|
||||
|
||||
| Name | Description |
|
||||
| ------- | ------------ |
|
||||
| default | Default slot |
|
||||
|
||||
### Types
|
||||
|
||||
The component exports the following type definitions:
|
||||
|
||||
```ts
|
||||
import type { BarrageProps, BarrageItem, BarrageInstance } from 'vant';
|
||||
```
|
||||
|
||||
## Theming
|
||||
|
||||
### CSS Variables
|
||||
|
||||
The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](#/en-US/config-provider).
|
||||
|
||||
| Name | Default Value | Description |
|
||||
| --- | --- | --- |
|
||||
| --van-barrage-font-size | _16px_ | - |
|
||||
| --van-barrage-space | _10px_ | - |
|
||||
| --van-barrage-color | _var(--van-white)_ | - |
|
||||
| --van-barrage-font | _-apple-system-font, Helvetica Neue, Arial, sans-serif_ | - |
|
153
packages/vant/src/barrage/README.zh-CN.md
Normal file
153
packages/vant/src/barrage/README.zh-CN.md
Normal file
@ -0,0 +1,153 @@
|
||||
# Barrage 弹幕
|
||||
|
||||
### 介绍
|
||||
|
||||
实现观看视频时弹出的评论性字幕功能。
|
||||
|
||||
### 引入
|
||||
|
||||
通过以下方式来全局注册组件,更多注册方式请参考[组件注册](#/zh-CN/advanced-usage#zu-jian-zhu-ce)。
|
||||
|
||||
```js
|
||||
import { createApp } from 'vue';
|
||||
import { Barrage } from 'vant';
|
||||
|
||||
const app = createApp();
|
||||
app.use(Barrage);
|
||||
```
|
||||
|
||||
## 代码演示
|
||||
|
||||
### 基础用法
|
||||
|
||||
可以通过 `v-model` 双向绑定弹幕数据,`Barrage` 会在组件区域内播放文字弹幕,使用数组数据 `push()` 可以发送弹幕文字。
|
||||
|
||||
```html
|
||||
<van-barrage v-model="list">
|
||||
<div class="video" style="width: 100%; height: 150px"></div>
|
||||
</van-barrage>
|
||||
<van-space style="margin-top: 10px">
|
||||
<van-button @click="add" type="primary" size="small"> 弹幕 </van-button>
|
||||
</van-space>
|
||||
```
|
||||
|
||||
```ts
|
||||
export default {
|
||||
setup() {
|
||||
const defaultList = [
|
||||
{ id: 100, text: '轻量' },
|
||||
{ id: 101, text: '可定制的' },
|
||||
{ id: 102, text: '移动端' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: '组件库' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
];
|
||||
|
||||
const list = ref([...defaultList]);
|
||||
const add = () => {
|
||||
list.value.push({ id: Math.random(), text: 'Barrage' });
|
||||
};
|
||||
|
||||
return { list, add };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 模拟视频弹幕
|
||||
|
||||
设置 `auto-play` 为 `false` 属性后,需要使用 `play()` 进行弹幕播放,暂停可以使用 `pause()` 实现。
|
||||
|
||||
```html
|
||||
<van-barrage v-model="list" ref="barrage" :auto-play="false">
|
||||
<div class="video" style="width: 100%; height: 150px"></div>
|
||||
</van-barrage>
|
||||
<van-space style="margin-top: 10px">
|
||||
<van-button @click="add" type="primary" size="small" :disabled="!isPlay">
|
||||
弹幕
|
||||
</van-button>
|
||||
<van-button @click="toggle()" size="small">
|
||||
{{ isPlay ? '暂停' : '开始' }}
|
||||
</van-button>
|
||||
</van-space>
|
||||
```
|
||||
|
||||
```ts
|
||||
export default {
|
||||
setup() {
|
||||
const defaultList = [
|
||||
{ id: 100, text: '轻量' },
|
||||
{ id: 101, text: '可定制的' },
|
||||
{ id: 102, text: '移动端' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: '组件库' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
];
|
||||
|
||||
const list = ref([...defaultList]);
|
||||
const barrage = ref<BarrageInstance>();
|
||||
const add = () => {
|
||||
list.value.push({ id: Math.random(), text: 'Barrage' });
|
||||
};
|
||||
|
||||
const [isPlay, toggle] = useToggle(false);
|
||||
|
||||
watch(isPlay, () => {
|
||||
if (isPlay.value) barrage.value?.play();
|
||||
else barrage.value?.pause();
|
||||
});
|
||||
|
||||
return { list, barrage, isPlay, toggle, add };
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --------- | ------------------------------- | ------------------ | ------ |
|
||||
| v-model | 弹幕数据 | _BarrageItem[]_ | - |
|
||||
| auto-play | 是否自动播放弹幕 | _boolean_ | `true` |
|
||||
| rows | 弹幕文字行数 | _number \| string_ | `4` |
|
||||
| top | 弹幕文字区域顶部间距,单位 `px` | _number \| string_ | `10` |
|
||||
| speed | 文字滑过容器的时间,单位 `ms` | _number \| string_ | `4000` |
|
||||
| delay | 弹幕动画延时,单位 `ms` | _number_ | `300` |
|
||||
|
||||
### 方法
|
||||
|
||||
通过 ref 可以获取到 Barrage 实例并调用实例方法,详见[组件实例方法](#/zh-CN/advanced-usage#zu-jian-shi-li-fang-fa)。
|
||||
|
||||
| 方法名 | 说明 | 参数 | 返回值 |
|
||||
| ------ | -------- | ---- | ------ |
|
||||
| play | 播放弹幕 | - | - |
|
||||
| pause | 暂停弹幕 | - | - |
|
||||
|
||||
### Slots
|
||||
|
||||
| 名称 | 说明 |
|
||||
| ------- | -------------- |
|
||||
| default | 弹幕组件子元素 |
|
||||
|
||||
### 类型定义
|
||||
|
||||
组件导出以下类型定义:
|
||||
|
||||
```ts
|
||||
import type { BarrageProps, BarrageItem, BarrageInstance } from 'vant';
|
||||
```
|
||||
|
||||
## 主题定制
|
||||
|
||||
### 样式变量
|
||||
|
||||
组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/config-provider)。
|
||||
|
||||
| 名称 | 默认值 | 描述 |
|
||||
| --- | --- | --- |
|
||||
| --van-barrage-font-size | _16px_ | - |
|
||||
| --van-barrage-space | _10px_ | - |
|
||||
| --van-barrage-color | _var(--van-white)_ | - |
|
||||
| --van-barrage-font | _-apple-system-font, Helvetica Neue, Arial, sans-serif_ | - |
|
103
packages/vant/src/barrage/demo/index.vue
Normal file
103
packages/vant/src/barrage/demo/index.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
import VanBarrage, { BarrageInstance } from '..';
|
||||
import VanButton from '../../button';
|
||||
import VanSpace from '../../space';
|
||||
import { useTranslate } from '../../../docs/site';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useToggle } from '@vant/use';
|
||||
|
||||
const t = useTranslate({
|
||||
'zh-CN': {
|
||||
barrage: '弹幕',
|
||||
play: '开始',
|
||||
pause: '暂停',
|
||||
videoBarrage: '模仿视频弹幕',
|
||||
lightweight: '轻量',
|
||||
customizable: '可定制的',
|
||||
mobile: '移动端',
|
||||
library: '组件库',
|
||||
},
|
||||
'en-US': {
|
||||
barrage: 'barrage',
|
||||
play: 'play',
|
||||
pause: 'pause',
|
||||
videoBarrage: 'Imitate video barrage',
|
||||
lightweight: 'Lightweight',
|
||||
customizable: 'Customizable',
|
||||
mobile: 'Mobile',
|
||||
library: 'Library',
|
||||
},
|
||||
});
|
||||
|
||||
const defaultList = [
|
||||
{ id: 100, text: t('lightweight') },
|
||||
{ id: 101, text: t('customizable') },
|
||||
{ id: 102, text: t('mobile') },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: t('library') },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
];
|
||||
|
||||
const list = ref([...defaultList]);
|
||||
const add = () => {
|
||||
list.value.push({ id: Math.random(), text: 'Barrage' });
|
||||
};
|
||||
|
||||
const videoList = ref([...defaultList]);
|
||||
const videoBarrage = ref<BarrageInstance>();
|
||||
const videoAdd = () => {
|
||||
videoList.value.push({ id: Math.random(), text: 'Barrage' });
|
||||
};
|
||||
|
||||
const [isPlay, toggle] = useToggle(false);
|
||||
|
||||
watch(isPlay, () => {
|
||||
if (isPlay.value) videoBarrage.value?.play();
|
||||
else videoBarrage.value?.pause();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<demo-block :title="t('basicUsage')">
|
||||
<van-barrage v-model="list">
|
||||
<div class="video"></div>
|
||||
</van-barrage>
|
||||
<van-space style="margin-top: 10px">
|
||||
<van-button @click="add" type="primary" size="small">
|
||||
{{ t('barrage') }}
|
||||
</van-button>
|
||||
</van-space>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="t('videoBarrage')">
|
||||
<van-barrage v-model="videoList" ref="videoBarrage" :auto-play="false">
|
||||
<div class="video"></div>
|
||||
</van-barrage>
|
||||
<van-space style="margin-top: 10px">
|
||||
<van-button
|
||||
@click="videoAdd"
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!isPlay"
|
||||
>
|
||||
{{ t('barrage') }}
|
||||
</van-button>
|
||||
<van-button @click="toggle()" size="small">
|
||||
{{ isPlay ? t('pause') : t('play') }}
|
||||
</van-button>
|
||||
</van-space>
|
||||
</demo-block>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.demo-barrage {
|
||||
padding: var(--van-padding-sm);
|
||||
background-color: var(--van-background-2);
|
||||
.video {
|
||||
background-color: var(--van-gray-2);
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
</style>
|
39
packages/vant/src/barrage/index.less
Normal file
39
packages/vant/src/barrage/index.less
Normal file
@ -0,0 +1,39 @@
|
||||
:root {
|
||||
--van-barrage-font-size: 16px;
|
||||
--van-barrage-space: 10px;
|
||||
--van-barrage-font: -apple-system-font, helvetica neue, arial, sans-serif;
|
||||
--van-barrage-color: var(--van-white);
|
||||
}
|
||||
|
||||
.van-barrage {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&__item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
padding-bottom: var(--van-barrage-space);
|
||||
opacity: 0.75;
|
||||
line-height: 1;
|
||||
font-size: var(--van-barrage-font-size);
|
||||
font-family: var(--van-barrage-font);
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
color: var(--van-barrage-color);
|
||||
text-shadow: 1px 0 1px #000000, 0 1px 1px #000000, 0 -1px 1px #000000,
|
||||
-1px 0 1px #000000;
|
||||
user-select: none;
|
||||
will-change: transform;
|
||||
transform: translateX(110%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes van-barrage {
|
||||
from {
|
||||
transform: translateX(110%);
|
||||
}
|
||||
to {
|
||||
transform: translateX(var(--move-distance));
|
||||
}
|
||||
}
|
15
packages/vant/src/barrage/index.ts
Normal file
15
packages/vant/src/barrage/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { withInstall } from '../utils';
|
||||
import _Barrage from './Barrage';
|
||||
|
||||
export const Barrage = withInstall(_Barrage);
|
||||
export default Barrage;
|
||||
|
||||
export { barrageProps } from './Barrage';
|
||||
export type { BarrageProps, BarrageItem } from './Barrage';
|
||||
export type { BarrageInstance } from './types';
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
VanBarrage: typeof Barrage;
|
||||
}
|
||||
}
|
134
packages/vant/src/barrage/test/__snapshots__/demo.spec.ts.snap
Normal file
134
packages/vant/src/barrage/test/__snapshots__/demo.spec.ts.snap
Normal file
@ -0,0 +1,134 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`should render demo and match snapshot 1`] = `
|
||||
<div>
|
||||
<div class="van-barrage"
|
||||
style="--move-distance: -100px;"
|
||||
>
|
||||
<div class="video">
|
||||
</div>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 0ms; animation-name: van-barrage; animation-timing-function: linear; top: 10px;"
|
||||
data-id="100"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 300ms; animation-name: van-barrage; animation-timing-function: linear; top: 110px;"
|
||||
data-id="101"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 600ms; animation-name: van-barrage; animation-timing-function: linear; top: 210px;"
|
||||
data-id="102"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 900ms; animation-name: van-barrage; animation-timing-function: linear; top: 310px;"
|
||||
data-id="103"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1200ms; animation-name: van-barrage; animation-timing-function: linear; top: 10px;"
|
||||
data-id="104"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1500ms; animation-name: van-barrage; animation-timing-function: linear; top: 110px;"
|
||||
data-id="105"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1800ms; animation-name: van-barrage; animation-timing-function: linear; top: 210px;"
|
||||
data-id="106"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="van-space van-space--horizontal van-space--align-center"
|
||||
style="margin-top: 10px;"
|
||||
>
|
||||
<div class="van-space-item">
|
||||
<button type="button"
|
||||
class="van-button van-button--primary van-button--small"
|
||||
>
|
||||
<div class="van-button__content">
|
||||
<span class="van-button__text">
|
||||
barrage
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="van-barrage"
|
||||
style="--move-distance: -100px;"
|
||||
>
|
||||
<div class="video">
|
||||
</div>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 0ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 10px;"
|
||||
data-id="100"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 300ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 110px;"
|
||||
data-id="101"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 600ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 210px;"
|
||||
data-id="102"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 900ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 310px;"
|
||||
data-id="103"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1200ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 10px;"
|
||||
data-id="104"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1500ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 110px;"
|
||||
data-id="105"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1800ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 210px;"
|
||||
data-id="106"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="van-space van-space--horizontal van-space--align-center"
|
||||
style="margin-top: 10px;"
|
||||
>
|
||||
<div class="van-space-item"
|
||||
style="margin-right: 8px;"
|
||||
>
|
||||
<button type="button"
|
||||
class="van-button van-button--primary van-button--small van-button--disabled"
|
||||
disabled
|
||||
>
|
||||
<div class="van-button__content">
|
||||
<span class="van-button__text">
|
||||
barrage
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="van-space-item">
|
||||
<button type="button"
|
||||
class="van-button van-button--default van-button--small"
|
||||
>
|
||||
<div class="van-button__content">
|
||||
<span class="van-button__text">
|
||||
play
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
139
packages/vant/src/barrage/test/__snapshots__/index.spec.tsx.snap
Normal file
139
packages/vant/src/barrage/test/__snapshots__/index.spec.tsx.snap
Normal file
@ -0,0 +1,139 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`should auto play when only list props 1`] = `
|
||||
<div class="van-barrage"
|
||||
style="--move-distance: -100px;"
|
||||
>
|
||||
<div class="video"
|
||||
style="width: 100%; height: 150px;"
|
||||
>
|
||||
</div>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 0ms; animation-name: van-barrage; animation-timing-function: linear; top: 10px;"
|
||||
data-id="100"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 300ms; animation-name: van-barrage; animation-timing-function: linear; top: 110px;"
|
||||
data-id="101"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 600ms; animation-name: van-barrage; animation-timing-function: linear; top: 210px;"
|
||||
data-id="102"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 900ms; animation-name: van-barrage; animation-timing-function: linear; top: 310px;"
|
||||
data-id="103"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1200ms; animation-name: van-barrage; animation-timing-function: linear; top: 10px;"
|
||||
data-id="104"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1500ms; animation-name: van-barrage; animation-timing-function: linear; top: 110px;"
|
||||
data-id="105"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1800ms; animation-name: van-barrage; animation-timing-function: linear; top: 210px;"
|
||||
data-id="106"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`should emit "update:modelValue" when animationend 1`] = `
|
||||
<div class="van-barrage"
|
||||
style="--move-distance: -100px;"
|
||||
>
|
||||
<div class="video"
|
||||
style="width: 100%; height: 150px;"
|
||||
>
|
||||
</div>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 0ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 10px;"
|
||||
data-id="100"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 300ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 110px;"
|
||||
data-id="101"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 600ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 210px;"
|
||||
data-id="102"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 900ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 310px;"
|
||||
data-id="103"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1200ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 10px;"
|
||||
data-id="104"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1500ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 110px;"
|
||||
data-id="105"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1800ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 210px;"
|
||||
data-id="106"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`should not auto play use play function when use play function 1`] = `
|
||||
<div class="van-barrage"
|
||||
style="--move-distance: -100px;"
|
||||
>
|
||||
<div class="video"
|
||||
style="width: 100%; height: 150px;"
|
||||
>
|
||||
</div>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 0ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 10px;"
|
||||
data-id="100"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 300ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 110px;"
|
||||
data-id="101"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 600ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 210px;"
|
||||
data-id="102"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 900ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 310px;"
|
||||
data-id="103"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1200ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 10px;"
|
||||
data-id="104"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1500ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 110px;"
|
||||
data-id="105"
|
||||
>
|
||||
</span>
|
||||
<span class="van-barrage__item"
|
||||
style="animation-duration: 4000ms; animation-delay: 1800ms; animation-name: van-barrage; animation-timing-function: linear; animation-play-state: paused; top: 210px;"
|
||||
data-id="106"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
4
packages/vant/src/barrage/test/demo.spec.ts
Normal file
4
packages/vant/src/barrage/test/demo.spec.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import Demo from '../demo/index.vue';
|
||||
import { snapshotDemo } from '../../../test/demo';
|
||||
|
||||
snapshotDemo(Demo);
|
145
packages/vant/src/barrage/test/index.spec.tsx
Normal file
145
packages/vant/src/barrage/test/index.spec.tsx
Normal file
@ -0,0 +1,145 @@
|
||||
import { ref } from 'vue';
|
||||
import { Barrage, BarrageInstance } from '..';
|
||||
import { mount, trigger } from '../../../test';
|
||||
import { flushPromises } from '@vue/test-utils';
|
||||
|
||||
test('should auto play when only list props', async () => {
|
||||
const wrapper = mount({
|
||||
render() {
|
||||
return (
|
||||
<Barrage
|
||||
modelValue={[
|
||||
{ id: 100, text: '轻量' },
|
||||
{ id: 101, text: '可定制的' },
|
||||
{ id: 102, text: '移动端' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: '组件库' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
]}
|
||||
>
|
||||
<div class="video" style="width: 100%; height: 150px"></div>
|
||||
</Barrage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
expect(wrapper.findAll('.van-barrage__item')).toHaveLength(7);
|
||||
});
|
||||
|
||||
test('should not auto play use play function when use play function', async () => {
|
||||
const barrage = ref<BarrageInstance>();
|
||||
const wrapper = mount({
|
||||
render() {
|
||||
return (
|
||||
<Barrage
|
||||
ref={barrage}
|
||||
autoPlay={false}
|
||||
modelValue={[
|
||||
{ id: 100, text: '轻量' },
|
||||
{ id: 101, text: '可定制的' },
|
||||
{ id: 102, text: '移动端' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: '组件库' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
]}
|
||||
>
|
||||
<div class="video" style="width: 100%; height: 150px"></div>
|
||||
</Barrage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
expect(wrapper.findAll('.van-barrage__item')).toHaveLength(7);
|
||||
|
||||
expect(
|
||||
(wrapper.find('.van-barrage__item') as HTMLSpanElement).style
|
||||
.animationPlayState
|
||||
).toBe('paused');
|
||||
|
||||
barrage.value?.play();
|
||||
|
||||
expect(
|
||||
(wrapper.find('.van-barrage__item') as HTMLSpanElement).style
|
||||
.animationPlayState
|
||||
).toBe('running');
|
||||
|
||||
barrage.value?.pause();
|
||||
|
||||
expect(
|
||||
(wrapper.find('.van-barrage__item') as HTMLSpanElement).style
|
||||
.animationPlayState
|
||||
).toBe('paused');
|
||||
});
|
||||
|
||||
test('should emit "update:modelValue" when animationend', async () => {
|
||||
const barrage = ref<BarrageInstance>();
|
||||
const wrapper = mount({
|
||||
render() {
|
||||
return (
|
||||
<Barrage
|
||||
ref={barrage}
|
||||
autoPlay={false}
|
||||
modelValue={[
|
||||
{ id: 100, text: '轻量' },
|
||||
{ id: 101, text: '可定制的' },
|
||||
{ id: 102, text: '移动端' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: '组件库' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
]}
|
||||
onUpdate:modelValue={(value) => this.$emit('change', value)}
|
||||
>
|
||||
<div class="video" style="width: 100%; height: 150px"></div>
|
||||
</Barrage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
expect(wrapper.findAll('.van-barrage__item')).toHaveLength(7);
|
||||
|
||||
barrage.value?.play();
|
||||
|
||||
expect(
|
||||
(wrapper.find('.van-barrage__item') as HTMLSpanElement).style
|
||||
.animationPlayState
|
||||
).toBe('running');
|
||||
|
||||
await wrapper.setProps({
|
||||
modelValue: [
|
||||
{ id: 100, text: '轻量' },
|
||||
{ id: 101, text: '可定制的' },
|
||||
{ id: 102, text: '移动端' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: '组件库' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
{ id: 107, text: 'Barrage' },
|
||||
],
|
||||
});
|
||||
|
||||
expect(wrapper.findAll('.van-barrage__item')).toHaveLength(8);
|
||||
|
||||
await trigger(
|
||||
wrapper.find('.van-barrage__item') as HTMLSpanElement,
|
||||
'animationend'
|
||||
);
|
||||
|
||||
expect(wrapper.emitted('change')?.[0][0]).toEqual([
|
||||
{ id: 101, text: '可定制的' },
|
||||
{ id: 102, text: '移动端' },
|
||||
{ id: 103, text: 'Vue' },
|
||||
{ id: 104, text: '组件库' },
|
||||
{ id: 105, text: 'VantUI' },
|
||||
{ id: 106, text: '666' },
|
||||
{ id: 107, text: 'Barrage' },
|
||||
]);
|
||||
});
|
12
packages/vant/src/barrage/types.ts
Normal file
12
packages/vant/src/barrage/types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { ComponentPublicInstance } from 'vue';
|
||||
import { BarrageProps } from './Barrage';
|
||||
|
||||
export type BarrageExpose = {
|
||||
play(): void;
|
||||
pause(): void;
|
||||
};
|
||||
|
||||
export type BarrageInstance = ComponentPublicInstance<
|
||||
BarrageProps,
|
||||
BarrageExpose
|
||||
>;
|
@ -239,6 +239,10 @@ location.href = location.href.replace('youzan.github.io', 'vant-ui.github.io');
|
||||
path: 'action-sheet',
|
||||
title: 'ActionSheet 动作面板',
|
||||
},
|
||||
{
|
||||
path: 'barrage',
|
||||
title: 'Barrage 弹幕',
|
||||
},
|
||||
{
|
||||
path: 'dialog',
|
||||
title: 'Dialog 弹出框',
|
||||
@ -687,6 +691,10 @@ location.href = location.href.replace('youzan.github.io', 'vant-ui.github.io');
|
||||
path: 'action-sheet',
|
||||
title: 'ActionSheet',
|
||||
},
|
||||
{
|
||||
path: 'barrage',
|
||||
title: 'Barrage',
|
||||
},
|
||||
{
|
||||
path: 'dialog',
|
||||
title: 'Dialog',
|
||||
|
Loading…
x
Reference in New Issue
Block a user