返回 presentation-ai
usePresentModeOrientation.ts
根目录 / src / hooks / presentation / usePresentModeOrientation.ts
1 "use client";
2
3 import { useEffect, useState } from "react";
4
5 type PresentModeViewportState = {
6 isPhoneViewport: boolean;
7 isPortraitViewport: boolean;
8 };
9
10 function getViewportDimensions() {
11 if (typeof window === "undefined") {
12 return { width: 0, height: 0 };
13 }
14
15 return {
16 width: Math.max(
17 window.innerWidth,
18 Math.round(window.visualViewport?.width ?? 0),
19 ),
20 height: Math.max(
21 window.innerHeight,
22 Math.round(window.visualViewport?.height ?? 0),
23 ),
24 };
25 }
26
27 function isPhoneLikeViewport(width: number, height: number): boolean {
28 if (typeof window === "undefined" || typeof navigator === "undefined") {
29 return false;
30 }
31
32 const smallerSide = Math.min(width, height);
33 const largerSide = Math.max(width, height);
34 const phoneSizedViewport = smallerSide <= 500 && largerSide <= 1000;
35 const coarsePointer = window.matchMedia("(pointer: coarse)").matches;
36 const noHover = window.matchMedia("(hover: none)").matches;
37 const hasTouchPoints = navigator.maxTouchPoints > 0;
38
39 return phoneSizedViewport || coarsePointer || (noHover && hasTouchPoints);
40 }
41
42 function getPresentModeViewportState(): PresentModeViewportState {
43 const { width, height } = getViewportDimensions();
44
45 return {
46 isPhoneViewport: isPhoneLikeViewport(width, height),
47 isPortraitViewport: height > width,
48 };
49 }
50
51 /**
52 * Returns viewport dimensions for present mode scaling.
53 * Always returns the actual viewport dimensions (no swapping).
54 */
55 export function getPresentModeViewportDimensions() {
56 const { width, height } = getViewportDimensions();
57
58 return { width, height };
59 }
60
61 export function usePresentModeOrientation(isPresenting: boolean) {
62 const [viewportState, setViewportState] = useState<PresentModeViewportState>(
63 () => getPresentModeViewportState(),
64 );
65
66 useEffect(() => {
67 if (!isPresenting) {
68 setViewportState(getPresentModeViewportState());
69 return;
70 }
71
72 const updateViewportState = () => {
73 setViewportState(getPresentModeViewportState());
74 };
75
76 updateViewportState();
77 window.addEventListener("resize", updateViewportState, { passive: true });
78 window.addEventListener("orientationchange", updateViewportState);
79 window.visualViewport?.addEventListener("resize", updateViewportState);
80
81 return () => {
82 window.removeEventListener("resize", updateViewportState);
83 window.removeEventListener("orientationchange", updateViewportState);
84 window.visualViewport?.removeEventListener("resize", updateViewportState);
85 };
86 }, [isPresenting]);
87
88 return viewportState;
89 }
90
90 lines TYPESCRIPT