Ref의 700px 자동 판정과 940px 레이아웃 경계를 연결하고 태블릿에서 1000px 모드를 선택한다. 검색 입력에는 pinch zoom을 보존하는 touch-action 계약을 적용한다.
79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
export const SCREEN_MODE_KEY = 'sam.screenMode';
|
|
export const SCREEN_MODE_CHANGE_EVENT = 'tryChangeScreenMode';
|
|
|
|
export type ScreenMode = 'auto' | '500px' | '1000px';
|
|
|
|
export type AutoViewportMeasurements = {
|
|
deviceWidth: number;
|
|
viewportHeight: number;
|
|
targetHeight?: number;
|
|
};
|
|
|
|
export const normalizeScreenMode = (value: string | null): ScreenMode =>
|
|
value === '500px' || value === '1000px' ? value : 'auto';
|
|
|
|
export const resolveAutoViewportContent = ({
|
|
deviceWidth,
|
|
viewportHeight,
|
|
targetHeight = 700,
|
|
}: AutoViewportMeasurements): string => {
|
|
if (deviceWidth < 500) {
|
|
return 'width=500';
|
|
}
|
|
|
|
if (viewportHeight < targetHeight) {
|
|
const widthAtTargetHeight = (deviceWidth / viewportHeight) * targetHeight;
|
|
return widthAtTargetHeight >= 700 ? 'width=1000' : `height=${Math.ceil(targetHeight)}`;
|
|
}
|
|
|
|
return deviceWidth >= 700 ? 'width=1000' : 'width=device-width, initial-scale=1';
|
|
};
|
|
|
|
export const resolveViewportContent = (mode: ScreenMode, measurements: AutoViewportMeasurements): string => {
|
|
if (mode === '500px') return 'width=500';
|
|
if (mode === '1000px') return 'width=1000';
|
|
return resolveAutoViewportContent(measurements);
|
|
};
|
|
|
|
const findOrCreateViewportMeta = (): HTMLMetaElement => {
|
|
const existing = document.querySelector<HTMLMetaElement>('meta[name="viewport"]');
|
|
if (existing) return existing;
|
|
|
|
const viewportMeta = document.createElement('meta');
|
|
viewportMeta.name = 'viewport';
|
|
document.head.appendChild(viewportMeta);
|
|
return viewportMeta;
|
|
};
|
|
|
|
export const installScreenModeViewport = (targetHeight = 700): void => {
|
|
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
|
|
|
const viewportMeta = findOrCreateViewportMeta();
|
|
let previousMode: ScreenMode | null = null;
|
|
let previousDeviceWidth: number | null = null;
|
|
|
|
const adjustViewport = () => {
|
|
const mode = normalizeScreenMode(window.localStorage.getItem(SCREEN_MODE_KEY));
|
|
const deviceWidth = window.screen.availWidth;
|
|
|
|
if (mode === previousMode && mode === 'auto' && deviceWidth === previousDeviceWidth) return;
|
|
if (mode === previousMode && mode !== 'auto') return;
|
|
|
|
previousMode = mode;
|
|
previousDeviceWidth = deviceWidth;
|
|
viewportMeta.content = resolveViewportContent(mode, {
|
|
deviceWidth,
|
|
viewportHeight: window.innerHeight,
|
|
targetHeight,
|
|
});
|
|
};
|
|
|
|
adjustViewport();
|
|
window.addEventListener('resize', adjustViewport);
|
|
window.addEventListener('orientationchange', adjustViewport);
|
|
window.addEventListener('storage', (event) => {
|
|
if (event.key === SCREEN_MODE_KEY) adjustViewport();
|
|
});
|
|
document.addEventListener(SCREEN_MODE_CHANGE_EVENT, adjustViewport);
|
|
};
|