UNPKG

press-pix

Version:
276 lines (245 loc) 7.13 kB
import { AegisReportInPixui } from 'pixui-runtime'; import { safeJsonParse } from 't-comm/es/json/json-parse'; import { GameletAPI } from 'gamelet-pixui-frame'; import { checkI18n } from '../i18n/check'; /** * 最小安全边距(单位:px) * 当从客户端获取的边距小于此值时,使用此值作为最小保证 */ const ORIGIN_PADDING = 44; /** * px 转 rem 的转换比例 * 计算公式:rem = px * PX_TO_REM_RATIO */ const PX_TO_REM_RATIO = 2 / 100; const originData = { left: 0, top: 0, right: 0, bottom: 0, }; const safeDistance = { data: originData, isReady: false, // 标记数据是否已经获取完成 readyPromise: null as Promise<void> | null, // 用于等待数据准备完成 resolveReady: null as (() => void) | null, // resolve 函数 listeners: [] as Array<() => void>, // 数据变化监听器列表 get() { return this.data; }, set(data) { this.data = data; this.isReady = true; if (this.resolveReady) { this.resolveReady(); } // 通知所有监听器数据已更新 this.listeners.forEach(listener => listener()); }, // 订阅数据变化 subscribe(listener: () => void) { this.listeners.push(listener); // 返回取消订阅函数 return () => { const index = this.listeners.indexOf(listener); if (index > -1) { this.listeners.splice(index, 1); } }; }, }; export async function tryGetSafeAreaInfo() { // 如果已经初始化过,直接返回 if (safeDistance.isReady) { return; } // 创建一个 Promise 用于等待数据返回 if (!safeDistance.readyPromise) { safeDistance.readyPromise = new Promise((resolve) => { safeDistance.resolveReady = resolve; }); } GameletAPI.addOnGameCommandListener((msg) => { setTimeout(() => { AegisReportInPixui.report({ msg: 'GAME_COMMAND_0', params: { msg, }, }); }, 3000); const ret = safeJsonParse(msg); setTimeout(() => { AegisReportInPixui.report({ msg: 'GAME_COMMAND_1', params: { msg, ret, }, }); }, 3000); if (ret && ret.type === 'GetMarginRtn') { const margin = (ret.content || '').split(',').map(val => parseInt(val, 10)); setTimeout(() => { AegisReportInPixui.report({ msg: 'GAME_COMMAND_SAFE_AREA_INFO', params: { msg, ret, margin, }, }); }, 3000); safeDistance.set({ left: margin[0], top: margin[1], right: margin[2], bottom: margin[3], }); } }); GameletAPI.callGame(JSON.stringify({ type: 'GetMargin', appid: GameletAPI.getAppID(), appId: GameletAPI.getAppID(), appName: GameletAPI.getAppName(), })); // 等待数据返回,最多等待 3 秒 await Promise.race([ safeDistance.readyPromise, new Promise(resolve => setTimeout(resolve, 3000)), ]); } // 模块加载时自动初始化 Safe Area 数据 // 这样所有页面导入时,数据获取就已经在后台进行了 tryGetSafeAreaInfo().catch((err) => { console.error('Failed to initialize Safe Area:', err); }); /** * 订阅 Safe Area 数据变化 * 当数据更新时会调用回调函数 * * @param callback - 数据变化时的回调函数 * @returns 取消订阅的函数 * * @example * const unsubscribe = subscribeSafeAreaChange(() => { * console.log('Safe Area data updated'); * }); * // 取消订阅 * unsubscribe(); */ export function subscribeSafeAreaChange(callback: () => void) { return safeDistance.subscribe(callback); } /** * 检查 Safe Area 数据是否已准备好 * * @returns 数据是否已准备好 */ export function isSafeAreaReady() { return safeDistance.isReady; } /** * px 转 rem * @param px - 像素值 * @returns rem 字符串(如 "1rem") */ function pxToRem(px: number): string { return `${(px * PX_TO_REM_RATIO).toFixed(2)}rem`; } /** * 计算实际的 padding 值(确保不小于最小安全边距) * @param left - 左边距原始值 * @param right - 右边距原始值 * @returns 计算后的左右 padding 值 */ function calculatePadding(left: number, right: number) { return { paddingLeft: Math.max(left, ORIGIN_PADDING), paddingRight: Math.max(right, ORIGIN_PADDING), }; } /** * 检查当前语言是否为 RTL(从右到左) */ export function isRTLLanguage(): boolean { const { lang } = checkI18n(); return lang === 'ur' || lang === 'ar'; } /** * 获取 safe area 左右 padding 样式,返回可直接用于 style 属性的对象 * import { getSafeAreaStyle } from 'src/local-logic/utils/safe-area'; * * @param options - 可选配置 * @param options.left - 是否包含 paddingLeft,默认 true * @param options.right - 是否包含 paddingRight,默认 true * @param options.position - 定位方式,'left' 或 'right' * * @example * // 直接用于 style 属性(左右都设置) * <div style={getSafeAreaStyle()}>...</div> * * // 只设置左边距 * <div style={getSafeAreaStyle({ right: false })}>...</div> * * // 只设置右边距 * <div style={getSafeAreaStyle({ left: false })}>...</div> * * // 设置定位 left * <div style={getSafeAreaStyle({ position: 'left' })}>...</div> * * // 设置定位 right * <div style={getSafeAreaStyle({ position: 'right' })}>...</div> * * // 或者与其他样式合并 * <div style={{ ...getSafeAreaStyle(), backgroundColor: 'red' }}>...</div> */ export function getSafeAreaStyle(options?: { left?: boolean; right?: boolean; position?: 'left' | 'right' }) { const { left, right } = safeDistance.data; const { paddingLeft, paddingRight } = calculatePadding(left, right); let result: Record<string, string> = {}; // 转换为 rem const leftRem = pxToRem(paddingLeft); const rightRem = pxToRem(paddingRight); // 如果指定了 position,只返回对应方位的定位值 if (options?.position === 'left') { result = { left: leftRem }; } else if (options?.position === 'right') { result = { right: rightRem }; } else { // 没有指定 position,按照 left/right 选项设置 padding const includeLeft = options?.left !== false; const includeRight = options?.right !== false; if (includeLeft) { result = { ...result, paddingLeft: leftRem }; } if (includeRight) { result = { ...result, paddingRight: rightRem }; } } return result; } /** * 获取 safe area 原始数据和处理后的 padding 值 * import { getSafeAreaInfo } from 'src/local-logic/utils/safe-area'; * * @example * const safeArea = getSafeAreaInfo(); * // 返回值包含: * // { left, top, right, bottom, paddingLeft, paddingTop, paddingRight, paddingBottom } */ export function getSafeAreaInfo() { const { left, top, right, bottom } = safeDistance.data; const { paddingLeft, paddingRight } = calculatePadding(left, right); return { left, top, right, bottom, paddingLeft, paddingTop: top, paddingRight, paddingBottom: bottom, }; }