Lock phone UI to portrait with PWA and landscape gate.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
/** 手机端尽量锁定竖屏;浏览器页用遮罩提示,已安装 PWA 由 manifest 约束。 */
|
||||
|
||||
type OrientationLockType =
|
||||
| "any"
|
||||
| "natural"
|
||||
| "landscape"
|
||||
| "portrait"
|
||||
| "portrait-primary"
|
||||
| "portrait-secondary"
|
||||
| "landscape-primary"
|
||||
| "landscape-secondary";
|
||||
|
||||
function isPhoneLike(): boolean {
|
||||
if (typeof window === "undefined") return false;
|
||||
const coarse = window.matchMedia("(pointer: coarse)").matches;
|
||||
const narrow = Math.min(window.screen.width, window.screen.height) <= 520;
|
||||
const ua = navigator.userAgent || "";
|
||||
const phoneUa = /Android.+Mobile|iPhone|iPod|Windows Phone/i.test(ua);
|
||||
return phoneUa || (coarse && narrow);
|
||||
}
|
||||
|
||||
async function tryLockPortrait(): Promise<void> {
|
||||
const orient = screen.orientation as ScreenOrientation & {
|
||||
lock?: (orientation: OrientationLockType) => Promise<void>;
|
||||
};
|
||||
if (!orient?.lock) return;
|
||||
try {
|
||||
await orient.lock("portrait");
|
||||
} catch {
|
||||
try {
|
||||
await orient.lock("portrait-primary");
|
||||
} catch {
|
||||
/* 多数浏览器仅全屏/PWA 允许 lock,失败则靠遮罩 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ensureGate(): HTMLElement {
|
||||
let el = document.getElementById("portrait-lock-gate");
|
||||
if (el) return el;
|
||||
el = document.createElement("div");
|
||||
el.id = "portrait-lock-gate";
|
||||
el.className = "portrait-lock-gate";
|
||||
el.setAttribute("aria-live", "polite");
|
||||
el.innerHTML =
|
||||
'<div class="portrait-lock-card">' +
|
||||
'<p class="portrait-lock-title">请竖屏使用</p>' +
|
||||
'<p class="portrait-lock-sub">手机端仅支持竖屏浏览</p>' +
|
||||
"</div>";
|
||||
document.body.appendChild(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function syncGate(): void {
|
||||
if (!isPhoneLike()) {
|
||||
const el = document.getElementById("portrait-lock-gate");
|
||||
if (el) el.classList.remove("show");
|
||||
return;
|
||||
}
|
||||
const landscape = window.matchMedia("(orientation: landscape)").matches;
|
||||
ensureGate().classList.toggle("show", landscape);
|
||||
if (!landscape) void tryLockPortrait();
|
||||
}
|
||||
|
||||
/** 注册竖屏锁定与横屏提示遮罩。 */
|
||||
export function initPortraitLock(): void {
|
||||
if (typeof window === "undefined") return;
|
||||
syncGate();
|
||||
window.addEventListener("orientationchange", syncGate);
|
||||
window.addEventListener("resize", syncGate);
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") syncGate();
|
||||
});
|
||||
void tryLockPortrait();
|
||||
}
|
||||
Reference in New Issue
Block a user