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