mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-11 05:26:15 -08:00
`art.play()` is an async wrapper around the native `video.play()` promise, which rejects when a pending play request is interrupted (`pause()`, `load()`, a new `src`, or the element being removed) and when autoplay is blocked. Every internal caller fires it from an event handler and cannot await it, so the rejection escapes as a global `unhandledrejection`. Error trackers report it as an uncaught crash even though the player recovers on its own — for example destroying the player right after mount, which is routine when the video sits in a modal the user can close quickly. Add a `silencePromise` util (the same approach video.js uses) and apply it to the non-awaited internal call sites: mask, play control, mini player, loop restart, autoPlayback jump, hotkey and click toggles. `switchMix` is fixed too, for a second symptom: `await art.play()` inside the async `video:canplay` handler both leaked the rejection and left the `switchUrl` promise pending forever, since the throw never reached the enclosing executor's `reject`. It now settles. The public API is unchanged: `art.play()` and `art.toggle()` still return the original promise, so applications that call them keep their own rejection to handle. Only the internal, non-awaited copy is silenced.
66 lines
1.6 KiB
JavaScript
66 lines
1.6 KiB
JavaScript
import { includeFromEvent, isMobile, silencePromise } from '../utils'
|
|
|
|
export default function clickInit(art, events) {
|
|
const {
|
|
constructor,
|
|
template: { $player, $video },
|
|
} = art
|
|
|
|
function onDocumentClick(event) {
|
|
if (includeFromEvent(event, $player)) {
|
|
art.isInput = event.target.tagName === 'INPUT'
|
|
art.isFocus = true
|
|
art.emit('focus', event)
|
|
}
|
|
else {
|
|
art.isInput = false
|
|
art.isFocus = false
|
|
art.emit('blur', event)
|
|
}
|
|
}
|
|
|
|
art.on('document:click', onDocumentClick)
|
|
art.on('document:contextmenu', onDocumentClick)
|
|
|
|
let clickTimes = []
|
|
events.proxy($video, 'click', (event) => {
|
|
const now = Date.now()
|
|
clickTimes.push(now)
|
|
const { MOBILE_CLICK_PLAY, DBCLICK_TIME, MOBILE_DBCLICK_PLAY, DBCLICK_FULLSCREEN } = constructor
|
|
|
|
const clicks = clickTimes.filter(t => now - t <= DBCLICK_TIME)
|
|
switch (clicks.length) {
|
|
case 1:
|
|
art.emit('click', event)
|
|
|
|
if (isMobile) {
|
|
if (!art.isLock && MOBILE_CLICK_PLAY) {
|
|
silencePromise(art.toggle())
|
|
}
|
|
}
|
|
else {
|
|
silencePromise(art.toggle())
|
|
}
|
|
clickTimes = clicks
|
|
break
|
|
case 2:
|
|
art.emit('dblclick', event)
|
|
|
|
if (isMobile) {
|
|
if (!art.isLock && MOBILE_DBCLICK_PLAY) {
|
|
silencePromise(art.toggle())
|
|
}
|
|
}
|
|
else {
|
|
if (DBCLICK_FULLSCREEN) {
|
|
art.fullscreen = !art.fullscreen
|
|
}
|
|
}
|
|
clickTimes = []
|
|
break
|
|
default:
|
|
clickTimes = []
|
|
}
|
|
})
|
|
}
|