mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-26 08:51:20 -08:00
feat: add Picture-in-Picture plugin for ArtPlayer
- Introduced `artplayer-plugin-pip` to enable Picture-in-Picture functionality. - Updated `artplayer.d.ts` to export new types related to the plugin. - Added example usage of the plugin in `pip.js`. - Created TypeScript definitions for the plugin in `artplayer-plugin-pip.d.ts`. - Implemented the plugin logic in `index.js` with necessary styles and event handling. - Updated documentation to include the new plugin and its usage. - Added README and package.json for the new plugin.
This commit is contained in:
1 parent
bd071b2d63
commit
c23520a4a6
12 files changed
+853
-279
No files matched your search
@@ -0,0 +1,11 @@
|
||||
# artplayer-plugin-pip
|
||||
|
||||
pip plugin for ArtPlayer
|
||||
|
||||
## Demo
|
||||
|
||||
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-pip/index.js&example=pip)
|
||||
|
||||
## License
|
||||
|
||||
MIT © Harvey Zack
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "artplayer-plugin-pip",
|
||||
"version": "1.0.0",
|
||||
"description": "pip plugin for ArtPlayer",
|
||||
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://artplayer.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"html5",
|
||||
"video",
|
||||
"player"
|
||||
],
|
||||
"main": "dist/artplayer-plugin-pip.js",
|
||||
"types": "types/artplayer-plugin-pip.d.ts",
|
||||
"legacy": "dist/artplayer-plugin-pip.legacy.js",
|
||||
"browserslist": "last 1 Chrome version"
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
export default function artplayerPluginPip(userOptions = {}) {
|
||||
const options = {
|
||||
width: 480,
|
||||
height: 270,
|
||||
placeholder: `Playing in Document Picture-in-Picture`,
|
||||
...userOptions,
|
||||
}
|
||||
|
||||
return (art) => {
|
||||
if (!('documentPictureInPicture' in window)) {
|
||||
art.notice.show = 'Document Picture-in-Picture is not supported'
|
||||
return
|
||||
}
|
||||
|
||||
const { proxy, icons, i18n, template: { $container }, constructor: { utils: { append, tooltip } } } = art
|
||||
|
||||
const state = {
|
||||
win: null,
|
||||
originalParent: null,
|
||||
originalNext: null,
|
||||
currentDoc: null,
|
||||
placeholder: null,
|
||||
}
|
||||
|
||||
function copyStylesTo(targetDoc) {
|
||||
const baseStyle = `
|
||||
html, body { margin:0; padding:0; width:100%; height:100%; background:#000; overflow:hidden; }
|
||||
#__art_dpip_root { position:absolute; inset:0; display:flex; }
|
||||
#__art_dpip_root > * { width:100% !important; height:100% !important; }
|
||||
`
|
||||
|
||||
const $style = document.querySelector('#artplayer-style')
|
||||
const clone = targetDoc.createElement('style')
|
||||
clone.textContent = baseStyle + $style.textContent
|
||||
targetDoc.head.appendChild(clone)
|
||||
|
||||
const meta = targetDoc.createElement('meta')
|
||||
meta.name = 'viewport'
|
||||
meta.content = 'width=device-width,initial-scale=1'
|
||||
targetDoc.head.appendChild(meta)
|
||||
}
|
||||
|
||||
async function open() {
|
||||
if (state.win)
|
||||
return
|
||||
|
||||
try {
|
||||
const pipWin = await window.documentPictureInPicture.requestWindow({
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
})
|
||||
|
||||
state.win = pipWin
|
||||
state.currentDoc = pipWin.document
|
||||
|
||||
state.placeholder = document.createElement('div')
|
||||
state.placeholder.className = 'artplayer-pip-placeholder'
|
||||
state.placeholder.style.cssText = `display:flex;justify-content:center;align-items:center;width:100%;height:100%;`
|
||||
state.placeholder.textContent = options.placeholder
|
||||
state.originalParent = $container.parentNode
|
||||
state.originalNext = $container.nextSibling
|
||||
state.originalParent.insertBefore(state.placeholder, state.originalNext)
|
||||
|
||||
const root = pipWin.document.createElement('div')
|
||||
root.id = '__art_dpip_root'
|
||||
pipWin.document.body.appendChild(root)
|
||||
|
||||
copyStylesTo(pipWin.document)
|
||||
|
||||
const adopted = pipWin.document.adoptNode($container)
|
||||
root.appendChild(adopted)
|
||||
state.currentDoc = pipWin.document
|
||||
|
||||
const onResize = () => art.resize?.()
|
||||
const onPageHide = () => close()
|
||||
|
||||
pipWin.addEventListener('resize', onResize)
|
||||
pipWin.addEventListener('pagehide', onPageHide)
|
||||
|
||||
state.cleanup = () => {
|
||||
pipWin.removeEventListener('resize', onResize)
|
||||
pipWin.removeEventListener('pagehide', onPageHide)
|
||||
}
|
||||
|
||||
art.emit('document-pip', true)
|
||||
}
|
||||
catch (err) {
|
||||
art.notice.show = 'Document Picture-in-Picture open failed'
|
||||
console.warn('[artplayer-plugin-pip] open failed:', err)
|
||||
}
|
||||
}
|
||||
|
||||
function restoreToOriginalDocument() {
|
||||
if (!state.placeholder)
|
||||
return
|
||||
const backDoc = state.placeholder.ownerDocument || document
|
||||
const adoptedBack = backDoc.adoptNode($container)
|
||||
state.originalParent.insertBefore(adoptedBack, state.placeholder)
|
||||
state.placeholder.remove()
|
||||
state.placeholder = null
|
||||
state.currentDoc = backDoc
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (!state.win)
|
||||
return
|
||||
try {
|
||||
state.cleanup?.()
|
||||
restoreToOriginalDocument()
|
||||
state.win.close()
|
||||
state.win = null
|
||||
art.emit('document-pip', false)
|
||||
}
|
||||
catch (err) {
|
||||
art.notice.show = 'Document Picture-in-Picture close failed'
|
||||
console.warn('[artplayer-plugin-pip] close failed:', err)
|
||||
}
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
if (state.win) {
|
||||
close()
|
||||
}
|
||||
else {
|
||||
open()
|
||||
}
|
||||
}
|
||||
|
||||
art.controls.add({
|
||||
name: 'document-pip',
|
||||
position: 'right',
|
||||
index: 40,
|
||||
tooltip: art.i18n.get('PIP Mode'),
|
||||
mounted: ($control) => {
|
||||
append($control, icons.pip)
|
||||
proxy($control, 'click', toggle)
|
||||
art.on('document-pip', (value) => {
|
||||
tooltip($control, i18n.get(value ? 'Exit PIP Mode' : 'PIP Mode'))
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
art.on('destroy', close)
|
||||
|
||||
return {
|
||||
name: 'artplayerPluginPip',
|
||||
open,
|
||||
close,
|
||||
toggle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.artplayerPluginPip = artplayerPluginPip
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.artplayer-plugin-pip {
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
interface Option {
|
||||
width?: number
|
||||
height?: number
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
interface Result {
|
||||
name: 'artplayerPluginPip'
|
||||
}
|
||||
|
||||
declare const artplayerPluginPip: (option: Option) => (art: Artplayer) => Result
|
||||
|
||||
export default artplayerPluginPip
|
||||
Reference in new issue
Block a user