文章最后更新时间:2024年05月30日
NPlayer简介
NPlayer 是由 Typescript 加 Sass 编写,无任何第三方运行时依赖,兼容 IE11,支持移动端、支持 SSR、支持直播。高度可定制,所有图标、主题色等都可以替换,并且提供了内置组件方便二次开发。你可以自定义任意多个断点,不仅仅是兼容移动端,只要愿意,你可以非常轻松的兼容手机竖屏、手机横屏、平板等设备。它还拥有插件系统,弹幕功能就是使用插件形式提供,使用时按需引入即可。该播放器还可以接入任何流媒体,如 HLS、dash 和 flv 等。
优势:
轻量化
完全开源
兼容性超清
支持滑屏操作,包括触碰和滑动快进退等
NPlayer安装
<script src="/js/NPlayer.min.js"></script> <div id="player"></div>
NPlayer初始化
const player = new NPlayer.Player({ src: 'https://www.18ma.cn/static/demo.M3U8', poster: 'https://www.18ma.cn/static/demo.jpg', ... }); player.mount('#player');//绑定ID
NPlayer参数
播放器构造函数参数。
import Player from 'nplayer' const player = new Player({ // 这里 }) console.log(player.opts)
参数 | 描述 |
---|---|
container | 播放器挂载容器元素,同 mount 方法参数,如果 mount 没有传入参数时,将使用该参数,当该参数为字符串时,将会自动查找对应元素 |
src | 视频地址,同 video 元素的 src 属性 |
video | 自己提供 video 元素 |
videoProps | video 元素的属性 |
videoSources | video source 子元素数组,请查看 快速开始 |
live | 是否是直播模式 |
autoSeekTime | 视频加载成功时自动跳转到的时间点(跳转后该参数会自动设为 0),你可以用这个参数实现记忆上次用户观看时间 |
thumbnail | 请查看 预览缩略图 |
controls | 请查看 控制条 |
bpControls | 设置不同断点下的控制条项布局,请查看 控制条 |
settings | 请查看 设置菜单 |
contextMenus | 请查看 右键菜单 |
contextMenuToggle | 是否偶数次单击右键时显示浏览器默认右键菜单 |
plugins | 插件列表,详情请查看 插件 |
i18n | 当前播放器语言,如 en 、zh |
shortcut | 是否开启快捷键功能 |
seekStep | 单次快进、快退的步长,快捷键中会使用到 |
volumeStep | 单次增加、降低音量的步长,快捷键中会使用到 |
themeColor | 主题色,请查看 定制主题 |
posterBgColor | 海报背景色,请查看 定制主题 |
progressBg | 进度条背景,请查看 定制主题 |
progressDot | 进度条上的点,请查看 定制主题 |
volumeProgressBg | 音量条背景,请查看 定制主题 |
volumeBarLength | 音量条长度,请查看 定制主题 |
volumeVertical | 垂直音量条,请查看 定制主题 |
loadingEl | 自定义视频 loading 元素,请查看 定制主题 |
openEdgeInIE | 是否在 Win10 的 IE 中自动打开 Edge,请查看 IE 11 兼容 |
poster | 海报图片地址,请查看 海报 |
posterEnable | 是否启用海报功能 |
posterPlayEl | 自定义海报播放按钮,请查看 定制主题 |
默认参数#
{ shortcut: true, seekStep: 10, volumeStep: 0.1, volumeBarLength: 100, settings: ['speed'], contextMenus: ['loop', 'pip', 'version'], contextMenuToggle: true, openEdgeInIE: true, posterEnable: true, videoProps: { preload: 'auto', playsinline: 'true', }, controls: [ ['play', 'volume', 'time', 'spacer', 'airplay', 'settings', 'web-fullscreen', 'fullscreen'], ['progress'], ], bpControls: { 650: [ ['play', 'progress', 'time', 'web-fullscreen', 'fullscreen'], [], ['spacer', 'airplay', 'settings'], ], } }
NPlayer多层级
NPlayer 由 6 个不同功能的层级组成,每个层级有自己的 z-index。
层级 | z-indx | 描述 |
---|---|---|
video 视频元素 | - | 视频元素没有设置 z-index |
control 控制条 | 10 | 视频底部控制条 |
poster 海报 | 20 | 视频海报 |
loading 加载中 | 30 | 视频加载时出现的加载中元素 |
contextmenu 右键菜单 | 40 | NPlayer 右键菜单 |
toast 提示框 | 50 | 提示框 |
z-index 高的组件会覆盖低的组件。当要实现自己组件时可以参考上表中的 z-index,将它放入合适层级。如,弹幕插件默认层级 z-index 是 5,那它将出现在 control 下方,video 元素上方。
流媒体支持
NPlayer 可以非常方便的接入流媒体协议,如果想使用 HLS 可以引入 hls.js。
import Hls from 'hls' import Player from 'player' const hls = new Hls() const player = new Player() hls.attachMedia(player.video) hls.on(Hls.Events.MEDIA_ATTACHED, function () { hls.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.M3U8') }) player.mount(document.body)
更多关于 hls.js 使用方法,请查看 hls.js 官方文档 。
hls 是视频点播很常用的协议,本教程还提供了使用 hls.js 实现清晰度切换功能,详情请查看 清晰度切换 。
DASH
和 HLS 非常相似的是 DASH 协议,要使用 DASH 协议可以使用 dash.js 。
import dash from 'dashjs' import Player from 'player' const player = new Player() dash .MediaPlayer() .create() .initialize( player.video, 'http://127.0.0.1:8001/out.mpd', true // 自动播放 )
更多关于 dash.js 请查看 dash.js 官方文档 。
任何流媒体
按照这个套路,其实任何这些流媒体都可以接入(flv, WebTorrent 等等)。因为它们只需要一个 video 元素就行,我们可以通过 player.video 属性访问到 video 元素。
除了让 NPlayer 自动创建 video 元素,还可以自己提供 video 元素。
import Hls from 'hls' import Player from 'player' const video = document.createElement('video') const hls = new Hls() const player = new Player({ video }) hls.attachMedia(player.video) hls.on(Hls.Events.MEDIA_ATTACHED, function () { hls.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8') }) player.mount(document.body)
弹幕插件
该插件可以给 NPlayer 添加弹幕功能。它可以保持大量弹幕而不卡顿,并且支持非常多的设置,比如弹幕防碰撞、弹幕速度、字体、速度、透明度、显示区域等。
引入弹幕插件
<script src="/js/danmaku.min.js"></script>
快速上手#
import Player from 'nplayer' import Danmaku from '@nplayer/danmaku' const danmakuOptions = { items: [ { time: 1, text: '弹幕~' } ] } const player = new Player({ plugins: [new Danmaku(danmakuOptions)] }) player.mount(document.body)
控制条#
弹幕插件会注册 danmaku-send 和 danmaku-settings 这两项。
通过 autoInsert 参数可以控制是否自动插入,自动插入逻辑是,找到 spacer 项,将它替换成 danmaku-send 和 danmaku-settings,如果找不到 spacer 则不会自动插入。
new Player({ controls: [['play', 'spacer', 'danmaku-settings'], ['progress']], plugins: [ new Danmaku({ autoInsert: false, }) ] })
你可以将 autoInsert 设置为 false,然后手动设置控制条项,如上代码,手动加入了弹幕设置项,移除了弹幕发送项。
弹幕对象#
弹幕插件还会在 player 对象上注册一个 danmaku 对象。你可以通过 player.danmaku 访问该对象。
console.log(player.danmaku)
弹幕列表#
弹幕列表可以通过 items 参数传入,一个弹幕对象签名如下。
interface BulletOption { color?: string; // 弹幕颜色 text: string; // 弹幕文字 time: number; // 弹幕出现时间 type?: 'top' | 'bottom' | 'scroll'; // 弹幕类型,默认为滚动类型 isMe?: boolean; // 是否是当前用户发送的 force?: boolean; // 是否强制展示该弹幕(弹幕较多,并且是防碰撞模式时,可能会丢弃一部分弹幕) }
弹幕列表必须按照 time 从小到大排序。如果获取的弹幕是无序的,那么在传入之前需要自己 .sort((a, b) => a.time - b.time) 一下。你还可以通过 danmaku 对象的 appendItems 和 resetItems 等方法,添加和重置弹幕。
清晰度切换
这个例子是使用 ControlItem 来实现视频的清晰度切换功能,这里使用 HLS,来切换视频清晰度。
import Player, { Popover } from "nplayer" import Hls from "hls.js" // 1. 首先创建一个控制条项 const Quantity = { el: document.createElement("div"), init() { this.btn = document.createElement("div") this.btn.textContent = "画质"; this.el.appendChild(this.btn) this.popover = new Popover(this.el) this.btn.addEventListener("click", () => this.popover.show()) // 点击按钮的时候展示 popover this.el.style.display = "none" // 默认隐藏 this.el.classList.add("quantity") } } // 2. 我们把它放到 spacer 后面 window.player = new Player({ controls: [ [ "play", "volume", "time", "spacer", Quantity, "airplay", "settings", "web-fullscreen", "fullscreen" ], ["progress"] ] }) // 3. 创建 HLS 实例 const hls = new Hls() hls.on(Hls.Events.MEDIA_ATTACHED, function () { hls.on(Hls.Events.MANIFEST_PARSED, function () { // 4. 给清晰度排序,清晰度越高的排在最前面 hls.levels.sort((a, b) => b.height - a.height) const frag = document.createDocumentFragment() // 5. 给与清晰度对应的元素添加,点击切换清晰度功能 const listener = (i) => (init) => { const last = Quantity.itemElements[Quantity.itemElements.length - 1] const prev = Quantity.itemElements[Quantity.value] || last const el = Quantity.itemElements[i] || last prev.classList.remove("quantity_item-active") el.classList.add("quantity_item-active") Quantity.btn.textContent = el.textContent if (init !== true && !window.player.paused) setTimeout(() => window.player.play()) // 因为 HLS 切换清晰度会使正在播放的视频暂停,我们这里让它再自动恢复播放 Quantity.value = hls.currentLevel = hls.loadLevel = i Quantity.popover.hide() } // 6. 添加清晰度对应元素 Quantity.itemElements = hls.levels.map((l, i) => { const el = document.createElement("div") el.textContent = l.name + "P" if (l.height === 1080) el.textContent += " 超清" if (l.height === 720) el.textContent += " 高清" if (l.height === 480) el.textContent += " 清晰" el.classList.add("quantity_item") el.addEventListener("click", listener(i)) frag.appendChild(el) return el }) const el = document.createElement("div") el.textContent = "自动" el.addEventListener("click", listener(-1)) el.classList.add("quantity_item") frag.appendChild(el) Quantity.itemElements.push(el) // 这里再添加一个 `自动` 选项,HLS 默认是根据网速自动切换清晰度 Quantity.popover.panelEl.appendChild(frag) Quantity.el.style.display = "block" listener(hls.currentLevel)(true) // 初始化当前清晰度 }); // 绑定 video 元素成功的时候,去加载视频 hls.loadSource("https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"); }) hls.attachMedia(window.player.video) window.player.mount("#app")
.quantity { position: relative; padding: 0 8px; cursor: pointer; font-size: 14px; font-weight: bold; white-space: nowrap; opacity: 0.8; } .quantity:hover { opacity: 1; } .quantity_item { padding: 5px 20px; font-weight: normal; } .quantity_item:hover { background: rgba(255, 255, 255, 0.3); } .quantity_item-active { color: var(--theme-color); }
NPlayer监听
有下面 5 个事件相关的方法。
方法 | 描述 |
---|---|
on(evt: string, fn: Function) | 监听事件 |
once(event: string, fn: Function) | 监听事件,但是只调用一次回调函数 |
emit(evt: string, ...args: any[]) | 触发事件 |
off(evt: string, fn?: Function) | 解除事件监听 |
removeAllListeners(evt?: string) | 移除所有事件监听 |
你可以使用这些方法监听内置事件或触发自定义事件。
NPlayer事件
NPlayer 事件名是大驼峰形式的字符串。
事件名 | 描述 |
---|---|
EnterFullscreen | 进入全屏 |
ExitFullscreen | 退出全屏 |
WebEnterFullscreen | 进入网页全屏 |
WebExitFullscreen | 退出网页全屏 |
LoadingShow | 视频 loading 显示 |
LoadingHide | 视频 loading 隐藏 |
ControlShow | 控制条显示 |
ControlHide | 控制条隐藏 |
ControlItemUpdate | 更新控制条项目位置时触发,调用 player.updateControlItems() |
EnterPip | 进入画中画 |
ExitPip | 退出画中画 |
UpdateSize | 更新播放器尺寸,比如 window resize 会触发,当外部将播放器元素大小变化时,可以手动触发该事件,防止播放器组件错位 |
UpdateOptions | 更新配置 |
AfterInit | 播放器初始化完成时触发 |
Mounted | 播放器挂载 |
BeforeDispose | 播放器销毁前,当调用 dispose 方法触发 |
OpenEdge | 在 IE 中自动打开 edge 浏览器,访问该网页时触发 |
Play | 同 video play 事件 |
Pause | 同 video pause 事件 |
Ended | 同 video ended 事件 |
Waiting | 同 video waiting 事件 |
Stalled | 同 video stalled 事件 |
Canplay | 同 video canplay 事件 |
LoadedMetadata | 同 video loadedmetadata 事件 |
Error | 同 video error 事件 |
Seeked | 同 video seeked 事件 |
TimeUpdate | 同 video timeupdate 事件 |
VolumeChange | 同 video volumechange 事件 |
RateChange | 同 video ratechange 事件 |
DurationChange | 同 video durationchange 事件 |
Progress | 同 video progress 事件 |
BpChange | 当播放器大小变换到特定断点时触发,bpControls 参数中设置的断点 |
发表评论