175 lines
5.5 KiB
JavaScript
175 lines
5.5 KiB
JavaScript
const H5_STATUS_BAR_QUERY_KEYS = ['statusBarHeight', 'safeTop', 'safeAreaTop', 'appStatusBarHeight'];
|
|
const H5_MIN_STATUS_BAR_HEIGHT = 22;
|
|
const H5_MAX_STATUS_BAR_HEIGHT = 200;
|
|
let currentStatusBarHeight = H5_MIN_STATUS_BAR_HEIGHT;
|
|
let statusBarStyleObserver = null;
|
|
|
|
// 将 URL 参数、宿主全局变量和 postMessage 消息中的高度统一转换为数字。
|
|
function normalizeStatusBarHeight(value) {
|
|
if (value === undefined || value === null || value === '') {
|
|
return null;
|
|
}
|
|
const normalizedValue = typeof value === 'string' ? value.replace(/px$/i, '').trim() : value;
|
|
const height = Number.parseFloat(normalizedValue);
|
|
if (!Number.isFinite(height)) {
|
|
return null;
|
|
}
|
|
return Math.max(0, Math.min(height, H5_MAX_STATUS_BAR_HEIGHT));
|
|
}
|
|
|
|
// 宿主未提供高度或提供的高度过小时,统一使用 H5 最小状态栏高度。
|
|
function resolveStatusBarHeight(value) {
|
|
const height = normalizeStatusBarHeight(value);
|
|
return height === null ? H5_MIN_STATUS_BAR_HEIGHT : Math.max(height, H5_MIN_STATUS_BAR_HEIGHT);
|
|
}
|
|
|
|
function parseMessageData(data) {
|
|
if (!data) {
|
|
return null;
|
|
}
|
|
if (typeof data === 'string') {
|
|
try {
|
|
return JSON.parse(data);
|
|
} catch (error) {
|
|
return null;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
// URL 参数优先级最高,同时兼容普通查询参数和 hash 查询参数。
|
|
function readStatusBarHeightFromQuery() {
|
|
const searchParamsList = [];
|
|
searchParamsList.push(new URLSearchParams(window.location.search));
|
|
const hashQuery = window.location.hash.includes('?') ? window.location.hash.split('?')[1] : '';
|
|
if (hashQuery) {
|
|
searchParamsList.push(new URLSearchParams(hashQuery));
|
|
}
|
|
|
|
for (const params of searchParamsList) {
|
|
for (const key of H5_STATUS_BAR_QUERY_KEYS) {
|
|
const height = normalizeStatusBarHeight(params.get(key));
|
|
if (height !== null) {
|
|
return height;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// 宿主可以在 H5 应用初始化前将状态栏高度注入 window。
|
|
function readStatusBarHeightFromWindow() {
|
|
const candidates = [
|
|
window.__APP_STATUS_BAR_HEIGHT__,
|
|
window.__UNI_STATUS_BAR_HEIGHT__,
|
|
window.__APP_SHELL_CONFIG__?.statusBarHeight,
|
|
window.__UNI_WEBVIEW_CONFIG__?.statusBarHeight,
|
|
];
|
|
for (const candidate of candidates) {
|
|
const height = normalizeStatusBarHeight(candidate);
|
|
if (height !== null) {
|
|
return height;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// 读取页面当前实际使用的 --status-bar-height。
|
|
export function getH5StatusBarHeight() {
|
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
return 0;
|
|
}
|
|
const statusBarHeight = window
|
|
.getComputedStyle(document.documentElement)
|
|
.getPropertyValue('--status-bar-height')
|
|
.replace(/px$/i, '')
|
|
.trim();
|
|
return resolveStatusBarHeight(statusBarHeight);
|
|
}
|
|
|
|
// 依次从 URL、宿主全局变量中获取初始高度,均未提供时使用默认最小高度。
|
|
export function getH5HostStatusBarHeight() {
|
|
if (typeof window === 'undefined') {
|
|
return 0;
|
|
}
|
|
return resolveStatusBarHeight(readStatusBarHeightFromQuery() ?? readStatusBarHeightFromWindow());
|
|
}
|
|
|
|
// 只在 html 上设置一个状态栏变量,body 和其他子元素会自动继承。
|
|
export function applyH5HostStatusBarHeight(height) {
|
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
return 0;
|
|
}
|
|
const statusBarHeight = resolveStatusBarHeight(height);
|
|
currentStatusBarHeight = statusBarHeight;
|
|
document.documentElement.style.setProperty('--status-bar-height', `${statusBarHeight}px`, 'important');
|
|
// 通知使用数值高度参与 JavaScript 布局计算的页面重新计算。
|
|
window.dispatchEvent(
|
|
new CustomEvent('app-shell-status-bar-change', {
|
|
detail: { height: statusBarHeight },
|
|
}),
|
|
);
|
|
|
|
return statusBarHeight;
|
|
}
|
|
|
|
// uni-app 重写 html 样式后,如果状态栏高度小于 22px,则立即恢复当前有效高度。
|
|
function observeStatusBarHeight() {
|
|
if (statusBarStyleObserver || typeof MutationObserver === 'undefined') {
|
|
return;
|
|
}
|
|
statusBarStyleObserver = new MutationObserver(() => {
|
|
const statusBarHeight = normalizeStatusBarHeight(
|
|
document.documentElement.style.getPropertyValue('--status-bar-height'),
|
|
);
|
|
if (statusBarHeight === null || statusBarHeight < H5_MIN_STATUS_BAR_HEIGHT) {
|
|
applyH5HostStatusBarHeight(currentStatusBarHeight);
|
|
}
|
|
});
|
|
statusBarStyleObserver.observe(document.documentElement, {
|
|
attributes: true,
|
|
attributeFilter: ['style'],
|
|
});
|
|
}
|
|
|
|
// 解析宿主运行期间通过 postMessage 发送的状态栏高度。
|
|
function resolveMessageStatusBarHeight(message) {
|
|
const data = parseMessageData(message?.data);
|
|
if (!data) {
|
|
return null;
|
|
}
|
|
|
|
return normalizeStatusBarHeight(
|
|
data.statusBarHeight ??
|
|
data.safeTop ??
|
|
data.safeAreaTop ??
|
|
data.payload?.statusBarHeight ??
|
|
data.payload?.safeTop,
|
|
);
|
|
}
|
|
|
|
// 初始化状态栏高度,并支持宿主通过全局方法或 postMessage 动态更新。
|
|
export function initH5HostShell() {
|
|
if (typeof window === 'undefined') {
|
|
return 0;
|
|
}
|
|
|
|
const safeTop = applyH5HostStatusBarHeight(getH5HostStatusBarHeight());
|
|
if (window.__FRIENDSHIP_H5_SHELL_READY__) {
|
|
return safeTop;
|
|
}
|
|
|
|
window.__FRIENDSHIP_H5_SHELL_READY__ = true;
|
|
observeStatusBarHeight();
|
|
window.setAppShellStatusBarHeight = (nextHeight) => applyH5HostStatusBarHeight(nextHeight);
|
|
window.addEventListener('message', (message) => {
|
|
const nextHeight = resolveMessageStatusBarHeight(message);
|
|
if (nextHeight !== null) {
|
|
applyH5HostStatusBarHeight(nextHeight);
|
|
}
|
|
});
|
|
|
|
return safeTop;
|
|
}
|