findme-miniprogram-frontend/utils/screen-adapter.js
2025-12-27 17:16:03 +08:00

369 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//屏幕适配工具类 -解决小程序滚动条问题
class ScreenAdapter {
constructor() {
this.systemInfo = null; // 存储设备系统信息(如屏幕尺寸,像素比等)
this.isInitialized = false; // 标记适配器是否已经初始化
this.deviceInfo = {}; // 存储设备详细信息(如信号,系统版本等)
this.adaptationInfo = {} // 存储计算后的适配参数(如滚动区域高度,导航栏高度)
}
// 初始化适配器
async init() {
if (this.isInitialized) {
return this.adaptationInfo;
}
try {
// 使用新的API 获取系统信息
const [windowInfo, deviceInfo, appBaseInfo] = await Promise.all([
this.getWindowInfo(),
this.getDeviceInfo(),
this.getAppBaseInfo()
]);
// 合并系统信息
this.systemInfo = {
...windowInfo,
...deviceInfo,
...appBaseInfo
}
//计算适配信息
this.calculateAdaptation();
this.isInitialized = true;
return this.adaptationInfo;
} catch (error) {
this.fallbackInit();
return this.adaptationInfo;
}finally{
}
}
// 获取窗口信息新API
getWindowInfo() {
return new Promise((resolve) => {
try {
const windowInfo = wx.getWindowInfo();
resolve(windowInfo)
} catch (error) {
// 同步失败用异步
wx.getWindowInfo({
success: (res) => resolve(res),
fail: () => {
// 失败返回默认值
resolve({
windowWidth: 375,
windowHeight: 667,
pixelRatio: 2,
statusBarHeight: 20,
platform: 'android',
system: 'Android 16',
model: 'Unknown Device',
brand: 'Unknown',
safeArea: {
top: 20,
bottom: 667,
left: 0,
right: 375
}
});
}
});
}
})
}
// 获取设备信息
getDeviceInfo() {
return new Promise((resolve) => {
try {
const deviceInfo = wx.getDeviceInfo()
resolve(deviceInfo)
} catch (error) {
resolve(
{
platform: 'unknown',
system: 'unknown',
model: 'unknown'
}
)
}
})
}
// 获取应用基础信息
getAppBaseInfo() {
return new Promise((resolve) => {
try {
const appBaseInfo = wx.getAppBaseInfo()
resolve(appBaseInfo)
} catch (error) {
resolve({
version: '1.0.0',
language: 'zh_CN'
})
}
})
}
// 兜底初始化
fallbackInit() {
try {
// this.systemInfo = wx.getWindowInfo()
this.systemInfo = this.getWindowInfo()
this.calculateAdaptation()
this.isInitialized = true
} catch (error) {
this.useDefaultValues();
}
}
// 计算适配信息
calculateAdaptation() {
const { windowWidth, screenHeight,windowHeight, screenTop,safeArea, model, pixelRatio = 2 } = this.systemInfo;
// 计算安全区域
const statusBarHeight = safeArea ? safeArea.top : 44;
const safeAreaBottom = safeArea ? ((windowHeight > safeArea.bottom) ? windowHeight - safeArea.bottom : 0) : 0;
// 计算胶囊按钮信息(兜底处理)
let menuButtonInfo = { height: 32, top: 6, width: 87, right: 281 };
try {
menuButtonInfo = wx.getMenuButtonBoundingClientRect();
} catch (error) {
console.warn('获取胶囊按钮信息失败,使用默认值 :', error);
}
const menuButtonHeight = menuButtonInfo.height;
const menuButtonTop = menuButtonInfo.top - statusBarHeight;
const targetModels = ['windows', 'huawei', 'nexus', 'mac'];
const lowerModel = model.toLocaleLowerCase();
const isTargetModel = targetModels.some(keyword => lowerModel.includes(keyword));
const navBarHeight = isTargetModel ? (menuButtonInfo.bottom + menuButtonTop):(menuButtonInfo.bottom + menuButtonTop*2);
// 计算可用高度(解决滚动条问题的关键)
const usableHeight = windowHeight;
const contentHeight = usableHeight - statusBarHeight - safeAreaBottom;
// 设备类型判断
const deviceType = this.detectDeviceType(windowWidth, windowHeight);
this.adaptationInfo = {
// 基础信息
windowWidth,
windowHeight: deviceType.toLocaleLowerCase().includes('iPad') ? windowHeight : contentHeight,
pixelRatio,
screenHeight,
// 安全区域
statusBarHeight,
safeAreaBottom,
safeAreaTop: statusBarHeight,
// safeAreaLeft: safeArea ? safeArea.left : 0,
// safeAreaRight: safeArea ? (windowWidth - safeArea.right) : 0,
// safeTop: model === 'Windows' ? navBarHeight : 0,
screenTop,
// centerOffset: windowWidth <= 430 ? 0 : (windowWidth - 430) / 2,
// 导航栏信息
navBarHeight,
menuButtonHeight,
menuButtonTop,
menuButtonInfo,
// 可用区域(解决滚动条的关键)
usableHeight,
contentHeight,
// 设备信息
deviceType,
platform: this.systemInfo.platform,
// CSS变量用于动态设置样式
cssVars: {
'--window-height': windowHeight + 'px',
'--window-width': windowWidth + 'px',
'--status-bar-height': statusBarHeight + 'px',
'--nav-bar-height': navBarHeight + 'px',
'--safe-area-bottom': safeAreaBottom + 'px',
'--content-height': contentHeight + 'px',
'--usable-height': usableHeight + 'px'
}
};
}
// 检测设备类型
detectDeviceType(width, height) {
// iPhone SE系列
if (width <= 375 && height <= 667) {
return 'iphone-se';
}
// iPhone 12/13/14系列
else if (width <= 390 && height >= 844) {
return 'iphone-standard';
}
// iPhone 12/13/14/15 Pro Max系列
else if (width <= 430 && height >= 926) {
return 'iphone-max';
}
// iPad系列
else if (width >= 768) {
return 'ipad';
}
// Android大屏
else if (height >= 800) {
return 'android-large';
}
// 其他设备
else {
return 'unknown';
}
}
// 使用默认值
useDefaultValues() {
this.adaptationInfo = {
windowWidth: 375,
windowHeight: 667,
pixelRatio: 2,
statusBarHeight: 44,
safeAreaBottom: 0,
safeAreaTop: 44,
safeAreaLeft: 0,
safeAreaRight: 0,
navBarHeight: 88,
menuButtonHeight: 32,
menuButtonTop: 6,
usableHeight: 667,
contentHeight: 623,
deviceType: 'unknown',
platform: 'unknown',
cssVars: {
'--window-height': '667px',
'--window-width': '375px',
'--status-bar-height': '44px',
'--nav-bar-height': '88px',
'--safe-area-bottom': '0px',
'--content-height': '623px',
'--usable-height': '667px'
}
};
this.isInitialized = true;
}
// 获取适配信息
getAdaptationInfo() {
if (!this.isInitialized) {
console.warn('屏幕适配器未初始化,返回默认值');
this.useDefaultValues();
}
return this.adaptationInfo;
}
// 为页面设置适配样式(解决滚动条的核心方法)
applyToPage(pageInstance) {
if (!pageInstance) {
console.error('页面实例为空');
return;
}
try {
const info = this.getAdaptationInfo();
console.log('应用适配信息到页面:', info);
// 🔥 设置页面数据 - 使用Object.assign替代扩展运算符
pageInstance.setData(Object.assign({}, info, {
// 兼容旧版本的字段名
windowHeight: info.windowHeight,
windowWidth: info.windowWidth,
statusBarHeight: info.statusBarHeight,
navBarHeight: info.navBarHeight,
safeAreaBottom: info.safeAreaBottom,
menuButtonHeight: info.menuButtonHeight,
menuButtonTop: info.menuButtonTop
}));
} catch (error) {
console.error('应用适配信息到页面失败:', error);
// 不抛出错误,避免影响页面加载
}
}
// 应用CSS变量
applyCSSVars() {
if (!this.adaptationInfo.cssVars) return;
try {
// 在小程序中,我们通过设置页面数据来传递这些值
// CSS变量将通过页面数据在WXML中使用
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
if (currentPage) {
currentPage.setData({
screenCSSVars: this.adaptationInfo.cssVars
});
}
} catch (error) {
console.error('应用CSS变量失败:', error);
}
}
// 监听窗口大小变化(横竖屏切换等)
onWindowResize(callback) {
wx.onWindowResize((res) => {
// 重新计算适配信息
this.systemInfo.windowWidth = res.size.windowWidth;
this.systemInfo.windowHeight = res.size.windowHeight;
this.calculateAdaptation();
if (callback && typeof callback === 'function') {
callback(this.adaptationInfo);
}
});
}
// 获取推荐的页面配置
getPageConfig() {
return {
// 禁用页面滚动,防止出现滚动条
disableScroll: true,
// 背景色
backgroundColor: '#f8f9fa',
// 导航栏样式
navigationBarBackgroundColor: '#667eea',
navigationBarTextStyle: 'white'
};
}
}
// 创建全局实例
const screenAdapter = new ScreenAdapter();
// 便捷方法
async function init(self) {
try {
await screenAdapter.init();
screenAdapter.applyToPage(self);
// onWindowResize 是监听器,不需要 await
screenAdapter.onWindowResize(() => {
try {
screenAdapter.applyToPage(self);
} catch (error) {
console.error('窗口大小变化时应用适配信息失败:', error);
}
});
} catch (error) {
console.error('初始化屏幕信息出错了', error);
// 即使初始化失败,也尝试应用默认值
try {
screenAdapter.applyToPage(self);
} catch (e) {
console.error('应用默认适配信息失败:', e);
}
}
}
// 导出实例和类
module.exports = {
init
};