├── .gitignore
├── README.md
├── babel.config.js
├── docs
├── .vitepress
│ ├── config.js
│ └── theme
│ │ ├── index.js
│ │ ├── register-components.js
│ │ └── styles
│ │ └── index.css
├── components
│ └── index.vue
├── guide
│ ├── changelog.md
│ └── install.md
└── index.md
├── index.html
├── lib
├── components
│ ├── d-contextmenu.vue
│ ├── d-icon.vue
│ ├── d-loading.vue
│ ├── d-player-top.vue
│ ├── d-slider.vue
│ ├── d-status.vue
│ ├── d-switch.vue
│ └── index.js
├── index.js
├── style
│ ├── animate.less
│ ├── base.less
│ ├── iconfont.css
│ ├── iconfont.woff2
│ ├── reset.less
│ ├── transition.less
│ └── vPlayer.less
├── utils
│ ├── dom.ts
│ └── util.ts
└── video-play
│ ├── main.vue
│ └── plugins
│ └── index.ts
├── package.json
├── scripts
├── gh-pages.sh
├── publish copy.js
└── publish.js
├── src
├── App.vue
├── main.ts
├── shims-vue.d.ts
├── utils
│ └── index.js
└── vite-env.d.ts
├── tsconfig.json
├── vite.config.ts
├── yarn-error.log
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
8 |
9 | [](https://www.npmjs.com/package/vue3-video-play)
10 | [](https://www.npmjs.com/package/vue3-video-play)
11 | [](https://github.com/xdlumia/vue3-video-play/stargazers)
12 | [](https://github.com/xdlumia/vue3-video-play/issues)
13 | [](https://github.com/xdlumia/vue3-video-play/network)
14 | [](https://github.com/xdlumia/vue3-video-play)
15 | [](https://github.com/xdlumia/vue3-video-play)
16 |
17 | [](https://www.npmjs.com/package/vue3-video-play)
18 |
19 | **必须使用 vue@3.2.2及以上版本**
20 |
21 | ### Vue3-video-play
22 |
23 | 适用于 Vue3 的 hls.js 播放器组件 | 并且支持 MP4/WebM/Ogg 格式
24 | 配置强大,UI 还算好看
25 |
26 | ## 功能一览
27 |
28 | 1. 支持快捷键操作
29 | 2. 支持倍速播放设置
30 | 3. 支持镜像画面设置
31 | 4. 支持关灯模式设置
32 | 5. 支持画中画模式播放
33 | 6. 支持全屏/网页全屏播放
34 | 7. 支持从固定时间开始播放
35 | 8. 支持移动端,移动端会自动调用自带视频播放器
36 | 9. 支持 hls 视频流播放,支持直播
37 | 10. hls 播放支持清晰度切换
38 |
39 | # 主页示例
40 |
41 | [https://codelife.cc/vue3-video-play/](https://codelife.cc/vue3-video-play/)
42 |
43 | ## 近期更新 v1.3.3 🎉
44 |
45 | - 修复: 右键事件错误
46 |
47 | # 使用指南
48 |
49 | ## 安装
50 |
51 | npm 安装:
52 |
53 | ```bash
54 | npm i vue3-video-play --save
55 | ```
56 |
57 | yarn 安装:
58 |
59 | ```bash
60 | yarn add vue3-video-play --save
61 | ```
62 |
63 | ## 开始使用
64 |
65 | #### 全局使用
66 |
67 | ```js
68 | import { createApp } from "vue";
69 | import App from "./App.vue";
70 | let app = createApp(App);
71 |
72 | import vue3videoPlay from "vue3-video-play"; // 引入组件
73 | import "vue3-video-play/dist/style.css"; // 引入css
74 | app.use(vue3videoPlay);
75 |
76 | app.mount("#app");
77 | ```
78 |
79 | #### 组件内使用
80 |
81 | ```js
82 | // require style
83 | import "vue3-video-play/dist/style.css";
84 | import { videoPlay } from "vue3-video-play";
85 | export default {
86 | components: {
87 | videoPlay,
88 | },
89 | };
90 | ```
91 |
92 | ## 基本示例
93 |
94 | 提供了丰富了配置功能
95 | :::demo 自定义配置 比如自定义 poster。
96 |
97 | ```vue
98 |
99 |
100 |
104 |
105 |
106 |
107 |
136 |
137 |
138 | ```
139 |
140 | :::
141 |
142 | 可以通过`props`的`speed`开启或关闭进度条功能, 并且通过 `currentTime`属性控制从 60 秒开始播放
143 |
144 | :::demo 通过`speed`关闭进度条拖动功能。 并且通过 `currentTime`属性控制从 60 秒开始播放
145 |
146 | ```vue
147 |
148 |
149 |
153 |
154 |
155 |
156 |
169 |
170 |
171 | ```
172 |
173 | :::
174 |
175 | 还可以通过`props`的`control`属性 来控制是否显示控制器
176 | :::demo 通过`control` 来控制是否显示控制器
177 |
178 | ```vue
179 |
180 |
181 |
185 |
186 |
187 |
188 |
200 |
201 |
202 | ```
203 |
204 | :::
205 |
206 | ## 事件示例
207 |
208 | :::demo `vue3-video-play` 支持原生`video`所有事件。
209 |
210 | ```vue
211 |
212 |
213 |
223 |
224 |
225 |
226 |
247 |
248 |
249 | ```
250 |
251 | :::
252 |
253 | ## Hls m3u8 视频/直播
254 |
255 | :::demo `vue3-video-play` 支持 m3u8(hls)播放
256 |
257 | ```vue
258 |
259 |
260 |
267 |
268 |
269 |
276 |
277 |
278 | ```
279 |
280 | :::
281 |
282 | ## Props
283 |
284 | vue3-video-play 支持 video 原生所有 Attributes [video 原生属性](https://segmentfault.com/a/1190000008053507) 使用方式和 props 属性使用一致
285 |
286 | | 名称 | 说明 | 类型 | 可选值 | 默认值 |
287 | | ------------- | :-------------------: | :-----: | :------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------: |
288 | | width | 播放器宽度 | string | - | 800px |
289 | | height | 播放器高度 | string | - | 450px |
290 | | color | 播放器主色调 | string | - | #409eff |
291 | | src | 视频资源 | string | - | - |
292 | | title | 视频名称 | string | - | - |
293 | | type | 视频类型 | string | - | video/mp4|m3u8 |
294 | | poster | 视频封面 | string | - | 视频第一帧 |
295 | | webFullScreen | 网页全屏 | boolean | - | false |
296 | | speed | 是否支持快进快退 | boolean | - | true |
297 | | currentTime | 跳转到固定播放时间(s) | number | - | 0 |
298 | | playsinline | ios 点击屏幕是否全屏 | boolean | - | false |
299 | | muted | 静音 | boolean | - | false |
300 | | speedRate | 倍速配置 | array | - | ["2.0", "1.0", "1.5", "1.25", "0.75", "0.5"] |
301 | | autoPlay | 自动播放 | boolean | - | false,为 true 时会自动静音 |
302 | | loop | 循环播放 | boolean | - | false |
303 | | mirror | 镜像画面 | boolean | - | false |
304 | | ligthOff | 关灯模式 | boolean | - | false |
305 | | volume | 默认音量 | number | 0-1 | 0.3 |
306 | | control | 是否显示控制器 | boolean | - | true |
307 | | controlBtns | 控制器显示的按钮 | array | ['audioTrack', 'quality', 'speedRate', 'volume', 'setting', 'pip', 'pageFullScreen', 'fullScreen'] | ['audioTrack', 'quality', 'speedRate', 'volume', 'setting', 'pip', 'pageFullScreen', 'fullScreen'] |
308 | | preload | 预加载 | string | meta/auto/none | auto |
309 |
310 | ### `props`属性 `controlBtns` 按钮说明
311 |
312 | | 名称 | 说明 |
313 | | -------------- | :--------------: |
314 | | audioTrack | 音轨切换按钮 |
315 | | quality | 视频质量切换按钮 |
316 | | speedRate | 速率切换按钮 |
317 | | volume | 音量 |
318 | | setting | 设置 |
319 | | pip | 画中画按钮 |
320 | | pageFullScreen | 网页全屏按钮 |
321 | | fullScreen | 全屏按钮 |
322 |
323 | ## Events
324 |
325 | vue3-video-play 支持 video 原生所有事件 [video 默认事件](https://segmentfault.com/a/1190000008053507)
326 |
327 | | 事件名称 | 说明 | 回调 |
328 | | -------------- | ------------------ | ----- |
329 | | mirrorChange | 镜像翻转事件 | val |
330 | | loopChange | 循环播放开关事件 | val |
331 | | lightOffChange | 关灯模式事件 | val |
332 | | loadstart | 客户端开始请求数据 | event |
333 | | progress | 客户端正在请求数据 | event |
334 | | error | 请求数据时遇到错误 | event |
335 | | stalled | 网速失速 | event |
336 | | play | 开始播放时触发 | event |
337 | | pause | 暂停时触发 | event |
338 | | loadedmetadata | 成功获取资源长度 | event |
339 | | loadeddata | 缓冲中 | event |
340 | | waiting | 等待数据,并非错误 | event |
341 | | playing | 开始回放 | event |
342 | | canplay | 暂停状态下可以播放 | event |
343 | | canplaythrough | 可以持续播放 | event |
344 | | timeupdate | 更新播放时间 | event |
345 | | ended | 播放结束 | event |
346 | | ratechange | 播放速率改变 | event |
347 | | durationchange | 资源长度改变 | event |
348 | | volumechange | 音量改变 | event |
349 |
350 | ## 快捷键说明
351 |
352 | 支持快捷键操作
353 | | 键名 | 说明 |
354 | | ---------- | ----------------------------- |
355 | | Space | 暂停/播放 |
356 | | 方向右键 → | 单次快进 10s,长按 5 倍速播放 |
357 | | 方向左键 ← | 快退 10s |
358 | | 方向上键 ↑ | 音量+10% |
359 | | 方向下键 ↓ | 音量-10% |
360 | | Esc | 退出全屏/退出网页全屏 |
361 | | F | 全屏/退出全屏 |
362 |
363 | # Author
364 |
365 | [xdlumia](https://codelife.cc)
366 |
367 | # 点个 start
368 |
369 | [vue3-video-play](https://github.com/xdlumia/vue3-video-play)
370 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-27 22:49:49
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-27 22:57:16
6 | * @Description: file content
7 | */
8 | module.exports = {
9 | presets: [
10 | '@vue/app'
11 | ],
12 | "plugins": [
13 | "@babel/plugin-proposal-optional-chaining"
14 | ]
15 | }
--------------------------------------------------------------------------------
/docs/.vitepress/config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-19 18:56:59
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-10-10 22:37:33
6 | * @Description: file content
7 | */
8 | module.exports = {
9 | title: "Vue3VideoPlay", // 网站标题
10 | description: 'Vue3VideoPlay 基于vue3编写的视频播放器', //网站描述
11 | keyword: 'videoPlayer,vue3Video, hlsjs, m3u8视频流播放', //网站描述
12 | // lang: 'en-US', //语言
13 | base: '/vue3-video-play/',
14 | repo: 'vuejs/vitepress',
15 | head: [
16 | // 改变title的图标
17 | // [
18 | // 'link',
19 | // {
20 | // rel: 'icon',
21 | // href: '/img/linktolink.png', //图片放在public文件夹下
22 | // },
23 | // ],
24 | ],
25 | markdown: {
26 |
27 | config: (md) => {
28 | const {
29 | demoBlockPlugin
30 | } = require('vitepress-theme-demoblock')
31 | md.use(demoBlockPlugin)
32 | }
33 | },
34 | // 主题配置
35 | themeConfig: {
36 | // 头部导航
37 | nav: [{
38 | text: '首页',
39 | link: '/'
40 | }, {
41 | text: '使用指南',
42 | link: '/guide/install'
43 | }, {
44 | text: 'github',
45 | link: 'https://github.com/xdlumia/vue3-video-play'
46 | }],
47 | // 侧边导航
48 | sidebar: [{
49 | text: '更新日志',
50 | link: '/guide/changelog'
51 | }, {
52 | text: '使用指南',
53 | link: '/guide/install'
54 | }],
55 |
56 | }
57 | }
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-21 15:12:41
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-31 14:57:41
6 | * @Description: file content
7 | */
8 | import theme from 'vitepress/dist/client/theme-default'
9 | import 'vitepress-theme-demoblock/theme/styles/index.css'
10 | import './styles/index.css'
11 | import {
12 | registerComponents
13 | } from './register-components'
14 |
15 | import 'vue3-video-play/dist/style.css'
16 | const isClient = typeof window == 'object'
17 | export default {
18 | ...theme,
19 | async enhanceApp({
20 | app,
21 | router,
22 | siteData
23 | }) {
24 | if (isClient) {
25 | await import('vue3-video-play').then((m) => {
26 | app.use(m.default)
27 | })
28 | // await import('../../../lib/index.js').then((m) => {
29 | // app.use(m.default)
30 | // })
31 | }
32 | // app is the Vue 3 app instance from createApp()
33 | // router is VitePress' custom router (see `lib/app/router.js`)
34 | // siteData is a ref of current site-level metadata.
35 |
36 | registerComponents(app)
37 | }
38 | }
--------------------------------------------------------------------------------
/docs/.vitepress/theme/register-components.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-21 15:12:41
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-25 16:07:32
6 | * @Description: file content
7 | */
8 | import Demo from 'vitepress-theme-demoblock/components/Demo.vue'
9 | import DemoBlock from 'vitepress-theme-demoblock/components/DemoBlock.vue'
10 | // import {
11 | // videoPlay
12 | // } from 'vue3-video-play'
13 |
14 | export function registerComponents(app) {
15 | app.component('Demo', Demo)
16 | app.component('DemoBlock', DemoBlock)
17 | }
--------------------------------------------------------------------------------
/docs/.vitepress/theme/styles/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --c-brand: #646cff;
3 | --c-brand-light: #747bff;
4 | }
5 |
6 | .demo-block .xl-button {
7 | margin: 0 10px 10px 0;
8 | }
9 |
--------------------------------------------------------------------------------
/docs/components/index.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-21 19:20:46
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-10-11 12:59:55
6 | * @Description: file content
7 | */
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Vue3-video-play
15 |
适用于 Vue3 的 hls.js 播放器组件 | 并且支持MP4/WebM/Ogg格式
16 |
配置强大,UI还算好看
17 |
18 |
26 |
68 |
69 |
74 |
75 |
MIT Licensed | Copyright © 2021-present xdlumia
76 |
77 |
78 |
79 |
93 |
94 |
--------------------------------------------------------------------------------
/docs/guide/changelog.md:
--------------------------------------------------------------------------------
1 |
8 |
11 | # 更新日志
12 | ### 1.3.1-beta.2
13 | *2021-09-1*
14 | - 修复: 工具栏播放/暂停按钮构建后不能点击的问题
15 | - 修复: 缓冲动画构建后动画丢失的问题
16 | ### 1.3.1-beta.1
17 | *2021-09-1*
18 | - 新增: props参数增加`controlBtns`属性,自定义控制器按钮显示
19 | - 修复: m3u8切换视频源不生效的bug
20 | - 修复: 设置poster不生效的bug(#5)
21 | - 优化: 视频加载后中间增加播放按钮
22 | ### 1.3.0-rc.3
23 | *2021-08-31*
24 |
25 | - 新增: 支持hls视频/直播
26 | - 新增: 新增画质切换,需视频支持
27 | - 新增: 新增画音视切换,需视频支持
28 | - 新增: props参数增加`currentTime`属性,可跳转到固定时间播放
29 | - 新增: props参数增加`type`属性,视频格式
30 |
31 | ### 1.2.52
32 | *2021-08-27*
33 |
34 | - 优化: 优化重新播放按钮样式偏移问题
35 |
36 | ### 1.2.51
37 | *2021-08-25*
38 |
39 | - 新增: `mirrorChange` `loopChange` `lightOffChange` 事件
40 | - 新增: 倍速播放默认配置,增加`0.5`倍速播放
41 | - 新增: 资源播放失败错误状态
42 | - 新增: 非循环播放状态下播放结束增加重新播放按钮
43 | - 新增: 增加空格快捷键 `播放/暂停` 的操作
44 | - 修复: 关灯模式不能覆盖菜单栏区域
45 | - 优化: svg图标更换成字体图标,总体减少8KB
46 | - 优化: 如果音量为0关闭静音按钮 音量设置为5
47 |
48 |
49 | ### 1.2.4
50 | *2021-08-24*
51 |
52 | - Refactors 重构进度条拖动和音量拖动功能,精简代码
53 | - Chore 处理鼠标移动到进度条上距左或者距右时间显示显示不完整的问题
54 | ### 1.2.3
55 | *2021-08-23*
56 |
57 | - Chore 暂停状态下视频中央增加播放按钮
58 |
59 | ### 1.2.2
60 | *2021-08-23*
61 |
62 | - Add `props` 新增 `speed` 属性,开启或者关闭快进快退,默认true ([#2](https://github.com/xdlumia/vue3-video-play/issues/2))
63 | - Fix 方向键快进快退的时候会移动页面的问题
64 | - Chore 双击视频全屏/关闭全屏
65 | - Chore 修改`设置`按钮鼠标`hover`效果
66 |
67 |
--------------------------------------------------------------------------------
/docs/guide/install.md:
--------------------------------------------------------------------------------
1 |
8 | [](https://www.npmjs.com/package/vue3-video-play)
9 | [](https://www.npmjs.com/package/vue3-video-play)
10 | [](https://github.com/xdlumia/vue3-video-play/stargazers)
11 | [](https://github.com/xdlumia/vue3-video-play/issues)
12 | [](https://github.com/xdlumia/vue3-video-play/network)
13 | [](https://github.com/xdlumia/vue3-video-play)
14 | [](https://github.com/xdlumia/vue3-video-play)
15 |
16 | [](https://www.npmjs.com/package/vue3-video-play)
17 |
18 |
19 | **必须使用 vue@3.2.2及以上版本**
20 |
21 | ### Vue3-video-play
22 |
23 | 适用于 Vue3 的 hls.js 播放器组件 | 并且支持 MP4/WebM/Ogg 格式
24 | 配置强大,UI 还算好看
25 |
26 | ## 功能一览
27 | 1. 支持快捷键操作
28 | 2. 支持倍速播放设置
29 | 3. 支持镜像画面设置
30 | 4. 支持关灯模式设置
31 | 5. 支持画中画模式播放
32 | 6. 支持全屏/网页全屏播放
33 | 6. 支持从固定时间开始播放
34 | 8. 支持移动端,移动端会自动调用自带视频播放器
35 | 9. 支持hls视频流播放,支持直播
36 | 10. hls播放支持清晰度切换
37 | # 主页示例
38 |
39 | [https://codelife.cc/vue3-video-play/](https://codelife.cc/vue3-video-play/)
40 |
41 |
42 |
43 | ## 近期更新 v1.3.1-beta.2 🎉
44 | - 新增: 支持hls视频流播放
45 | - 新增: 新增画质切换,需视频流支持
46 | - 新增: 新增画音视切换,需视频流支持
47 | - 新增: props参数增加`currentTime`属性,可跳转到固定时间播放
48 | - 新增: props参数增加`type`属性,视频格式
49 | - 新增: props参数增加`controlBtns`属性,自定义控制器按钮显示
50 | - 新增: 右键菜单功能,右键菜单包涵,视频滤镜调节、快捷键说明、复制当前视频网址
51 | - 新增: `mirrorChange` `loopChange` `lightOffChange` 事件
52 | - 新增: 增加空格快捷键 `播放/暂停` 的操作
53 | - 优化: 如果音量为 0 关闭静音按钮 音量设置为 5
54 | # 使用指南
55 |
56 | ## 安装
57 | npm安装:
58 | ``` bash
59 | npm i vue3-video-play --save
60 | ```
61 | yarn安装:
62 | ``` bash
63 | yarn add vue3-video-play --save
64 | ```
65 |
66 | ## 开始使用
67 |
68 | #### 全局使用
69 |
70 | ``` js
71 | import { createApp } from 'vue'
72 | import App from './App.vue'
73 | let app = createApp(App)
74 |
75 | import vue3videoPlay from 'vue3-video-play' // 引入组件
76 | import 'vue3-video-play/dist/style.css' // 引入css
77 | app.use(vue3videoPlay)
78 |
79 | app.mount('#app')
80 | ```
81 |
82 | #### 组件内使用
83 |
84 | ```js
85 | // require style
86 | import 'vue3-video-play/dist/style.css'
87 | import { videoPlay } from 'vue-video-play'
88 | export default {
89 | components: {
90 | videoPlay
91 | }
92 | }
93 | ```
94 |
95 |
96 | ## 基本示例
97 | 提供了丰富了配置功能
98 | :::demo 自定义配置 比如自定义poster。
99 |
100 | ```vue
101 |
102 |
103 |
104 |
105 |
106 |
107 |
127 |
128 |
130 | ```
131 |
132 | :::
133 |
134 | 可以通过`props`的`speed`开启或关闭进度条功能, 并且通过 `currentTime`属性控制从60秒开始播放
135 |
136 | :::demo 通过`speed`关闭进度条拖动功能。 并且通过 `currentTime`属性控制从60秒开始播放
137 |
138 | ```vue
139 |
140 |
141 |
142 |
143 |
144 |
145 |
158 |
159 |
161 | ```
162 |
163 | :::
164 |
165 |
166 |
167 | 还可以通过`props`的`control`属性 来控制是否显示控制器
168 | :::demo 通过`control` 来控制是否显示控制器
169 | ```vue
170 |
171 |
172 |
173 |
174 |
175 |
176 |
188 |
189 |
191 |
192 | ```
193 | :::
194 |
195 |
196 |
197 | ## 事件示例
198 | :::demo `vue3-video-play` 支持原生`video`所有事件。
199 |
200 | ```vue
201 |
202 |
203 |
212 |
213 |
214 |
215 |
216 |
217 |
238 |
239 |
241 |
242 | ```
243 |
244 | :::
245 |
246 |
247 |
248 | ## Hls m3u8视频/直播
249 | :::demo `vue3-video-play` 支持m3u8(hls)播放
250 | ```vue
251 |
252 |
253 |
260 |
261 |
262 |
263 |
270 |
271 |
273 |
274 | ```
275 | :::
276 |
277 |
278 |
279 | ## Props
280 | vue3-video-play 支持video原生所有Attributes [video原生属性](https://segmentfault.com/a/1190000008053507) 使用方式和props属性使用一致
281 |
282 | | 名称 | 说明 | 类型 | 可选值 | 默认值 |
283 | | ------------- | :-------------------: | :-----: | :------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------: |
284 | | width | 播放器宽度 | string | - | 800px |
285 | | height | 播放器高度 | string | - | 450px |
286 | | color | 播放器主色调 | string | - | #409eff |
287 | | src | 视频资源 | string | - | - |
288 | | title | 视频名称 | string | - | - |
289 | | type | 视频类型 | string | - | video/mp4 |
290 | | poster | 视频封面 | string | - | 视频第一帧 |
291 | | webFullScreen | 网页全屏 | boolean | - | false |
292 | | speed | 是否支持快进快退 | boolean | - | true |
293 | | currentTime | 跳转到固定播放时间(s) | number | - | 0 |
294 | | playsinline | ios点击屏幕是否全屏 | boolean | - | false |
295 | | muted | 静音 | boolean | - | false |
296 | | speedRate | 倍速配置 | array | - | ["2.0", "1.0", "1.5", "1.25", "0.75", "0.5"] |
297 | | autoPlay | 自动播放 | boolean | - | false,为true时会自动静音 |
298 | | loop | 循环播放 | boolean | - | false |
299 | | mirror | 镜像画面 | boolean | - | false |
300 | | ligthOff | 关灯模式 | boolean | - | false |
301 | | volume | 默认音量 | number | 0-1 | 0.3 |
302 | | control | 是否显示控制器 | boolean | - | true |
303 | | controlBtns | 控制器显示的按钮 | array | ['audioTrack', 'quality', 'speedRate', 'volume', 'setting', 'pip', 'pageFullScreen', 'fullScreen'] | ['audioTrack', 'quality', 'speedRate', 'volume', 'setting', 'pip', 'pageFullScreen', 'fullScreen'] |
304 | | preload | 预加载 | string | meta/auto/none | auto |
305 |
306 | ### `props`属性 `controlBtns` 按钮说明
307 | | 名称 | 说明 |
308 | | -------------- | :--------------: |
309 | | audioTrack | 音轨切换按钮 |
310 | | quality | 视频质量切换按钮 |
311 | | speedRate | 速率切换按钮 |
312 | | volume | 音量 |
313 | | setting | 设置 |
314 | | pip | 画中画按钮 |
315 | | pageFullScreen | 网页全屏按钮 |
316 | | fullScreen | 全屏按钮 |
317 |
318 | ## Events
319 | vue3-video-play支持video原生所有事件 [video默认事件](https://segmentfault.com/a/1190000008053507)
320 |
321 | | 事件名称 | 说明 | 回调 |
322 | | -------------- | ------------------ | ----- |
323 | | mirrorChange | 镜像翻转事件 | val |
324 | | loopChange | 循环播放开关事件 | val |
325 | | lightOffChange | 关灯模式事件 | val |
326 | | loadstart | 客户端开始请求数据 | event |
327 | | progress | 客户端正在请求数据 | event |
328 | | error | 请求数据时遇到错误 | event |
329 | | stalled | 网速失速 | event |
330 | | play | 开始播放时触发 | event |
331 | | pause | 暂停时触发 | event |
332 | | loadedmetadata | 成功获取资源长度 | event |
333 | | loadeddata | 缓冲中 | event |
334 | | waiting | 等待数据,并非错误 | event |
335 | | playing | 开始回放 | event |
336 | | canplay | 暂停状态下可以播放 | event |
337 | | canplaythrough | 可以持续播放 | event |
338 | | timeupdate | 更新播放时间 | event |
339 | | ended | 播放结束 | event |
340 | | ratechange | 播放速率改变 | event |
341 | | durationchange | 资源长度改变 | event |
342 | | volumechange | 音量改变 | event |
343 |
344 | ## 快捷键说明
345 | 支持快捷键操作
346 | | 键名 | 说明 |
347 | | ---------- | ----------------------------- |
348 | | Space | 暂停/播放 |
349 | | 方向右键 → | 单次快进 10s,长按 5 倍速播放 |
350 | | 方向左键 ← | 快退 10s |
351 | | 方向上键 ↑ | 音量+10% |
352 | | 方向下键 ↓ | 音量-10% |
353 | | Esc | 退出全屏/退出网页全屏 |
354 | | F | 全屏/退出全屏 |
355 | # Author
356 |
357 | [xdlumia](https://codelife.cc)
358 |
359 | # 点个start
360 |
361 | [vue3-video-play](https://github.com/xdlumia/vue3-video-play)
362 |
363 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | ---
4 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Vite App
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/lib/components/d-contextmenu.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-26 12:13:47
4 | * @LastEditors: itab.link
5 | * @LastEditTime: 2023-11-09 14:44:07
6 | * @Description: file content
7 | */
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ state.dialogTitle }}
15 | X
16 |
17 |
18 |
22 | -
27 | {{ item.key }}
28 | {{ item.label }}
29 |
30 |
31 |
32 |
36 | -
37 | 饱和度
38 |
43 | {{ Math.round(filter.saturate * 255) }}
44 |
45 | -
46 | 亮度
47 |
52 | {{ Math.round(filter.brightness * 255) }}
53 |
54 | -
55 | 对比度
56 |
61 | {{ Math.round(filter.contrast * 255) }}
62 |
63 | 重置
70 |
71 |
72 |
73 |
74 |
82 |
83 |
84 |
85 |
204 |
205 |
--------------------------------------------------------------------------------
/lib/components/d-icon.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2020-10-27 10:31:35
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-28 07:14:47
6 | * @Description: file content
7 | */
8 |
9 |
12 |
13 |
14 |
20 |
33 |
41 |
--------------------------------------------------------------------------------
/lib/components/d-loading.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-20 11:00:41
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-09-01 18:08:30
6 | * @Description: file content
7 | */
8 |
9 |
14 |
15 |
16 |
17 |
18 | {{ text }}
19 |
20 |
21 |
22 |
23 | 正在缓冲...
24 |
25 |
26 |
27 |
28 | 重新播放
29 |
30 |
31 |
32 |
33 |
34 | 请求错误
35 |
36 |
37 |
38 |
39 |
40 |
41 |
64 |
65 |
104 |
--------------------------------------------------------------------------------
/lib/components/d-player-top.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-19 16:59:13
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-28 07:14:57
6 | * @Description: file content
7 | */
8 |
9 |
10 |
{{ title || '' }}
11 |
{{ currTime }}
12 |
13 |
14 |
15 |
49 |
50 |
--------------------------------------------------------------------------------
/lib/components/d-slider.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-23 21:12:57
4 | * @LastEditors: itab.link
5 | * @LastEditTime: 2023-11-09 15:33:06
6 | * @Description: file content
7 | */
8 |
9 |
16 |
17 |
18 |
24 | {{ props.hoverText }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
173 |
--------------------------------------------------------------------------------
/lib/components/d-status.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-20 13:52:52
4 | * @LastEditors: itab.link
5 | * @LastEditTime: 2023-11-09 15:28:50
6 | * @Description: file content
7 | */
8 |
9 |
10 |
11 |
17 | {{ ~~(state.volume * 100) }}%
18 |
19 |
23 |
24 | 5倍速播放中
25 |
26 |
27 |
28 |
29 |
34 |
35 |
--------------------------------------------------------------------------------
/lib/components/d-switch.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-20 09:34:45
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-28 07:15:07
6 | * @Description: file content
7 | */
8 |
9 |
10 |
19 |
20 |
21 |
22 |
23 |
62 |
--------------------------------------------------------------------------------
/lib/components/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2020-03-17 17:16:37
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2020-10-29 10:43:30
6 | * @Description: 收集公共组件 公共组件入口必须是index.js命名 并且必须正确命名name
7 | */
8 |
9 | const files = require.context('./', true, /\index.js$/)
10 |
11 | export default files.keys().filter(v => v != './index.js').reduce((arr, key) => {
12 | let comp = files(key).default
13 | return [...arr, comp]
14 | }, [])
15 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2020-10-29 10:08:49
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-22 08:43:52
6 | * @Description: Table
7 | */
8 | import videoPlay from './video-play/main.vue';
9 |
10 | function install(app) {
11 | app.component(videoPlay.name, videoPlay)
12 | }
13 | videoPlay.install = install
14 | export {
15 | videoPlay,
16 | install
17 | }
18 | export default videoPlay;
--------------------------------------------------------------------------------
/lib/style/animate.less:
--------------------------------------------------------------------------------
1 | .rotating{
2 | animation: rotating 2s linear infinite;
3 | }
4 | @keyframes rotating{
5 | 100%{-webkit-transform:rotate(360deg);}
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/lib/style/base.less:
--------------------------------------------------------------------------------
1 | .iconfont{display: inline-block;}
2 | .d-flex-x,
3 | .d-flex-y,
4 | .d-flex-center {
5 | display: flex;
6 | }
7 |
8 | .d-flex-x {
9 | align-items: center;
10 | }
11 |
12 | .d-flex-y {
13 |
14 | justify-content: center;
15 | }
16 |
17 | .d-flex-center {
18 | justify-content: center;
19 | align-items: center;
20 | }
21 |
22 | .mr5 {
23 | margin-right: 5px;
24 | }
25 |
26 | .mr10 {
27 | margin-right: 10px;
28 | }
29 |
30 | .ml5 {
31 | margin-left: 5px;
32 | }
33 |
34 | .ml10 {
35 | margin-left: 10px;
36 | }
37 |
38 | .d-pointer {
39 | cursor: pointer;
40 | }
--------------------------------------------------------------------------------
/lib/style/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "iconfont"; /* Project id 2178361 */
3 | src: url('iconfont.woff2?t=1629866025665') format('woff2')
4 | }
5 |
6 | .iconfont {
7 | font-family: "iconfont" !important;
8 | font-size: 16px;
9 | font-style: normal;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | }
13 |
14 | .icon-replay:before {
15 | content: "\e631";
16 | }
17 |
18 | .icon-pip:before {
19 | content: "\e820";
20 | }
21 |
22 | .icon-loading:before {
23 | content: "\e62e";
24 | }
25 |
26 | .icon-play:before {
27 | content: "\e851";
28 | }
29 |
30 | .icon-pause:before {
31 | content: "\e863";
32 | }
33 |
34 | .icon-screen:before {
35 | content: "\e88f";
36 | }
37 |
38 | .icon-web-screen:before {
39 | content: "\e609";
40 | }
41 |
42 | .icon-settings:before {
43 | content: "\e60c";
44 | }
45 |
46 | .icon-volume-down:before {
47 | content: "\e60d";
48 | }
49 |
50 | .icon-volume-up:before {
51 | content: "\e60e";
52 | }
53 |
54 | .icon-volume-mute:before {
55 | content: "\e60f";
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/lib/style/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xdlumia/vue3-video-play/d1b8d34556df0182ffeb41d494fb51bcb823a6ef/lib/style/iconfont.woff2
--------------------------------------------------------------------------------
/lib/style/reset.less:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html,
7 | body,
8 | div,
9 | span,
10 | applet,
11 | object,
12 | iframe,
13 | h1,
14 | h2,
15 | h3,
16 | h4,
17 | h5,
18 | h6,
19 | p,
20 | blockquote,
21 | pre,
22 | a,
23 | abbr,
24 | acronym,
25 | address,
26 | big,
27 | cite,
28 | code,
29 | del,
30 | dfn,
31 | em,
32 | img,
33 | ins,
34 | kbd,
35 | q,
36 | s,
37 | samp,
38 | small,
39 | strike,
40 | strong,
41 | sub,
42 | sup,
43 | tt,
44 | var,
45 | b,
46 | u,
47 | i,
48 | center,
49 | dl,
50 | dt,
51 | dd,
52 | ol,
53 | ul,
54 | li,
55 | fieldset,
56 | form,
57 | label,
58 | legend,
59 | table,
60 | caption,
61 | tbody,
62 | tfoot,
63 | thead,
64 | tr,
65 | th,
66 | td,
67 | article,
68 | aside,
69 | canvas,
70 | details,
71 | embed,
72 | figure,
73 | figcaption,
74 | footer,
75 | header,
76 | hgroup,
77 | menu,
78 | nav,
79 | output,
80 | ruby,
81 | section,
82 | summary,
83 | time,
84 | mark,
85 | audio,
86 | video {
87 | margin: 0;
88 | padding: 0;
89 | border: 0;
90 | font-size: 100%;
91 | font: inherit;
92 | vertical-align: baseline;
93 | }
94 |
95 | /* HTML5 display-role reset for older browsers */
96 | article,
97 | aside,
98 | details,
99 | figcaption,
100 | figure,
101 | footer,
102 | header,
103 | hgroup,
104 | menu,
105 | nav,
106 | section {
107 | display: block;
108 | }
109 |
110 | body {
111 | line-height: 1;
112 | }
113 |
114 | ol,
115 | ul {
116 | list-style: none;
117 | }
118 |
119 | blockquote,
120 | q {
121 | quotes: none;
122 | }
123 |
124 | blockquote:before,
125 | blockquote:after,
126 | q:before,
127 | q:after {
128 | content: '';
129 | content: none;
130 | }
131 |
132 | table {
133 | border-collapse: collapse;
134 | border-spacing: 0;
135 | }
136 |
137 | textarea {
138 | font-family: inherit;
139 | }
--------------------------------------------------------------------------------
/lib/style/transition.less:
--------------------------------------------------------------------------------
1 |
2 | .d-fade-in-enter-active,
3 | .d-fade-in-leave-active {
4 | transition: .5s;
5 | }
6 | .d-fade-in-enter-from,
7 | .d-fade-in-leave-to {
8 | opacity: 0;
9 | }
10 |
11 | .d-scale-out-enter-active,
12 | .d-scale-out-leave-active {
13 | transition: .3s;
14 | }
15 | .d-scale-out-leave-to {
16 | transform: scale(1.3);
17 | opacity: 0;
18 | }
19 |
20 | .rotateHover{
21 | transition: .2s;
22 | &:hover{
23 | transform: rotate(90deg);
24 | }
25 | }
--------------------------------------------------------------------------------
/lib/style/vPlayer.less:
--------------------------------------------------------------------------------
1 | .d-player-wrap {
2 | position: relative;
3 | overflow: hidden;
4 | background-color: #000;
5 |
6 | &.web-full-screen {
7 | z-index: 9999999;
8 | position: fixed;
9 | left: 0;
10 | top: 0;
11 | width: 100vw !important;
12 | height: 100vh !important;
13 | }
14 |
15 | .d-player-video {
16 | position: relative;
17 | z-index: 1;
18 | width: 100%;
19 | height: 100%;
20 | .d-player-video-poster{
21 | position: absolute;
22 | height: 100%;
23 | width: 100%;
24 | top:0;
25 | left:0;
26 | img{
27 | display: block;
28 | width: 100%;
29 | height: 100%;
30 | object-fit: cover;
31 | }
32 | }
33 |
34 | .d-player-video-main {
35 | width: 100%;
36 | height: 100%;
37 | transition: .2s;
38 |
39 |
40 | &.video-mirror {
41 | transform: rotateY(180deg);
42 | }
43 | }
44 |
45 | }
46 |
47 | .d-player-control {
48 | transition: .1s;
49 | transform: translateY(40px);
50 | position: absolute;
51 | z-index: 2;
52 | left: 0;
53 | bottom: 0;
54 | height: 50px;
55 | width: 100%;
56 | color: #fff;
57 |
58 | .d-control-progress {
59 | width: 100%;
60 | position: relative;
61 | height: 10px;
62 | cursor: pointer;
63 |
64 | .d-progress-bar {
65 | position: absolute;
66 | left: 0;
67 | right: 0;
68 | bottom: 0;
69 | width: 100%;
70 | transition: height 0.1s;
71 | height: 3px;
72 | z-index: 1;
73 |
74 | // pointer-events: none;
75 | //对 d-slider内的组件样式修改
76 | :deep(.d-slider__runway) {
77 | transition: height 0.1s;
78 | height: 100%;
79 |
80 | .d-slider__bar::before {
81 | transform: translateY(-50%) scale(0, 0);
82 | }
83 | }
84 | }
85 |
86 | &:hover {
87 | .d-progress-bar {
88 | height: 100%;
89 |
90 | :deep(.d-slider__bar::before) {
91 | transform: translateY(-50%) scale(1, 1) !important;
92 | }
93 | }
94 | }
95 | }
96 |
97 | .d-control-tool {
98 | position: absolute;
99 | padding: 0 10px;
100 | box-sizing: border-box;
101 | background: rgba(0, 0, 0, 0.8);
102 | display: flex;
103 | justify-content: space-between;
104 | align-items: center;
105 | top: 10px;
106 | left: 0;
107 | bottom: 0;
108 | width: 100%;
109 | box-sizing: border-box;
110 |
111 | .d-tool-bar {
112 | display: flex;
113 | height: 100%;
114 |
115 | .d-tool-item {
116 | position: relative;
117 | height: 100%;
118 | cursor: pointer;
119 | text-align: center;
120 | padding: 0 8px;
121 | display: flex;
122 | align-items: center;
123 | font-size: 13px;
124 |
125 | .d-tool-item-main {
126 | position: absolute;
127 | white-space: nowrap;
128 | z-index: 2;
129 | bottom: 98%;
130 | left: 50%;
131 | padding: 6px 16px;
132 | box-sizing: border-box;
133 | display: none;
134 | background: rgba(0, 0, 0, 0.95);
135 | border-radius: 5px;
136 | transform: translateX(-50%);
137 | }
138 |
139 | &:hover {
140 | .d-tool-item-main {
141 | display: flex;
142 | }
143 | }
144 | }
145 | }
146 |
147 | // 时间
148 | .d-tool-time {
149 | font-size: 12px;
150 | color: #fff;
151 | font-weight: 300;
152 |
153 | .total-time {
154 | color: rgba(255, 255, 255, 0.8);
155 | }
156 | }
157 |
158 | // 音量
159 | .volume-box {
160 | height: 160px;
161 | width: 50px;
162 | display: flex;
163 | align-items: center;
164 | justify-content: center;
165 |
166 | .volume-main {
167 | height: 90%;
168 | display: flex;
169 | width: 60px;
170 | flex-direction: column;
171 | align-items: center;
172 |
173 | .volume-text-size {
174 | margin-bottom: 10px;
175 | font-size: 12px;
176 | font-weight: 400;
177 | }
178 |
179 | &.is-muted {
180 | :deep(.d-slider__bar) {
181 | height: 0 !important
182 | }
183 | }
184 | }
185 | }
186 |
187 | .speed-main {
188 | padding: 0 10px;
189 |
190 | li {
191 | cursor: pointer;
192 | line-height: 34px;
193 | font-size: 12px;
194 | color: #fff;
195 |
196 | &:hover {
197 | opacity: .8;
198 | }
199 |
200 | &.speed-active {
201 | color: rgba(var(--primary-color), 1);
202 | font-weight: bold;
203 | }
204 | }
205 | }
206 | }
207 | }
208 |
209 | &.d-player-wrap-hover {
210 | .d-player-control {
211 | transform: translateY(0px);
212 | }
213 | }
214 | }
215 |
216 | .d-player-state,
217 | .d-player-input {
218 | position: absolute;
219 | left: 0;
220 | top: 0;
221 | right: 0;
222 | bottom: 40px;
223 | display: flex;
224 | justify-content: center;
225 | align-items: center;
226 | overflow: hidden;
227 | z-index: 1;
228 | }
229 |
230 | .d-player-input {
231 | width: 100%;
232 | border: none;
233 | opacity: 0;
234 | cursor: default;
235 |
236 | }
237 |
238 | .d-play-btn {
239 | width: 90px;
240 | height: 90px;
241 | color: #fff;
242 | display: flex;
243 | align-items: center;
244 | justify-content: center;
245 | background-color: rgba(0, 0, 0, .7);
246 | border-radius: 50%;
247 | }
248 |
249 |
250 | // 黑幕
251 | .d-player-lightoff {
252 | position: fixed;
253 | left: 0;
254 | top: 0;
255 | width: 100vw;
256 | height: 100vh;
257 | background-color: rgba(0, 0, 0, .9);
258 | }
259 |
260 | // 关灯模式
261 | .is-lightoff {
262 | z-index: 999998;
263 | }
--------------------------------------------------------------------------------
/lib/utils/dom.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-23 21:17:54
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-26 14:03:35
6 | * @Description: file content
7 | */
8 | export const on = function (
9 | element: Element | HTMLElement | Document | Window,
10 | event: string,
11 | handler: EventListenerOrEventListenerObject,
12 | useCapture = false,
13 | ): void {
14 | if (element && event && handler) {
15 | element.addEventListener(event, handler, useCapture)
16 | }
17 | }
18 | /* istanbul ignore next */
19 | export const off = function (
20 | element: Element | HTMLElement | Document | Window,
21 | event: string,
22 | handler: EventListenerOrEventListenerObject,
23 | useCapture = false,
24 | ): void {
25 | if (element && event && handler) {
26 | element.removeEventListener(event, handler, useCapture)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/utils/util.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-19 12:50:35
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-26 09:50:44
6 | * @Description: file content
7 | */
8 |
9 | // hex转rgb
10 | export const hexToRgba = (hex) => {
11 | return `${parseInt("0x" + hex.slice(1, 3))},${parseInt(
12 | "0x" + hex.slice(3, 5)
13 | )},${parseInt("0x" + hex.slice(5, 7))}`;
14 | }
15 | export const firstUpperCase = (str) => str.charAt(0).toUpperCase() + str.slice(1)
16 | // 电影时间格式化
17 | export const timeFormat = (time) => {
18 | let hh: any = ~~(time / 3600);
19 | let mm: any = ~~((time % 3600) / 60);
20 | let ss: any = ~~(time % 60); //取整
21 | hh = hh < 10 ? "0" + hh : hh; //个位数补0
22 | mm = mm < 10 ? "0" + mm : mm; //个位数补0
23 | ss = ss < 10 ? "0" + ss : ss; //个位数补0
24 | return `${hh}:${mm}:${ss}`;
25 | }
26 | // 是否是移动端
27 | export const isMobile = !!("ontouchstart" in window)
28 | // 全屏模式
29 | export const toggleFullScreen = (el) => {
30 | //如果当前是全屏状态,就退出全屏,否则进入全屏状态
31 | //获取当前的全屏状态
32 | let documentEL = (document as any)
33 | let isFullscreen = documentEL.webkitIsFullScreen || documentEL.fullscreen;
34 | if (!isFullscreen) {
35 | const inFun =
36 | el.requestFullscreen || el.webkitRequestFullScreen;
37 | //让当前播放器进入全屏状态
38 | inFun.call(el);
39 | } else {
40 | const exitFun =
41 | document.exitFullscreen || documentEL.webkitExitFullScreen;
42 | //退出全屏状态要使用document
43 | exitFun.call(documentEL);
44 | }
45 | return !isFullscreen
46 |
47 | }
48 | // 画中画模式
49 | export const requestPictureInPicture = (el: HTMLElement | Document | Window) => {
50 | if ((document as any).pictureInPictureElement) {
51 | (document as any).exitPictureInPicture().catch((error) => {
52 | console.log(error, "Video failed to leave Picture-in-Picture mode.");
53 | });
54 |
55 | } else {
56 | //开启
57 | (el as any).requestPictureInPicture().catch((error) => {
58 | console.log(error, "Video failed to enter Picture-in-Picture mode.");
59 | });
60 | }
61 | }
--------------------------------------------------------------------------------
/lib/video-play/main.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2020-11-03 16:29:47
4 | * @LastEditors: itab.link
5 | * @LastEditTime: 2023-11-09 15:50:42
6 | * @Description: file content
7 | */
8 |
9 |
10 |
22 |
23 |
24 |
30 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
88 |
89 |
90 |
91 |
92 |
97 |
98 |
107 |
108 |
109 |
254 |
255 |
256 |
257 |
263 |
622 |
623 |
624 |
--------------------------------------------------------------------------------
/lib/video-play/plugins/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-25 11:19:35
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-09-02 16:03:36
6 | * @Description: file content
7 | */
8 | import type { PropType } from "vue";
9 | export const videoEmits = [
10 | "loadstart",
11 | "play",
12 | "pause",
13 | "playing",
14 | "seeking",
15 | "seeked",
16 | "waiting",
17 | "durationchange",
18 | "progress",
19 | "canplay",
20 | "timeupdate",
21 | "ended",
22 | "error",
23 | "stalled",
24 | ];
25 | export const defineProps = {
26 | width: { type: String, default: "800px" },
27 | height: { type: String, default: "450px" },
28 | color: { type: String, default: "#409eff" },
29 | src: { required: true, type: String, default: "" }, //视频源
30 | title: { type: String, default: "" }, //视频名称
31 | type: { type: String, default: "video/mp4" }, //视频类型
32 | poster: { type: String, default: "" }, //封面
33 | webFullScreen: { type: Boolean, default: false }, //网页全屏
34 | speed: { type: Boolean, default: true }, //是否支持快进快退 //移动端不支持
35 | currentTime: { type: Number, default: 0 }, //当前播放时间
36 | playsinline: { type: Boolean, default: false }, //ios端 点击播放是否全屏
37 | muted: { type: Boolean, default: false }, //静音
38 | speedRate: {
39 | type: Array,
40 | default: () => ["2.0", "1.5", "1.25", "1.0", "0.75", "0.5"],
41 | }, //播放倍速
42 | autoPlay: { type: Boolean, default: false }, //自动播放
43 | loop: { type: Boolean, default: false }, //循环播放
44 | mirror: { type: Boolean, default: false }, //镜像画面
45 | ligthOff: { type: Boolean, default: false }, //关灯模式
46 | volume: { type: [String, Number], default: 0.3 }, //默认音量大小
47 | control: { type: Boolean, default: true }, //是否显示控制器
48 | controlBtns: {
49 | type: Array as PropType>,
50 | default: [
51 | "audioTrack",
52 | "quality",
53 | "speedRate",
54 | "volume",
55 | "setting",
56 | "pip",
57 | "pageFullScreen",
58 | "fullScreen",
59 | ],
60 | }, //是否显示控制器
61 | preload: { type: String, default: "auto" }, //预加载
62 | };
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue3-video-play",
3 | "version": "1.3.2",
4 | "description": "hls.js player component for Vue3",
5 | "files": [
6 | "dist",
7 | "lib"
8 | ],
9 | "main": "./dist/index.umd.js",
10 | "module": "./dist/index.es.js",
11 | "scripts": {
12 | "dev": "vite",
13 | "build": "vue-tsc --noEmit && vite build",
14 | "pub": "node ./scripts/publish.js",
15 | "serve": "vite preview",
16 | "docs:dev": "vitepress dev docs",
17 | "docs:build": "vitepress build docs",
18 | "docs:serve": "vitepress serve docs",
19 | "docs:pub": "sh ./scripts/gh-pages.sh"
20 | },
21 | "author": {
22 | "name": "xdlumia",
23 | "email": "xdq@live.cn",
24 | "url": "https://codelife.cc"
25 | },
26 | "homepage": "https://codelife.cc/vue3-video-play/",
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/xdlumia/vue3-video-play.git"
30 | },
31 | "bugs": {
32 | "url": "https://github.com/xdlumia/vue3-video-play/issues"
33 | },
34 | "dependencies": {
35 | "hls.js": "^1.4.12",
36 | "throttle-debounce": "^5.0.0",
37 | "vue": "^3.2.25"
38 | },
39 | "devDependencies": {
40 | "@vitejs/plugin-vue": "^4.4.1",
41 | "@vue/compiler-sfc": "^3.3.8",
42 | "chalk": "^5.3.0",
43 | "less": "^4.2.0",
44 | "less-loader": "^11.1.3",
45 | "readline-sync": "^1.4.10",
46 | "shelljs": "^0.8.5",
47 | "typescript": "^5.2.2",
48 | "vite": "^4.5.0",
49 | "vitepress": "^0.22.4",
50 | "vitepress-theme-demoblock": "^3.0.3",
51 | "vue-tsc": "^1.8.22",
52 | "vue3-video-play": "^1.3.1-beta.6"
53 | },
54 | "keywords": [
55 | "vue 3",
56 | "vue3",
57 | "vue",
58 | "vue3-video-play",
59 | "hlsjs",
60 | "hls.js",
61 | "vue video player",
62 | "vue3 video player",
63 | "video player video",
64 | "video player",
65 | "vue player",
66 | "vue video"
67 | ],
68 | "license": "ISC"
69 | }
--------------------------------------------------------------------------------
/scripts/gh-pages.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env zsh
2 | ###
3 | # @Author: web.王晓冬
4 | # @Date: 2021-08-21 22:18:02
5 | # @LastEditors: web.王晓冬
6 | # @LastEditTime: 2021-09-01 15:27:27
7 | # @Description: file content
8 | ###
9 |
10 | # 确保脚本抛出遇到的错误
11 | set -e
12 |
13 | # 生成静态文件
14 | yarn run docs:build
15 |
16 | # 进入生成的文件夹
17 | cd ../docs/.vitepress/dist
18 |
19 | # 如果是发布到自定义域名
20 | # echo 'www.example.com' > CNAME
21 |
22 | git init
23 | git add -A
24 | git commit -m 'deploy to the gh-pages'
25 |
26 | # 如果发布到 https://.github.io
27 | # git push -f git@github.com:xdlumia/vue3-video-play.git gh-pages
28 | # git push -f git@github.com:xdlumia/vue3-video-play.git master:gh-pages
29 | # git push -f git@github.com:xdlumia/xdlumia.github.io.git master:gh-pages
30 | # git push -f https://github.com/xdlumia/vue3-video-play.git master:gh-pages
31 | git push -f https://github.com/xdlumia/xdlumia.github.io.git master:gh-pages
32 |
--------------------------------------------------------------------------------
/scripts/publish copy.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2020-03-18 12:36:57
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-25 20:22:03
6 | * @Description: file content
7 | */
8 | // shell字体颜色 默认=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,紫色=35,天蓝色=36,白色=3
9 |
10 | const shell = require('shelljs');
11 | const readlineSync = require('readline-sync');
12 | const path = require('path');
13 | let packageJSON = require(path.resolve('package.json'));
14 | const chalk = require('chalk');
15 |
16 | const defaultLog = (log) => console.log(chalk.blue(`------${log}-----`))
17 | const errorLog = (log) => console.log(chalk.red(`------${log}-----`))
18 | const successLog = (log) => console.log(chalk.green(`------${log}-----`))
19 | // 当前版本
20 | const currentVersion = packageJSON.version
21 | // 版本标识
22 | const [vrsionFlag] = process.argv.slice(2)
23 | // 获取git当前分支
24 | let currentBranch = shell.exec('git symbolic-ref --short -q HEAD', {
25 | async: false,
26 | silent: true
27 | }).stdout.trim();
28 | if (currentBranch != 'dev') {
29 | shell.echo("\033[1;31m Error: 当前是 " + currentBranch + " 分支 请切换到dev分支\033[0m");
30 | return
31 | }
32 |
33 | // 新版本
34 | var confirm = readlineSync.question(`Current is "v${currentVersion}".\n\
35 | // -- p:patch m:minor s:major n:Exit default:patch
36 | // -- are you sure? (p/s/m/n)`)
37 | // 直接升级小号
38 | if (confirm.trim() == '' || confirm.trim().toLowerCase() == 'p') {
39 | shell.exec('npm version patch')
40 | }
41 | // 则升级一位中号,大号不动,小号置为空
42 | else if (confirm.trim().toLowerCase() == 'm') {
43 | shell.exec('npm version minor')
44 | }
45 | // 升级一位大号,其他位都置为0
46 | else if (confirm.trim().toLowerCase() == 's') {
47 | shell.exec('npm version major')
48 | } else {
49 | shell.echo("\033[1;31m Error: 输入错误 已自动退出\033[0m")
50 | shell.exit()
51 | }
52 |
53 | shell.exec('git checkout master');
54 | shell.exec('git pull');
55 | shell.exec('git merge dev');
56 | if (shell.exec('git push origin master --tags').code != 0) {
57 | shell.echo("\033[1;31mError: git push ogigin master 失败! 已退出\033[0m");
58 | shell.exec('git checkout dev');
59 | shell.exit()
60 | return
61 | }
62 |
63 | shell.exec('git checkout dev');
64 | shell.exec('git rebase master');
65 | // shell.exec('git push origin dev');
66 | if (shell.exec('git push origin dev').code != 0) {
67 | shell.echo("\033[1;31mError: git push origin dev 失败! 已退出\033[0m");
68 | shell.exec('git checkout dev');
69 | shell.exit()
70 | return
71 | }
72 | if (shell.exec('npm publish').code != 0) {
73 | shell.echo("\033[1;31mError: npm publish 失败! 已退出\033[0m");
74 | shell.exit()
75 | return
76 | }
77 | shell.echo("\033[1;32mSuccess Publish success!\033[0m");
78 | shell.exit()
--------------------------------------------------------------------------------
/scripts/publish.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2020-03-18 12:36:57
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-27 10:54:53
6 | * @Description: file content
7 | */
8 | // shell字体颜色 默认=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,紫色=35,天蓝色=36,白色=3
9 |
10 | const shell = require('shelljs');
11 | const readlineSync = require('readline-sync');
12 | const path = require('path');
13 | let packageJSON = require(path.resolve('package.json'));
14 |
15 | const defaultLog = (log) => console.log(`--------------${log}---------`)
16 | const errorLog = (log) => console.log('\x1B[31m%s\x1B[0m', `--------------${log}-----------`)
17 | const successLog = (log) => console.log('\x1B[32m%s\x1B[0m', `---------------${log}--------`)
18 |
19 | // 当前版本
20 | const currentVersion = packageJSON.version
21 | // 版本标识
22 | const [commitInfo] = process.argv.slice(2) || 'auto commit'
23 | // 获取git当前分支
24 | let currentBranch = shell.exec('git symbolic-ref --short -q HEAD', {
25 | async: false,
26 | silent: true
27 | }).stdout.trim();
28 | if (currentBranch != 'dev') {
29 | errorLog(`当前是${currentBranch}分支 请切换到dev分支`)
30 | // shell.echo("\033[1;31m Error: 当前是 " + currentBranch + " 分支 请切换到dev分支\033[0m");
31 | return
32 | }
33 | // shell.exec('git add .');
34 | // shell.exec(`git commit -m ${commitInfo}`);
35 | // shell.exec('git push');
36 | // successLog('dev分支提交成功')
37 | // shell.exec('git checkout main');
38 | // shell.exec('git pull');
39 | // shell.exec('git merge dev');
40 | // defaultLog('dev分支合并到main分支')
41 | // if (shell.exec('git push origin main --tags').code != 0) {
42 | // // shell.echo("\033[1;31mError: git push ogigin main 失败! 已退出\033[0m");
43 | // errorLog(`git push ogigin main 失败! 已退出 已`)
44 | // shell.exec('git checkout dev');
45 | // shell.exit()
46 | // return
47 | // }
48 | // successLog('main分支提交成功')
49 | // shell.exec('git checkout dev');
50 | // shell.exec('git push origin dev');
51 |
52 |
53 | // 新版本
54 | var confirm = readlineSync.question(`Current is "v${currentVersion}".\n\
55 | // -- p:patch m:minor s:major n:Exit default:patch
56 | // -- are you sure? (p/s/m/n)`)
57 | // 直接升级小号
58 | if (confirm.trim() == '' || confirm.trim().toLowerCase() == 'p') {
59 | shell.exec('npm version patch')
60 | }
61 | // 则升级一位中号,大号不动,小号置为空
62 | else if (confirm.trim().toLowerCase() == 'm') {
63 | shell.exec('npm version minor')
64 | }
65 | // 升级一位大号,其他位都置为0
66 | else if (confirm.trim().toLowerCase() == 's') {
67 | shell.exec('npm version major')
68 | } else {
69 | errorLog(`输入错误 已自动退出`)
70 | // shell.echo("\033[1;31m Error: 输入错误 已自动退出\033[0m")
71 | shell.exit()
72 | }
73 |
74 | shell.exec('yarn build');
75 |
76 | if (shell.exec('npm pub').code != 0) {
77 | shell.echo("\033[1;31mError: npm publish 失败! 已退出\033[0m");
78 | shell.exit()
79 | return
80 | }
81 | // shell.echo("\033[1;32mSuccess Publish success!\033[0m");
82 | successLog('Publish success!')
83 | shell.exit()
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-20 19:10:57
4 | * @LastEditors: itab.link
5 | * @LastEditTime: 2023-11-09 15:38:31
6 | * @Description: file content
7 | */
8 |
9 |
12 |
13 | {{ options.webFullScreen }}
14 |
19 |
24 |
25 |
26 |
27 |
67 |
68 |
70 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-19 10:25:40
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-20 20:07:18
6 | * @Description: file content
7 | */
8 | import { createApp } from 'vue'
9 | import App from './App.vue'
10 |
11 | const app = createApp(App)
12 |
13 |
14 | app.mount('#app')
15 |
--------------------------------------------------------------------------------
/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import { DefineComponent } from 'vue'
3 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
4 | const component: DefineComponent<{}, {}, any>
5 | export default component
6 | }
7 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-22 10:47:24
4 | * @LastEditors: web.王晓冬
5 | * @LastEditTime: 2021-08-23 19:37:14
6 | * @Description: file content
7 | */
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "jsx": "preserve",
8 | "sourceMap": true,
9 | "resolveJsonModule": true,
10 | "noImplicitAny": false,
11 | "esModuleInterop": true,
12 | "lib": [
13 | "esnext",
14 | "dom"
15 | ]
16 | },
17 | "allowJs": true,
18 | "include": [
19 | "src/**/*.ts",
20 | "src/**/*.d.ts",
21 | "src/**/*.tsx",
22 | "src/**/*.vue"
23 | ]
24 | }
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: web.王晓冬
3 | * @Date: 2021-08-19 10:25:40
4 | * @LastEditors: itab.link
5 | * @LastEditTime: 2023-11-09 15:27:03
6 | * @Description: file content
7 | */
8 | import { defineConfig } from "vite";
9 | import vue from "@vitejs/plugin-vue";
10 |
11 | // https://vitejs.dev/config/
12 | export default defineConfig({
13 | plugins: [vue()],
14 | server: {
15 | port: 3005,
16 | open: true,
17 | // 反向代理
18 | proxy: {
19 | "/api": {
20 | target: "http://xxx.xxxxx.xxx/",
21 | changeOrigin: true,
22 | rewrite: (path) => path.replace(/^\/api/, ""),
23 | },
24 | },
25 | },
26 | build: {
27 | terserOptions: {
28 | compress: {
29 | drop_debugger: true,
30 | },
31 | },
32 | lib: {
33 | entry: "./lib/index.js",
34 | name: "index",
35 | fileName: "index",
36 | },
37 | rollupOptions: {
38 | // 确保外部化处理那些你不想打包进库的依赖
39 | external: ["vue"],
40 | output: {
41 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
42 | globals: {
43 | vue: "Vue",
44 | },
45 | },
46 | },
47 | // hmr: { overlay: false }
48 | },
49 | });
50 |
--------------------------------------------------------------------------------
/yarn-error.log:
--------------------------------------------------------------------------------
1 | Arguments:
2 | C:\Program Files\nodejs\node.exe C:\Users\admin\AppData\Roaming\npm\node_modules\yarn\bin\yarn.js add vue3-video-play@1.3.0-rc.2
3 |
4 | PATH:
5 | C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\nodejs\;C:\Program Files\Git\cmd;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Users\admin\AppData\Local\Microsoft\WindowsApps;C:\Users\admin\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\admin\AppData\Roaming\npm;C:\Users\admin\AppData\Roaming\nrm;
6 |
7 | Yarn version:
8 | 1.22.11
9 |
10 | Node version:
11 | 14.17.1
12 |
13 | Platform:
14 | win32 x64
15 |
16 | Trace:
17 | Error: EPERM: operation not permitted, unlink 'E:\github\vue3-video-play\node_modules\esbuild\esbuild.exe'
18 |
19 | npm manifest:
20 | {
21 | "name": "vue3-video-play",
22 | "version": "1.3.0-rc.2",
23 | "description": "hls.js player component for Vue3",
24 | "files": [
25 | "dist",
26 | "lib"
27 | ],
28 | "main": "./dist/index.umd.js",
29 | "module": "./dist/index.es.js",
30 | "scripts": {
31 | "dev": "vite",
32 | "build": "vue-tsc --noEmit && vite build",
33 | "pub": "node ./scripts/publish.js",
34 | "serve": "vite preview",
35 | "docs:dev": "vitepress dev docs",
36 | "docs:build": "vitepress build docs",
37 | "docs:serve": "vitepress serve docs",
38 | "docs:pub": "sh ./scripts/gh-pages.sh"
39 | },
40 | "author": {
41 | "name": "xdlumia",
42 | "email": "214005111@qq.com",
43 | "url": "https://codelife.cc"
44 | },
45 | "homepage": "https://codelife.cc/vue3-video-play/",
46 | "repository": {
47 | "type": "git",
48 | "url": "https://github.com/xdlumia/vue3-video-play.git"
49 | },
50 | "bugs": {
51 | "url": "https://github.com/xdlumia/vue3-video-play/issues"
52 | },
53 | "dependencies": {
54 | "hls.js": "^1.0.10",
55 | "throttle-debounce": "^3.0.1",
56 | "vue": "^3.2.2"
57 | },
58 | "devDependencies": {
59 | "@vitejs/plugin-vue": "^1.4.0",
60 | "@vue/compiler-sfc": "^3.2.2",
61 | "chalk": "^4.1.2",
62 | "less": "^4.1.1",
63 | "less-loader": "^10.0.1",
64 | "readline-sync": "^1.4.10",
65 | "shelljs": "^0.8.4",
66 | "typescript": "^4.3.5",
67 | "vite": "^2.5.0",
68 | "vitepress": "^0.16.1",
69 | "vitepress-theme-demoblock": "^1.1.1",
70 | "vue-tsc": "^0.2.3",
71 | "vue3-video-play": "^1.2.52"
72 | },
73 | "keywords": [
74 | "vue 3",
75 | "vue3",
76 | "vue",
77 | "vue3-video-play",
78 | "hlsjs",
79 | "hls.js",
80 | "vue video player",
81 | "vue3 video player",
82 | "video player video",
83 | "video player",
84 | "vue player",
85 | "vue video"
86 | ],
87 | "license": "ISC"
88 | }
89 |
90 | yarn manifest:
91 | No manifest
92 |
93 | Lockfile:
94 | No lockfile
95 |
--------------------------------------------------------------------------------