# PPBar
[](https://github.com/web-streaming/ppbar)
[](https://github.com/web-streaming/ppbar)
功能强大的播放器进度条,支持章节、标记、热力图、缩略图、直播、旋转等功能。还支持自定义 UI,可以将它打造成 Youtube 或 B站的播放器进度条的外观。
[](https://web-streaming.github.io/ppbar/)
[English](./README.md)
## 安装
```
npm i ppbar
```
或者通过 CDN 使用
```html
```
## 使用
使用 ppbar,需要导入 `ProgressBar` 类和 ppbar 的样式。
```js
import ProgressBar from 'ppbar';
import 'ppbar/dist/index.min.css';
const div = document.createElement('div');
const bar = new ProgressBar(div, {
// 参数
})
document.body.appendChild(div)
```
`ProgressBar` 构造函数接收两个参数,第一个它的容器,第二个是 ppbar 的参数。如果设置第一个参数会自动将 ppbar 的 DOM 元素添加到传入容器中。
当然希望手动添加 ppbar 的 DOM 元素,可以不传入第一个参数。
```ts
import ProgressBar from 'ppbar';
import 'ppbar/dist/index.min.css';
const bar = new ProgressBar(undefined, {
// 参数
})
document.body.appendChild(bar.el)
```
ppbar 有一个 `updateConfig` 方法,可以随时通过它来动态更新参数。
```ts
import ProgressBar from 'ppbar';
import 'ppbar/dist/index.min.css';
const bar = new ProgressBar(undefined, {
// 参数
})
document.body.appendChild(bar.el)
bar.updateConfig({ /* 新参数 */ })
// 如果不需要 bar 对象了请销毁它
bar.destroy()
```
### 章节
章节可以设置进度条分段效果,每个章节必须有一个标题,鼠标 hover 进度条时会被展示出来。
章节应该有个 `time` 属性,表示该章节的结尾,最后一个章节可以省略 `time` 属性。
```js
import ProgressBar from 'ppbar';
import 'ppbar/dist/index.min.css';
new ProgressBar(document.body, {
chapters: [
{ time: 10, title: 'chapter1' },
{ time: 20, title: 'chapter2' },
{ title: 'chapter3' },
]
})
```
上面一共设置 3 个章节,分别是:
1. `0s - 10s` chapter1
2. `10s - 20s` chapter2
3. `20s - duration` chapter3
### 标记
该功能允许你在进度条上设置标记点,每个标记点可以放入自定义的 DOM 或一张图片。
```ts
import ProgressBar from 'ppbar';
import 'ppbar/dist/index.min.css';
new ProgressBar(document.body, {
markers: [
{ time: 10, title: 'marker1', image: './img.jpg', size: [30, 30] },
]
})
```
上面代码在进度条 10 秒位置,设置了一个标记点,标记点中有一个图片,它的大小是 `30x30`。
除了图片还可以使用自定义的 dom 元素。
```ts
import ProgressBar from 'ppbar';
import 'ppbar/dist/index.min.css';
const div = document.createElement('div')
new ProgressBar(document.body, {
markers: [
{ time: 10, title: 'marker1', el: div },
]
})
```
当标记点被点击时会触发 `markerClick` 事件。
```ts
import { EVENT, ProgressBar } from 'ppbar'
const bar = new ProgressBar(document.body, {
markers: [
{ time: 10, title: 'marker1' },
]
})
bar.on(EVENT.MARKER_CLICK, (marker) => console.log(marker))
```
### 热力图
热力图用于标记整个视频不同时间段观看量的变化。比如 0 到 5 秒观看量是 100,5 到 10 秒观看量是 200 ... 然后将这些观看量绘制成曲线就是 ppbar 的热力图了。
```ts
const bar = new ProgressBar(document.body, {
heatMap: {
points: [1, 2, 3],
defaultDuration: 5,
}
})
```
上面代码配置表示,每个点表示的时长为 `5` 秒,也就是 `0 - 5` 的值是 `1`,`5 - 10` 为 `2`,`10 - 15` 为 `3`。
另外还支持给每个点单独设置时长。
```ts
const bar = new ProgressBar(document.body, {
heatMap: {
points: [{ duration: 10, score: 1 }, { score: 2 }, { duration: 2, score: 3 }],
defaultDuration: 5,
}
})
```
上面配置中,第一个和第三个有自己的时长,第二个则是使用 `defaultDuration`。
如果希望热力图和 Youtube 一样,只有 hover 的时候才显示,可以设置 `hoverShow`。
```ts
const bar = new ProgressBar(document.body, {
heatMap: {
points: [1, 2, 3],
defaultDuration: 5,
hoverShow: true
}
})
```
### 缩略图
缩略图用于预览视频的不同时间点的画面。一张缩略图由多张小缩略图拼接而成。
```ts
const bar = new ProgressBar(document.body, {
thumbnail: {
start: 0, // 缩略图开始时间,单位秒
gap: 10, // 单个小缩略图表示的时长
row: 5, // 大缩略图是又几行小缩略图组成
col: 5, // 大缩略图是又几列小缩略图组成
width: 160, // 单个小缩略图宽度
height: 90, // 单个小缩略图的高度
images: [], // 缩略图地址数组
}
})
```
以上参数的值都是默认参数。
另外如果你不希望展示缩略图可以将它设置为 `false`。
```ts
const bar = new ProgressBar(document.body, {
thumbnail: false
})
```
### 直播
ppbar 还支持直播时移,在直播模式下进度条的时间展示将会是负数。
```ts
new ProgressBar(document.body, { live: true })
```
另外还可以使用 `updateConfig` 方法动态开启关闭直播模式。
```ts
const bar = new Progress(document.body)
bar.updateConfig({ live: true })
```
### 旋转
ppbar 还支持被旋转,例如在移动端不使用全屏,但是希望视频横着播放,这是就会对播放器设置 `transform: rotate(90deg)`。这时候就需要手动更新 ppbar 的旋转来防止 ppbar 交互失效。
```ts
const div = document.createElement('div')
div.style.transform = 'rotate(90deg)'
new ProgressBar(div, { rotate: 90 })
```
你还可以通过 `updateRotate` 方法来动态更新参数。
```ts
const bar = new ProgressBar(document.body)
bar.updateRotate(90)
```
### 事件
ppbar 还会抛出事件,你可以使用 `on` 方法监听事件,`once` 方法监听一次事件,`off` 取消监听事件。
```ts
import { EVENT, ProgressBar } from 'ppbar'
const bar = new ProgressBar(document.body)
bar.once(EVENT.DRAGGING, console.log)
bar.on(EVENT.DRAGEND, console.log)
bar.off(EVENT.DRAGEND, console.log)
```
具体的事件描述请查看 [API 事件章节](#api)
## 自定义样式
ppbar 支持自定义修改样式。
```scss
new ProgressBar(document.body, {
dot: ''
})
```
参数 `dot` 可以自定进度条原点,可以是自定义 DOM 元素、字符串,为字符串时将直接设置 `innerHTML`。
另外还可以通过 CSS 变量来设置 ppbar 的主题色。
```ts
const bar = new ProgressBar(document.body)
bar.el.style.setProperty('--primary-color', 'rgba(35,173,229, 1)');
```
`--primary-color` 是 ppbar 的主题色,默认为 `#f00`。
最后你还可以通过 Sass 变量来自定义样式。
```scss
@use '~ppbar/lib/index.scss' with (
$primaryColor: #0f0,
$markerDotBg: #f00,
$heatMapHeight: 30px
)
```
在 scss 文件中导入并覆盖样式。
```ts
import ProgressBar from 'ppbar'
import './index.scss'
```
导入上面自定义的样式。
目前一共支持 3 种自定义 sass 变量。
- `$primaryColor` 进度条的主题色,默认 `#f00`
- `$markerDotBg` 标记点的背景色,默认 `#fff`
- `$heatMapHeight` 热力图高度,默认 `40px`
## 集成到播放器
你可以使用 ppbar 打造自己的播放器,或者将它集成到现成的播放器中,下面以 [nplayer](https://github.com/woopen/nplayer) 为例。
```ts
import ProgressBar, { EVENT as BAR_EVENT } from 'ppbar';
import Player, { EVENT } from 'nplayer';
import 'ppbar/dist/index.min.css'
const div = document.createElement('div')
div.style.width = '100%'
const progress = new ProgressBar(div, {
chapters: [
{ time: 10, title: 'chapter-a' },
{ time: 28, title: 'chapter-b' },
{ time: 51, title: 'chapter-c' },
{ title: 'chapter-d' },
],
markers: [{
time: 15,
title: 'title1',
image: 'https://github.com/web-streaming/ppbar/blob/main/demo/m1.png?raw=true',
size: [32, 34],
}, {
time: 30,
title: 'title2',
image: 'https://github.com/web-streaming/ppbar/blob/main/demo/m2.png?raw=true',
size: [32, 34],
},
{
time: 55,
title: 'title3',
image: 'https://github.com/web-streaming/ppbar/blob/main/demo/m3.png?raw=true',
size: [32, 34],
}],
heatMap: {
points: [9592,9692,10063,41138,30485,23905,10966.5,10316.5,8533.5,7249,7181,6813,5929,18046.5,8817,3684.5],
defaultDuration: 3.75
},
thumbnail: {
images: ['https://github.com/woopen/nplayer/blob/main/website/static/img/M1.jpg?raw=true']
}
})
const MyProgress = {
el: div,
init(player) {
player.on(EVENT.DURATION_CHANGE, () => progress.updateDuration(player.duration))
player.on(EVENT.TIME_UPDATE, () => progress.updatePlayed(player.currentTime))
player.on(EVENT.PROGRESS, () => progress.updateBuffer(player.buffered.end(player.buffered.length - 1)))
// 监听播放器事件,关联到 ppbar
}
}
const player = new Player({
src: 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4',
controls: [
['play', 'volume', 'time', 'spacer', 'settings', 'web-fullscreen', 'fullscreen'],
[MyProgress]
]
})
progress.on(BAR_EVENT.DRAGEND, (time) => {
player.currentTime = time
})
progress.on(BAR_EVENT.MARKER_CLICK, (marker) => {
player.currentTime = marker.time
})
// 将 ppbar 事件关联到 player
player.mount(document.body)
```
效果如下。

## API
### 配置
| 参数名 | 类型 | 描述 |
| -- | -- | -- |
| `live` | `boolean` | 是否是直播模式 |
| `duration` | `number` | 进度条的时长 |
| `rotate` | `0 \| 90 \| -90` | 进度条要被旋转的度数 |
| `dot` | `HTMLElement \| string \| true` | 进度条标记点,`true` 表示使用默认 |
| `chapters` | `{time?:number,title:string}[]` | 章节,`time` 是一个章节的结束时间,最后一个章节可以不设置 |
| `heatMap` | `Object` | 热力图 |
| `heatMap.points` | `(number\|{duration?:number;score:number})[]` | 热力图分数点 |
| `heatMap.defaultDuration` | `Object` | 默认单点时长 |
| `heatMap.hoverShow` | `Object` | 是否要 hover 的时候才展示 |
| `markers` | `Object[]` | 标记数组 |
| `markers[].time` | `number` | 必填,标记对应的时间点 |
| `markers[].title` | `string` | 标记点标题 |
| `markers[].el` | `HTMLElement` | 标记点自定义 DOM 元素 |
| `markers[].image` | `string` | 标记点图片 |
| `markers[].size` | `number[]` | 标记点图片大小 |
| `thumbnail` | `Object \| false` | 缩略图,`false` 为不展示 |
| `thumbnail.start` | `number` | 缩略图开始时间,默认 `0` |
| `thumbnail.gap` | `number` | 每个缩略图的时长,默认 `10` |
| `thumbnail.row` | `number` | 雪碧图由几行图片组成,默认 `5` |
| `thumbnail.col` | `number` | 雪碧图由几列图片组成,默认 `5` |
| `thumbnail.width` | `number` | 缩略图宽度,默认 `160` |
| `thumbnail.height` | `number` | 缩略图高度,默认 `90` |
| `thumbnail.images` | `string[]` | 缩略图地址数组 |
### 属性
| 属性 | 类型 | 描述 |
| -- | -- | -- |
| `el` | `HTMLElement` | 进度条的 DOM 元素 |
| `config` | `ProgressConfig` | 进度条参数 |
| `rect` | `Rect` | 进度条盒子大小,类似 [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect) |
| `duration` | `number` | 时长,默认 `0` |
| `rotate` | `number` | 被旋转的角度,默认 `0` |
| `live` | `boolean` | 是否是直播模式 |
### 方法
#### updateSize()
更新进度条大小,一般在修改容器大小位置时调用,防止进度条中元素对不齐。
```ts
updateSize(): void;
```
#### updateRotate()
更新进度条旋转角度。
```ts
updateRotate(r: 0 | 90 | -90): void
```
#### updateDuration()
更新时长
```ts
updateDuration(duration?: number): void
```
#### updateConfig()
更新进度条参数,参数可以部分更新,一般在切换视频时使用。
```ts
updateConfig(config?: Partial): void
```
#### updateMarkerPosition()
更新标记点的位置,在直播中随着时间推移可以标记点位置会发生变化,可以使用该函数。参数是相对时间,表示所有标记点都移动多少时间距离。
```ts
updateMarkerPosition(relativeTime: number): void
```
#### updatePlayed()
更新播放进度。
```ts
updatePlayed(time: number): void
```
#### updateBuffer()
更新缓存进度。
```ts
updateBuffer(time: number): void
```
#### updateHover()
更新 hover 进度。
```ts
updateHover(time: number): void
```
#### destroy()
销毁进度条。
```ts
destroy(): void
```
### 事件
| 事件名 | 描述 |
| -- | -- |
| `markerClick` | 标记点被点击,参数是标记点对象 |
| `dragging` | 正在拖动进度条,频繁触发 |
| `dragend` | 拖动结束 |
| `mousemove` | 鼠标在进度条上移动,频繁触发 |
| `mouseleave` | 鼠标离开 |