Kliping
KlipingBerandaDokumentasiKomponenBlocksVariasiTemplateDice UIHooks
Memulai
  • Pengenalan
  • Instalasi
  • Kredit
State
  • useBoolean
  • useControllableValue
  • useCounter
  • useDebounce
  • useResetState
  • useThrottle
  • useToggle
Lanjutan
  • useCreation
  • useCustomCompareEffect
  • useLatest
  • useLockFn
  • useMemoizedFn
  • usePrevious
Siklus Hidup
  • useDebounceEffect
  • useDebounceFn
  • useDeepCompareEffect
  • useDeepCompareLayoutEffect
  • useEffectEvent
  • useEffectWithTarget
  • useInterval
  • useIsHydrated
  • useIsomorphicLayoutEffect
  • useMount
  • useThrottleEffect
  • useThrottleFn
  • useTimeout
  • useUnmount
  • useUpdate
  • useUpdateEffect
Peramban
  • useActiveElement
  • useBattery
  • useClickAnyWhere
  • useClickAway
  • useClipboard
  • useDocumentVisibility
  • useElementSize
  • useEventListener
  • useFps
  • useFullscreen
  • useHash
  • useHover
  • useInViewport
  • useIsMatchMedia
  • useIsOnline
  • useMouse
  • useNetwork
  • useOs
  • useScrollLock
  • useTextSelection
  • useTitle
Pengembangan
  • useWhyDidYouUpdate
Paket Luar
  • useMcp
  • useQuery
  • useStickToBottom
  • useSwr
Pustaka
  • isBrowser
  • createContext
  • bacaElemen
  • saatTerpasang

useFullscreen

SebelumnyaBerikutnya

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.

ts
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 ​

ts
export interface UseFullscreenOptions {
  /** Keluar dari layar penuh saat scope-nya mati. Bawaan: false. */
  autoExit?: boolean
}

export function useFullscreen(
  target?: TargetElemen,
  options: UseFullscreenOptions = {},
)
useFpsuseHash

Di halaman ini

PemasanganAPI
Dirilis di bawah lisensi MIT. Ditulis ulang untuk Vue dari shadcn-vue, shadcn-ui-blocks, dan shadcn-hooks. Hak cipta © 2026 Kliping