useFullscreen
Layar penuh, dengan seluruh awalan vendor yang masih dibutuhkan.
Kotak ini bisa dibuat layar penuh.
Ini composable terpanjang di registry, dan panjangnya bukan hiasan. Fullscreen API punya empat nama berbeda untuk hal yang sama, dan Safari di iOS memakai jalur yang sama sekali lain: metodenya ada di elemen <video>, bukan di document. Setiap nama yang didaftar di bawah pernah jadi satu-satunya yang bekerja di suatu peramban.
Keadaannya dibaca dari peramban lewat peristiwa, bukan disimpan sendiri saat enter() dipanggil. Itu wajib: orang bisa keluar dari layar penuh dengan menekan Escape, dan tidak ada satu pun kode kita yang tahu itu terjadi kecuali melalui fullscreenchange.
Pemasangan
npx shadcn-vue@latest add @kliping/use-fullscreen Perlu @kliping terdaftar di components.json project Anda. Lihat cara menyiapkannya.
Ikut terpasang sendiri: is-browser, saat-terpasang, target, use-event-listener.
Salin dulu is-browser, saat-terpasang, target, use-event-listener — masing-masing memuat sumbernya di halamannya sendiri — lalu tempel berkas ini.
import { getCurrentScope, onScopeDispose, readonly, ref } from 'vue'
import { isBrowser } from '@/lib/is-browser'
import { saatTerpasang } from '@/lib/saat-terpasang'
import { bacaElemen } from '@/lib/target'
import { useEventListener } from '@/composables/use-event-listener'
import type { TargetElemen } from '@/lib/target'
const PERISTIWA = [
'fullscreenchange',
'webkitfullscreenchange',
'webkitendfullscreen',
'mozfullscreenchange',
'MSFullscreenChange',
]
const METODE_MASUK = [
'requestFullscreen',
'webkitRequestFullscreen',
'webkitEnterFullscreen',
'webkitEnterFullScreen',
'webkitRequestFullScreen',
'mozRequestFullScreen',
'msRequestFullscreen',
]
const METODE_KELUAR = [
'exitFullscreen',
'webkitExitFullscreen',
'webkitExitFullScreen',
'webkitCancelFullScreen',
'mozCancelFullScreen',
'msExitFullscreen',
]
const PROP_AKTIF = [
'fullScreen',
'webkitIsFullScreen',
'webkitDisplayingFullscreen',
'mozFullScreen',
'msFullscreenElement',
]
const PROP_ELEMEN = [
'fullscreenElement',
'webkitFullscreenElement',
'mozFullScreenElement',
'msFullscreenElement',
]
export interface UseFullscreenOptions {
/** Keluar dari layar penuh saat scope-nya mati. Bawaan: false. */
autoExit?: boolean
}
export function useFullscreen(
target?: TargetElemen,
options: UseFullscreenOptions = {},
) {
const { autoExit = false } = options
const isSupported = ref(false)
const isFullscreen = ref(false)
let metodeMasuk: string | undefined
let metodeKeluar: string | undefined
let propAktif: string | undefined
let propElemen: string | undefined
const elemen = (): any =>
(bacaElemen(target) ?? (isBrowser ? document.documentElement : undefined))
const cari = (daftar: string[]) => {
const el = elemen()
return daftar.find(nama =>
(el && nama in el) || (isBrowser && nama in document),
)
}
const bacaKemampuan = () => {
if (!isBrowser)
return
metodeMasuk = cari(METODE_MASUK)
metodeKeluar = cari(METODE_KELUAR)
propAktif = cari(PROP_AKTIF)
propElemen = cari(PROP_ELEMEN)
isSupported.value = Boolean(elemen() && metodeMasuk && metodeKeluar && propAktif)
}
/** Apakah ada sesuatu — apa pun — yang sedang layar penuh. */
const adaYangPenuh = (): boolean => {
if (!propAktif || !isBrowser)
return false
const doc = document as any
if (doc[propAktif] != null)
return Boolean(doc[propAktif])
// Safari di iOS menaruhnya di elemennya, bukan di document.
const el = elemen()
if (el && el[propAktif] != null)
return Boolean(el[propAktif])
return false
}
/** Apakah yang sedang layar penuh adalah elemen kita. */
const elemenKitaPenuh = (): boolean => {
if (!propElemen || !isBrowser)
return false
return (document as any)[propElemen] === elemen()
}
const exit = async () => {
if (!isSupported.value || !isFullscreen.value || !metodeKeluar)
return
const doc = document as any
const el = elemen()
if (doc[metodeKeluar] != null)
await doc[metodeKeluar]()
else if (el?.[metodeKeluar] != null)
await el[metodeKeluar]()
isFullscreen.value = false
}
const enter = async () => {
if (!isSupported.value || isFullscreen.value)
return
// Peramban menolak permintaan kedua selagi yang pertama masih aktif, jadi
// yang sedang berjalan dilepas dulu — termasuk kalau itu milik orang lain.
if (adaYangPenuh())
await exit()
const el = elemen()
if (metodeMasuk && el?.[metodeMasuk] != null) {
await el[metodeMasuk]()
isFullscreen.value = true
}
}
const toggle = async () => {
await (isFullscreen.value ? exit() : enter())
}
const saatBerubah = () => {
const penuh = adaYangPenuh()
// Hanya diikuti kalau tidak ada yang penuh sama sekali, atau yang penuh
// memang elemen kita. Tanpa syarat itu, elemen lain yang masuk layar penuh
// akan membuat composable ini ikut mengaku penuh.
if (!penuh || elemenKitaPenuh())
isFullscreen.value = penuh
}
useEventListener(PERISTIWA, saatBerubah, {
target: () => (isBrowser ? document : undefined),
passive: true,
})
useEventListener(PERISTIWA, saatBerubah, {
target: () => elemen(),
passive: true,
})
saatTerpasang(() => {
bacaKemampuan()
saatBerubah()
})
if (getCurrentScope()) {
onScopeDispose(() => {
if (autoExit)
void exit()
})
}
return {
isSupported: readonly(isSupported),
isFullscreen: readonly(isFullscreen),
enter,
exit,
toggle,
}
}API
export interface UseFullscreenOptions {
/** Keluar dari layar penuh saat scope-nya mati. Bawaan: false. */
autoExit?: boolean
}
export function useFullscreen(
target?: TargetElemen,
options: UseFullscreenOptions = {},
)