提交 378732e1 编写于 作者: L liuyijun

feat: 新增barrage弹幕组件

上级 b6ff663b
......@@ -232,15 +232,15 @@
"show": true,
"author": "swag~jun"
},
{
"version": "1.0.0",
"name": "CircleProgress",
"type": "component",
"cName": "进度条",
"desc": "展示操作或任务的当前进度。",
"sort": 7,
"show": true,
"author": "swag~jun"
{
"version": "1.0.0",
"name": "CircleProgress",
"type": "component",
"cName": "进度条",
"desc": "展示操作或任务的当前进度。",
"sort": 7,
"show": true,
"author": "swag~jun"
}
]
},
......@@ -356,6 +356,16 @@
"sort": 1,
"show": true,
"author": "songsong"
},
{
"version": "1.0.0",
"name": "Barrage",
"type": "component",
"cName": "弹幕",
"desc": "用于话语和词组的轮播展示,适用于视频中或其他类似需求中。",
"sort": 2,
"show": true,
"author": "swag~jun"
}
]
}
......
.nut-barrage {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
box-sizing: border-box;
background-color: #f7f8fa;
.dmitem {
width: 100px;
// max-width: 150px;
display: block;
position: absolute;
right: 0;
padding: 3px 25px;
border-radius: 50px;
font-size: 12px;
text-align: center;
white-space: pre;
transform: translateX(100%);
background: linear-gradient(to right, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0));
&.move {
will-change: transform;
animation-name: moving;
animation-timing-function: linear;
animation-play-state: running;
}
}
@keyframes moving {
from {
transform: translateX(100%);
}
to {
transform: translateX(-250%);
// transform: translateX(-180%);
// transform: translateX(var(--move-distance));
}
}
@-webkit-keyframes moving {
from {
-webkit-transform: translateX(100%);
}
to {
transform: translateX(-250%);
}
}
}
import React, { ForwardRefRenderFunction, useEffect, useRef, useImperativeHandle } from 'react'
import classNames from 'classnames'
import bem from '@/utils/bem'
import './barrage.scss'
export interface BarrageProps {
className: string
style: React.CSSProperties
danmu: Array<string>
frequency: number
loop: boolean
speeds: number
rows: number
top: number
}
const defaultProps = {
danmu: [''],
frequency: 500,
loop: true,
speeds: 2000,
rows: 3,
top: 10,
}
const InternalBarrage: ForwardRefRenderFunction<unknown, Partial<BarrageProps>> = (props, ref) => {
const { className, frequency, loop, danmu, speeds, rows, top, ...restProps } = {
...defaultProps,
...props,
}
const dmBody = useRef<HTMLDivElement>(null)
const dmContainer = useRef<HTMLDivElement>(null)
const danmuCWidth = useRef(0)
const timer = useRef(0)
const index = useRef(0)
const b = bem('barrage')
const classes = classNames(className, b(''))
useImperativeHandle(ref, () => ({
add: (word: string) => {
const _index = index.current % danmu.length
danmu.splice(_index, 0, word)
},
}))
useEffect(() => {
if (dmBody.current) {
danmuCWidth.current = dmBody.current.offsetWidth
run()
}
return () => {
clearInterval(timer.current)
}
}, [])
const run = () => {
clearInterval(timer.current)
timer.current = setInterval(() => {
play()
run()
}, frequency)
}
const play = () => {
const _index = loop ? index.current % danmu.length : index.current
let el = document.createElement(`div`)
el.innerHTML = danmu[_index] as string
el.classList.add('dmitem')
;(dmContainer.current as HTMLDivElement).appendChild(el)
const width = el.offsetWidth
const height = el.offsetHeight
el.classList.add('move')
el.style.animationDuration = `${speeds}ms`
el.style.top = (_index % rows) * (height + top) + 20 + 'px'
el.style.width = width + 20 + 'px'
el.style.setProperty('--move-distance', `-${danmuCWidth.current}px`)
el.dataset.index = `${_index}`
el.addEventListener('animationend', () => {
;(dmContainer.current as HTMLDivElement).removeChild(el)
})
index.current++
}
return (
<div className={classes} ref={dmBody} {...restProps}>
<div ref={dmContainer} className="dmContainer"></div>
</div>
)
}
export const Barrage = React.forwardRef<unknown, Partial<BarrageProps>>(InternalBarrage)
Barrage.defaultProps = defaultProps
Barrage.displayName = 'NutBarrage'
.nut-cell,
.nut-barrage {
padding: 20px 0;
height: 150px;
}
import React, { useRef } from 'react'
import Cell from '@/packages/cell'
import { Barrage } from './barrage'
import Button from '@/packages/button'
import './demo.scss'
interface danmuRefState {
add: (word: string) => void
}
const BarrageDemo = () => {
let list = ['画美不看', '不明觉厉', '喜大普奔', '男默女泪', '累觉不爱', '爷青结-']
const danmuRef = useRef<danmuRefState>(null)
const addDanmu = () => {
let n = Math.random()
if (danmuRef.current) {
danmuRef.current.add('随机——' + String(n).substr(2, 10))
}
}
return (
<>
<div className="demo">
<h2>基础用法</h2>
<Cell>
<Barrage ref={danmuRef} danmu={list}></Barrage>
</Cell>
<div className="test" style={{ textAlign: 'center' }}>
<Button type="danger" onClick={addDanmu}>
随机添加
</Button>
</div>
</div>
</>
)
}
export default BarrageDemo
# Barrage 弹幕
### 介绍
用于话语和词组的轮播展示,适用于视频中或其他类似需求中。
### 安装
``` javascript
import { Barrage } from '@nutui/nutui-react';
```
## 代码演示
### 基础用法1
```tsx
<h2>基础用法</h2>
<Cell>
<Barrage ref={danmuRef} danmu={list}></Barrage>
</Cell>
<div className="test" style={{ textAlign: 'center' }}>
<Button type="danger" onClick={addDanmu}>随机添加</Button>
</div>
```
```tsx
let list = ['画美不看', '不明觉厉', '喜大普奔', '男默女泪', '累觉不爱', '爷青结-']
const danmuRef = useRef<danmuRefState>(null)
const addDanmu = () => {
let n = Math.random()
if (danmuRef.current) {
danmuRef.current.add('随机——' + String(n).substr(2, 10))
}
}
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| danmu | 弹幕列表数据 | Array | [] |
| frequency | 可视区域内每个弹幕出现的时间间隔 | Number | 500 |
| speeds | 每个弹幕的滚动时间 | Number | 2000 |
| rows | 弹幕行数,分几行展示 | Number | 1 |
| top | 弹幕垂直距离 | Number | 10 |
| loop | 是否循环播放 | Boolean | true |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| add | 添加数据 | - |
\ No newline at end of file
import { Barrage } from './barrage'
export default Barrage
......@@ -23,6 +23,7 @@ import Input from './input'
import TextArea from './textarea'
import CheckBox from './checkbox'
import Signature from './signature'
import Barrage from './barrage'
export {
Avatar,
......@@ -50,4 +51,5 @@ export {
TextArea,
CheckBox,
Signature,
Barrage,
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册