mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-26 00:23:21 -08:00
refactor(danmuku): [PKG-DANMUKU-05] own renderer settings and heatmap resources
This commit is contained in:
1 parent
12332df221
commit
053d211fb0
38 files changed
+65630
-2420
No files matched your search
@@ -33,6 +33,24 @@ export function danmukuCandidateEnvironment(implementation, settings = {}) {
|
||||
? { ...implementation, code: `globalThis.Worker = class { constructor() { throw globalThis.__danmukuCandidateWorkerError } };\n${implementation.code}` }
|
||||
: implementation
|
||||
const env = danmukuEnvironment(initialWorker, settings)
|
||||
const createElement = env.context.document.createElement.bind(env.context.document)
|
||||
const removeChild = function (child) {
|
||||
const index = this.children.indexOf(child)
|
||||
if (index < 0)
|
||||
throw new Error('Cannot remove a node from a different parent')
|
||||
this.children.splice(index, 1)
|
||||
child.parentElement = null
|
||||
return child
|
||||
}
|
||||
env.context.document.createElement = (...args) => {
|
||||
const element = createElement(...args)
|
||||
element.removeChild = removeChild
|
||||
return element
|
||||
}
|
||||
for (const element of Object.values(env.art.template)) {
|
||||
if (element?.children)
|
||||
element.removeChild = removeChild
|
||||
}
|
||||
if (settings.initialWorkerError)
|
||||
env.context.__danmukuCandidateWorkerError = settings.initialWorkerError
|
||||
const consoleWarnings = []
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import vm from 'node:vm'
|
||||
import { parseHTML } from 'linkedom'
|
||||
import { danmukuCandidateEnvironment } from './danmuku-candidate.js'
|
||||
|
||||
export function danmukuSettingEnvironment(implementation, { readyState = 'complete' } = {}) {
|
||||
const env = danmukuCandidateEnvironment(implementation)
|
||||
// Load actual core utilities without initializing the core's browser banner/style.
|
||||
delete env.context.document
|
||||
const core = evaluate(implementation.coreCode)
|
||||
const { document, window } = parseHTML('<!doctype html><html><head></head><body><div id="external"><span id="unrelated">Keep me</span></div><div id="second"></div><div id="player"><div id="controls"></div><div id="danmuku"></div></div></body></html>')
|
||||
Object.defineProperty(document, 'readyState', { value: readyState, configurable: true })
|
||||
// Linkedom parses the real templates and dispatches events, but has no layout.
|
||||
Object.defineProperties(window.HTMLElement.prototype, {
|
||||
clientWidth: { configurable: true, get() { return this.testRect?.width ?? 640 } },
|
||||
clientHeight: { configurable: true, get() { return this.testRect?.height ?? 360 } },
|
||||
offsetWidth: { configurable: true, get() { return this.clientWidth } },
|
||||
offsetHeight: { configurable: true, get() { return this.clientHeight } },
|
||||
})
|
||||
window.HTMLElement.prototype.getBoundingClientRect = function () {
|
||||
const { left = 0, top = 0, width = 640, height = 360 } = this.testRect || {}
|
||||
return { x: left, y: top, left, top, width, height, right: left + width, bottom: top + height }
|
||||
}
|
||||
env.context.document = document
|
||||
env.context.Element = window.Element
|
||||
env.context.HTMLElement = window.HTMLElement
|
||||
env.context.window.document = document
|
||||
env.context.window.getComputedStyle = element => element.style
|
||||
env.context.getComputedStyle = element => element.style
|
||||
const documentListeners = []
|
||||
const add = document.addEventListener.bind(document)
|
||||
const remove = document.removeEventListener.bind(document)
|
||||
document.addEventListener = (type, callback, options) => {
|
||||
const record = { type, callback, active: true, options }
|
||||
documentListeners.push(record)
|
||||
record.wrapped = (...args) => {
|
||||
if (options?.once)
|
||||
record.active = false
|
||||
return callback(...args)
|
||||
}
|
||||
add(type, record.wrapped, options)
|
||||
}
|
||||
document.removeEventListener = (type, callback, options) => {
|
||||
for (const record of documentListeners) {
|
||||
if (record.type === type && record.callback === callback && record.active) {
|
||||
record.active = false
|
||||
remove(type, record.wrapped, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
function evaluate(code) {
|
||||
env.context.__settingModule = { exports: {} }
|
||||
vm.runInContext(`(function(module, exports) {\n${code}\n})(__settingModule, __settingModule.exports)`, env.context, { timeout: 1000 })
|
||||
return env.context.__settingModule.exports
|
||||
}
|
||||
env.art.constructor.utils = core.utils
|
||||
env.art.template = {
|
||||
$player: document.querySelector('#player'),
|
||||
$controlsCenter: document.querySelector('#controls'),
|
||||
$danmuku: document.querySelector('#danmuku'),
|
||||
}
|
||||
const exported = evaluate(implementation.code)
|
||||
env.factory = typeof exported === 'function' ? exported : exported.default
|
||||
env.proxies.length = 0
|
||||
env.art.proxy = (target, type, callback, options) => {
|
||||
assert.ok(target?.addEventListener, `A real template target is required for ${type}`)
|
||||
const record = { target, type, callback, options, active: true }
|
||||
env.proxies.push(record)
|
||||
target.addEventListener(type, callback, options)
|
||||
record.dispose = () => {
|
||||
record.active = false
|
||||
target.removeEventListener(type, callback, options)
|
||||
}
|
||||
return record.dispose
|
||||
}
|
||||
env.art.events = { remove: dispose => dispose() }
|
||||
const event = (type, values = {}) => Object.assign(new window.Event(type, { bubbles: true, cancelable: true }), values)
|
||||
return {
|
||||
...env,
|
||||
document,
|
||||
domWindow: window,
|
||||
documentListeners,
|
||||
event,
|
||||
query: selector => document.querySelector(selector),
|
||||
reloadPlugin() { return evaluate(implementation.code) },
|
||||
createPeer() {
|
||||
const peerListeners = new Map()
|
||||
const player = document.createElement('div')
|
||||
const controls = document.createElement('div')
|
||||
const danmuku = document.createElement('div')
|
||||
player.append(controls, danmuku)
|
||||
document.body.appendChild(player)
|
||||
const art = {
|
||||
...env.art,
|
||||
isDestroy: false,
|
||||
template: { $player: player, $controlsCenter: controls, $danmuku: danmuku },
|
||||
on(name, callback) { peerListeners.set(name, [...peerListeners.get(name) || [], callback]) },
|
||||
off(name, callback) { peerListeners.set(name, (peerListeners.get(name) || []).filter(item => item !== callback)) },
|
||||
emit(name, ...args) {
|
||||
for (const callback of [...peerListeners.get(name) || []])
|
||||
callback(...args)
|
||||
},
|
||||
}
|
||||
return {
|
||||
art,
|
||||
destroy() {
|
||||
art.isDestroy = true
|
||||
art.emit('destroy')
|
||||
},
|
||||
}
|
||||
},
|
||||
listenerCount: () => [...env.listeners.values()].reduce((count, callbacks) => count + callbacks.length, 0),
|
||||
proxyCount: () => env.proxies.filter(record => record.active).length,
|
||||
dispatch(target, type, values) { return target.dispatchEvent(event(type, values)) },
|
||||
timeout() {
|
||||
const [id, timer] = env.timers.entries().next().value || []
|
||||
assert.ok(timer, 'Expected a pending controlled timer')
|
||||
env.timers.delete(id)
|
||||
timer.callback()
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user