feat(web): implement web pip method and event (#4370)

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
This commit is contained in:
YangJH 2025-01-18 23:19:55 +09:00 committed by GitHub
parent 449dfb62b5
commit 8dc10fd4b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 5 deletions

View File

@ -317,7 +317,7 @@ Example:
### `onPictureInPictureStatusChanged` ### `onPictureInPictureStatusChanged`
<PlatformsList types={['iOS', 'Android']} /> <PlatformsList types={['iOS', 'Android', 'web']} />
Callback function that is called when picture in picture becomes active or inactive. Callback function that is called when picture in picture becomes active or inactive.

View File

@ -80,7 +80,7 @@ Future:
### `enterPictureInPicture` ### `enterPictureInPicture`
<PlatformsList types={['Android', 'iOS']} /> <PlatformsList types={['Android', 'iOS', 'web']} />
`enterPictureInPicture()` `enterPictureInPicture()`
@ -114,7 +114,7 @@ NOTE: Video ads cannot start when you are using the PIP on iOS (more info availa
### `exitPictureInPicture` ### `exitPictureInPicture`
<PlatformsList types={['Android', 'iOS']} /> <PlatformsList types={['Android', 'iOS', 'web']} />
`exitPictureInPicture()` `exitPictureInPicture()`

View File

@ -50,6 +50,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
onVolumeChange, onVolumeChange,
onEnd, onEnd,
onPlaybackStateChanged, onPlaybackStateChanged,
onPictureInPictureStatusChanged,
}, },
ref, ref,
) => { ) => {
@ -180,6 +181,31 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
[setFullScreen], [setFullScreen],
); );
const enterPictureInPicture = useCallback(() => {
try {
if (!nativeRef.current) {
console.error('Video Component is not mounted');
} else {
nativeRef.current.requestPictureInPicture();
}
} catch (e) {
console.error(e);
}
}, []);
const exitPictureInPicture = useCallback(() => {
if (
nativeRef.current &&
nativeRef.current === document.pictureInPictureElement
) {
try {
document.exitPictureInPicture();
} catch (e) {
console.error(e);
}
}
}, []);
useImperativeHandle( useImperativeHandle(
ref, ref,
() => ({ () => ({
@ -193,8 +219,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
dismissFullscreenPlayer, dismissFullscreenPlayer,
setFullScreen, setFullScreen,
save: unsupported, save: unsupported,
enterPictureInPicture: unsupported, enterPictureInPicture,
exitPictureInPicture: unsupported, exitPictureInPicture,
restoreUserInterfaceForPictureInPictureStopCompleted: unsupported, restoreUserInterfaceForPictureInPictureStopCompleted: unsupported,
nativeHtmlVideoRef: nativeRef, nativeHtmlVideoRef: nativeRef,
}), }),
@ -210,6 +236,8 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
presentFullscreenPlayer, presentFullscreenPlayer,
dismissFullscreenPlayer, dismissFullscreenPlayer,
setFullScreen, setFullScreen,
enterPictureInPicture,
exitPictureInPicture,
], ],
); );
@ -254,6 +282,27 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
nativeRef.current.playbackRate = rate; nativeRef.current.playbackRate = rate;
}, [rate]); }, [rate]);
useEffect(() => {
if (
typeof onPictureInPictureStatusChanged !== 'function' ||
!nativeRef.current
) {
return;
}
const onEnterPip = () =>
onPictureInPictureStatusChanged({isActive: true});
const onLeavePip = () =>
onPictureInPictureStatusChanged({isActive: false});
const video = nativeRef.current;
video.addEventListener('enterpictureinpicture', onEnterPip);
video.addEventListener('leavepictureinpicture', onLeavePip);
return () => {
video.removeEventListener('enterpictureinpicture', onEnterPip);
video.removeEventListener('leavepictureinpicture', onLeavePip);
};
}, [onPictureInPictureStatusChanged]);
useMediaSession(src?.metadata, nativeRef, showNotificationControls); useMediaSession(src?.metadata, nativeRef, showNotificationControls);
return ( return (