NPlayer - 兼容性超强的轻量开源H5弹幕播放器

admin

文章最后更新时间:2024年05月30日

NPlayer.jpg

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 元素
videoPropsvideo 元素的属性
videoSourcesvideo source 子元素数组,请查看 快速开始
live是否是直播模式
autoSeekTime视频加载成功时自动跳转到的时间点(跳转后该参数会自动设为 0),你可以用这个参数实现记忆上次用户观看时间
thumbnail请查看 预览缩略图
controls请查看 控制条
bpControls设置不同断点下的控制条项布局,请查看 控制条
settings请查看 设置菜单
contextMenus请查看 右键菜单
contextMenuToggle是否偶数次单击右键时显示浏览器默认右键菜单
plugins插件列表,详情请查看 插件
i18n当前播放器语言,如 enzh
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 右键菜单40NPlayer 右键菜单
toast 提示框50提示框

z-index 高的组件会覆盖低的组件。当要实现自己组件时可以参考上表中的 z-index,将它放入合适层级。如,弹幕插件默认层级 z-index 是 5,那它将出现在 control 下方,video 元素上方。

流媒体支持

HLS

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 参数中设置的断点

NPlayer下载

此处为隐藏内容,请评论后查看隐藏内容,谢谢!

文章版权声明:除非注明,否则均为十八码原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
AddoilApplauseBadlaughBombCoffeeFabulousFacepalmFecesFrownHeyhaInsidiousKeepFightingNoProbPigHeadShockedSinistersmileSlapSocialSweatTolaughWatermelonWittyWowYeahYellowdog
评论列表 (有 13 条评论,1051人围观)
网友昵称:morans
morans V 评论者 沙发
05-25 回复
我来了
网友昵称:suyin
suyin V 评论者 椅子
06-16 回复
感谢你的分享
网友昵称:www5ixcc
www5ixcc V 评论者 板凳
06-18 回复
感谢你的分享
网友昵称:xiaoban3122
xiaoban3122 V 评论者 凉席
06-24 回复
感谢你的分享
网友昵称:彬彬有礼
彬彬有礼 V 评论者 地板
07-23 回复
感谢分享
网友昵称:insbot
insbot V 评论者 6楼
07-26 回复
感谢你的分享
网友昵称:dudulove61@gmail.com
dudulove61@gmail.com V 评论者 7楼
09-19 回复
感谢你的分享
网友昵称:490454744@qq.com
490454744@qq.com V 评论者 8楼
10-08 回复
感谢你的分享
网友昵称:58086955
58086955 V 评论者 9楼
10-28 回复
感谢你的分享
网友昵称:18myhq
18myhq V 评论者 10楼
11-08 回复
感谢
网友昵称:120589497@qq.com
120589497@qq.com V 评论者 11楼
11-17 回复
感谢分享
网友昵称:phonedai
phonedai V 评论者 12楼
11-17 回复
感谢你的分享

目录[+]