miniprogramme/utils/screen-adapter.js

354 lines
9.1 KiB
JavaScript
Raw Normal View History

2025-09-12 16:08:17 +08:00
// 屏幕适配工具类 - 解决小程序滚动条问题
// 基于2024年最新的微信小程序最佳实践
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;
console.log('屏幕适配器初始化成功:', this.adaptationInfo);
return this.adaptationInfo;
} catch (error) {
console.error('屏幕适配器初始化失败:', error);
// 使用兜底方案
this.fallbackInit();
return this.adaptationInfo;
}
}
// 获取窗口信息新API
getWindowInfo() {
return new Promise((resolve) => {
try {
const windowInfo = wx.getWindowInfo();
resolve(windowInfo);
} catch (error) {
console.warn('getWindowInfo失败使用兜底方案:', error);
resolve({
windowWidth: 375,
windowHeight: 667,
pixelRatio: 2,
safeArea: {
top: 44,
bottom: 0,
left: 0,
right: 375
}
});
}
});
}
// 获取设备信息新API
getDeviceInfo() {
return new Promise((resolve) => {
try {
const deviceInfo = wx.getDeviceInfo();
resolve(deviceInfo);
} catch (error) {
console.warn('getDeviceInfo失败使用兜底方案:', error);
resolve({
platform: 'unknown',
system: 'unknown',
model: 'unknown'
});
}
});
}
// 获取应用基础信息新API
getAppBaseInfo() {
return new Promise((resolve) => {
try {
const appBaseInfo = wx.getAppBaseInfo();
resolve(appBaseInfo);
} catch (error) {
console.warn('getAppBaseInfo失败使用兜底方案:', error);
resolve({
version: '1.0.0',
language: 'zh_CN'
});
}
});
}
// 兜底初始化
fallbackInit() {
try {
this.systemInfo = wx.getSystemInfoSync();
this.calculateAdaptation();
this.isInitialized = true;
} catch (error) {
console.error('兜底初始化也失败,使用硬编码默认值:', error);
this.useDefaultValues();
}
}
// 计算适配信息
calculateAdaptation() {
const { windowWidth, windowHeight, safeArea, pixelRatio = 2 } = this.systemInfo;
// 计算安全区域
const statusBarHeight = safeArea ? safeArea.top : 44;
const safeAreaBottom = safeArea ? (windowHeight - safeArea.bottom) : 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 navBarHeight = statusBarHeight + menuButtonHeight + menuButtonTop * 2;
// 计算可用高度(解决滚动条问题的关键)
const usableHeight = windowHeight;
const contentHeight = usableHeight - statusBarHeight - safeAreaBottom;
// 设备类型判断
const deviceType = this.detectDeviceType(windowWidth, windowHeight);
this.adaptationInfo = {
// 基础信息
windowWidth,
windowHeight,
pixelRatio,
// 安全区域
statusBarHeight,
safeAreaBottom,
safeAreaTop: statusBarHeight,
safeAreaLeft: safeArea ? safeArea.left : 0,
safeAreaRight: safeArea ? (windowWidth - safeArea.right) : 0,
// 导航栏信息
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 Pro Max系列
else if (width <= 428 && 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;
}
const info = this.getAdaptationInfo();
// 🔥 设置页面数据 - 使用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
}));
// 应用CSS变量到页面
this.applyCSSVars();
console.log('页面适配应用成功:', pageInstance.route || 'unknown');
}
// 应用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) => {
console.log('窗口大小变化:', 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();
// 导出实例和类
module.exports = {
ScreenAdapter,
screenAdapter,
// 便捷方法
async init() {
return await screenAdapter.init();
},
getAdaptationInfo() {
return screenAdapter.getAdaptationInfo();
},
applyToPage(pageInstance) {
return screenAdapter.applyToPage(pageInstance);
},
onWindowResize(callback) {
return screenAdapter.onWindowResize(callback);
},
getPageConfig() {
return screenAdapter.getPageConfig();
}
};