miniprogramme/utils/screen-adapter.js
2025-09-12 16:08:17 +08:00

354 lines
No EOL
9.1 KiB
JavaScript
Raw 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.

// 屏幕适配工具类 - 解决小程序滚动条问题
// 基于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();
}
};