516 lines
17 KiB
JavaScript
516 lines
17 KiB
JavaScript
|
|
// 移动端优化组件
|
|||
|
|
(function() {
|
|||
|
|
let isMobile = false;
|
|||
|
|
let isTablet = false;
|
|||
|
|
let currentOrientation = 'portrait';
|
|||
|
|
|
|||
|
|
// 检测设备类型
|
|||
|
|
function detectDevice() {
|
|||
|
|
const userAgent = navigator.userAgent.toLowerCase();
|
|||
|
|
const screenWidth = window.innerWidth;
|
|||
|
|
const screenHeight = window.innerHeight;
|
|||
|
|
|
|||
|
|
// 检测是否为移动设备
|
|||
|
|
isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) || screenWidth <= 768;
|
|||
|
|
isTablet = /ipad|android(?!.*mobile)|tablet/i.test(userAgent) || (screenWidth > 768 && screenWidth <= 1024);
|
|||
|
|
|
|||
|
|
// 检测屏幕方向
|
|||
|
|
currentOrientation = screenWidth > screenHeight ? 'landscape' : 'portrait';
|
|||
|
|
|
|||
|
|
// 添加设备类到body(确保body存在)
|
|||
|
|
if (document.body) {
|
|||
|
|
document.body.classList.remove('mobile', 'tablet', 'desktop', 'portrait', 'landscape');
|
|||
|
|
document.body.classList.add(
|
|||
|
|
isMobile ? 'mobile' : isTablet ? 'tablet' : 'desktop',
|
|||
|
|
currentOrientation
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { isMobile, isTablet, currentOrientation };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加移动端优化样式
|
|||
|
|
function addMobileStyles() {
|
|||
|
|
const style = document.createElement('style');
|
|||
|
|
style.textContent = `
|
|||
|
|
/* 移动端基础优化 */
|
|||
|
|
* {
|
|||
|
|
-webkit-tap-highlight-color: transparent;
|
|||
|
|
-webkit-touch-callout: none;
|
|||
|
|
-webkit-user-select: none;
|
|||
|
|
-moz-user-select: none;
|
|||
|
|
-ms-user-select: none;
|
|||
|
|
user-select: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 允许文本选择 */
|
|||
|
|
p, h1, h2, h3, h4, h5, h6, span, div, article, section {
|
|||
|
|
-webkit-user-select: text;
|
|||
|
|
-moz-user-select: text;
|
|||
|
|
-ms-user-select: text;
|
|||
|
|
user-select: text;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端滚动优化 */
|
|||
|
|
html {
|
|||
|
|
-webkit-overflow-scrolling: touch;
|
|||
|
|
scroll-behavior: smooth;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端字体优化 */
|
|||
|
|
body.mobile {
|
|||
|
|
font-size: 16px; /* 防止iOS缩放 */
|
|||
|
|
line-height: 1.6;
|
|||
|
|
-webkit-text-size-adjust: 100%;
|
|||
|
|
-moz-text-size-adjust: 100%;
|
|||
|
|
-ms-text-size-adjust: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端容器优化 */
|
|||
|
|
.container {
|
|||
|
|
padding-left: 1rem;
|
|||
|
|
padding-right: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile .container {
|
|||
|
|
padding-left: 1rem;
|
|||
|
|
padding-right: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.tablet .container {
|
|||
|
|
padding-left: 2rem;
|
|||
|
|
padding-right: 2rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端导航优化 */
|
|||
|
|
body.mobile #navbar {
|
|||
|
|
padding: 0.75rem 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile .nav-container {
|
|||
|
|
padding: 0.75rem 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端按钮优化 */
|
|||
|
|
body.mobile button,
|
|||
|
|
body.mobile .btn {
|
|||
|
|
min-height: 44px; /* iOS推荐的最小触摸目标 */
|
|||
|
|
padding: 0.75rem 1.5rem;
|
|||
|
|
font-size: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端表单优化 */
|
|||
|
|
body.mobile input,
|
|||
|
|
body.mobile textarea,
|
|||
|
|
body.mobile select {
|
|||
|
|
min-height: 44px;
|
|||
|
|
padding: 0.75rem;
|
|||
|
|
font-size: 16px; /* 防止iOS缩放 */
|
|||
|
|
border-radius: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端卡片优化 */
|
|||
|
|
body.mobile .card,
|
|||
|
|
body.mobile .glass-card {
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
padding: 1rem;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端文字优化 */
|
|||
|
|
body.mobile h1 {
|
|||
|
|
font-size: 2rem;
|
|||
|
|
line-height: 1.2;
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile h2 {
|
|||
|
|
font-size: 1.75rem;
|
|||
|
|
line-height: 1.3;
|
|||
|
|
margin-bottom: 0.75rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile h3 {
|
|||
|
|
font-size: 1.5rem;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
margin-bottom: 0.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile p {
|
|||
|
|
font-size: 1rem;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端网格优化 */
|
|||
|
|
body.mobile .grid {
|
|||
|
|
grid-template-columns: 1fr;
|
|||
|
|
gap: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile .grid-cols-2,
|
|||
|
|
body.mobile .grid-cols-3,
|
|||
|
|
body.mobile .grid-cols-4,
|
|||
|
|
body.mobile .md\\:grid-cols-2,
|
|||
|
|
body.mobile .md\\:grid-cols-3,
|
|||
|
|
body.mobile .lg\\:grid-cols-3 {
|
|||
|
|
grid-template-columns: 1fr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 平板端网格优化 */
|
|||
|
|
body.tablet .grid-cols-3,
|
|||
|
|
body.tablet .lg\\:grid-cols-3 {
|
|||
|
|
grid-template-columns: repeat(2, 1fr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端间距优化 */
|
|||
|
|
body.mobile .py-16 { padding-top: 3rem; padding-bottom: 3rem; }
|
|||
|
|
body.mobile .py-12 { padding-top: 2rem; padding-bottom: 2rem; }
|
|||
|
|
body.mobile .py-8 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
|
|||
|
|
body.mobile .mb-12 { margin-bottom: 2rem; }
|
|||
|
|
body.mobile .mb-8 { margin-bottom: 1.5rem; }
|
|||
|
|
|
|||
|
|
/* 移动端图片优化 */
|
|||
|
|
body.mobile img {
|
|||
|
|
max-width: 100%;
|
|||
|
|
height: auto;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 横屏优化 */
|
|||
|
|
body.mobile.landscape {
|
|||
|
|
/* 横屏时减少垂直间距 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile.landscape .py-16 {
|
|||
|
|
padding-top: 2rem;
|
|||
|
|
padding-bottom: 2rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile.landscape h1 {
|
|||
|
|
font-size: 1.75rem;
|
|||
|
|
margin-bottom: 0.75rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端返回顶部按钮优化 */
|
|||
|
|
body.mobile #back-to-top {
|
|||
|
|
bottom: 1.5rem;
|
|||
|
|
right: 1.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile .back-to-top-btn {
|
|||
|
|
width: 3rem;
|
|||
|
|
height: 3rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 移动端页面加载器优化 */
|
|||
|
|
body.mobile #page-loader .w-20 {
|
|||
|
|
width: 4rem;
|
|||
|
|
height: 4rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile #page-loader h1 {
|
|||
|
|
font-size: 1.25rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile #page-loader .w-64 {
|
|||
|
|
width: 16rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 防止页面缩放 */
|
|||
|
|
@media screen and (max-width: 768px) {
|
|||
|
|
html {
|
|||
|
|
zoom: 1;
|
|||
|
|
-ms-zoom: 1;
|
|||
|
|
-webkit-zoom: 1;
|
|||
|
|
-moz-zoom: 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 触摸设备特定优化 */
|
|||
|
|
@media (hover: none) and (pointer: coarse) {
|
|||
|
|
/* 移除hover效果,使用active效果 */
|
|||
|
|
.hover\\:scale-110:hover {
|
|||
|
|
transform: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.hover\\:scale-110:active {
|
|||
|
|
transform: scale(1.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.hover\\:shadow-xl:hover {
|
|||
|
|
box-shadow: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.hover\\:shadow-xl:active {
|
|||
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 安全区域适配(iPhone X等带刘海屏的设备) */
|
|||
|
|
@supports (padding: max(0px)) {
|
|||
|
|
body.mobile {
|
|||
|
|
padding-left: max(1rem, env(safe-area-inset-left));
|
|||
|
|
padding-right: max(1rem, env(safe-area-inset-right));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile #navbar {
|
|||
|
|
padding-left: max(1rem, env(safe-area-inset-left));
|
|||
|
|
padding-right: max(1rem, env(safe-area-inset-right));
|
|||
|
|
padding-top: max(0.75rem, env(safe-area-inset-top));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body.mobile #back-to-top {
|
|||
|
|
right: max(1.5rem, env(safe-area-inset-right));
|
|||
|
|
bottom: max(1.5rem, env(safe-area-inset-bottom));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
`;
|
|||
|
|
document.head.appendChild(style);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 优化图片加载
|
|||
|
|
function optimizeImages() {
|
|||
|
|
const images = document.querySelectorAll('img');
|
|||
|
|
|
|||
|
|
images.forEach(img => {
|
|||
|
|
// 添加懒加载
|
|||
|
|
if (!img.loading) {
|
|||
|
|
img.loading = 'lazy';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加错误处理
|
|||
|
|
img.addEventListener('error', function() {
|
|||
|
|
this.style.display = 'none';
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 移动端图片优化
|
|||
|
|
if (isMobile) {
|
|||
|
|
// 移动端使用较小的图片
|
|||
|
|
const src = img.src;
|
|||
|
|
if (src && !src.includes('placeholder')) {
|
|||
|
|
// 这里可以添加图片压缩逻辑
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 优化触摸事件
|
|||
|
|
function optimizeTouch() {
|
|||
|
|
// 添加触摸反馈
|
|||
|
|
document.addEventListener('touchstart', function(e) {
|
|||
|
|
const target = e.target;
|
|||
|
|
if (target.tagName === 'BUTTON' || target.classList.contains('btn') || target.tagName === 'A') {
|
|||
|
|
target.style.opacity = '0.7';
|
|||
|
|
}
|
|||
|
|
}, { passive: true });
|
|||
|
|
|
|||
|
|
document.addEventListener('touchend', function(e) {
|
|||
|
|
const target = e.target;
|
|||
|
|
if (target.tagName === 'BUTTON' || target.classList.contains('btn') || target.tagName === 'A') {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
target.style.opacity = '';
|
|||
|
|
}, 150);
|
|||
|
|
}
|
|||
|
|
}, { passive: true });
|
|||
|
|
|
|||
|
|
// 防止双击缩放
|
|||
|
|
let lastTouchEnd = 0;
|
|||
|
|
document.addEventListener('touchend', function(event) {
|
|||
|
|
const now = (new Date()).getTime();
|
|||
|
|
if (now - lastTouchEnd <= 300) {
|
|||
|
|
event.preventDefault();
|
|||
|
|
}
|
|||
|
|
lastTouchEnd = now;
|
|||
|
|
}, false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理屏幕方向变化
|
|||
|
|
function handleOrientationChange() {
|
|||
|
|
// 延迟执行以确保尺寸更新
|
|||
|
|
setTimeout(() => {
|
|||
|
|
detectDevice();
|
|||
|
|
|
|||
|
|
// 重新计算布局
|
|||
|
|
const event = new Event('resize');
|
|||
|
|
window.dispatchEvent(event);
|
|||
|
|
|
|||
|
|
// 滚动到顶部(防止方向变化后位置错乱)
|
|||
|
|
if (window.scrollY > 100) {
|
|||
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 优化键盘弹出时的布局
|
|||
|
|
function handleKeyboard() {
|
|||
|
|
if (!isMobile) return;
|
|||
|
|
|
|||
|
|
const originalHeight = window.innerHeight;
|
|||
|
|
let keyboardOpen = false;
|
|||
|
|
|
|||
|
|
window.addEventListener('resize', function() {
|
|||
|
|
const currentHeight = window.innerHeight;
|
|||
|
|
const heightDifference = originalHeight - currentHeight;
|
|||
|
|
|
|||
|
|
// 键盘弹出(高度减少超过150px)
|
|||
|
|
if (heightDifference > 150 && !keyboardOpen) {
|
|||
|
|
keyboardOpen = true;
|
|||
|
|
document.body.classList.add('keyboard-open');
|
|||
|
|
|
|||
|
|
// 隐藏导航栏(为输入框腾出空间)
|
|||
|
|
const navbar = document.getElementById('navbar');
|
|||
|
|
if (navbar) {
|
|||
|
|
navbar.style.transform = 'translateY(-100%)';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 隐藏返回顶部按钮
|
|||
|
|
const backToTop = document.getElementById('back-to-top');
|
|||
|
|
if (backToTop) {
|
|||
|
|
backToTop.style.display = 'none';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 键盘收起
|
|||
|
|
else if (heightDifference <= 150 && keyboardOpen) {
|
|||
|
|
keyboardOpen = false;
|
|||
|
|
document.body.classList.remove('keyboard-open');
|
|||
|
|
|
|||
|
|
// 恢复导航栏
|
|||
|
|
const navbar = document.getElementById('navbar');
|
|||
|
|
if (navbar) {
|
|||
|
|
navbar.style.transform = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 恢复返回顶部按钮
|
|||
|
|
const backToTop = document.getElementById('back-to-top');
|
|||
|
|
if (backToTop) {
|
|||
|
|
backToTop.style.display = '';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 性能优化
|
|||
|
|
function optimizePerformance() {
|
|||
|
|
// 使用 Intersection Observer 优化动画
|
|||
|
|
if ('IntersectionObserver' in window) {
|
|||
|
|
const observer = new IntersectionObserver((entries) => {
|
|||
|
|
entries.forEach(entry => {
|
|||
|
|
if (entry.isIntersecting) {
|
|||
|
|
entry.target.classList.add('animate-visible');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}, {
|
|||
|
|
threshold: 0.1,
|
|||
|
|
rootMargin: '50px'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 观察所有动画元素
|
|||
|
|
document.querySelectorAll('[class*="animate-"]').forEach(el => {
|
|||
|
|
if (!el.classList.contains('animate-visible')) {
|
|||
|
|
observer.observe(el);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 移动端减少动画
|
|||
|
|
if (isMobile) {
|
|||
|
|
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|||
|
|
if (mediaQuery.matches) {
|
|||
|
|
document.body.classList.add('reduce-motion');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加调试信息(开发环境)
|
|||
|
|
function addDebugInfo() {
|
|||
|
|
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
|||
|
|
const debugInfo = document.createElement('div');
|
|||
|
|
debugInfo.style.cssText = `
|
|||
|
|
position: fixed;
|
|||
|
|
top: 70px;
|
|||
|
|
right: 10px;
|
|||
|
|
background: rgba(0,0,0,0.8);
|
|||
|
|
color: white;
|
|||
|
|
padding: 8px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
z-index: 10000;
|
|||
|
|
font-family: monospace;
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
function updateDebugInfo() {
|
|||
|
|
const { isMobile, isTablet, currentOrientation } = detectDevice();
|
|||
|
|
debugInfo.innerHTML = `
|
|||
|
|
${window.innerWidth}×${window.innerHeight}<br>
|
|||
|
|
${isMobile ? 'Mobile' : isTablet ? 'Tablet' : 'Desktop'}<br>
|
|||
|
|
${currentOrientation}
|
|||
|
|
`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateDebugInfo();
|
|||
|
|
document.body.appendChild(debugInfo);
|
|||
|
|
|
|||
|
|
window.addEventListener('resize', updateDebugInfo);
|
|||
|
|
window.addEventListener('orientationchange', updateDebugInfo);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化移动端优化
|
|||
|
|
function init() {
|
|||
|
|
// 检测设备类型
|
|||
|
|
detectDevice();
|
|||
|
|
|
|||
|
|
// 添加移动端样式
|
|||
|
|
addMobileStyles();
|
|||
|
|
|
|||
|
|
// 优化触摸体验
|
|||
|
|
optimizeTouch();
|
|||
|
|
|
|||
|
|
// 处理屏幕方向变化
|
|||
|
|
window.addEventListener('orientationchange', handleOrientationChange);
|
|||
|
|
window.addEventListener('resize', () => {
|
|||
|
|
setTimeout(detectDevice, 100);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 页面加载完成后的优化
|
|||
|
|
if (document.readyState === 'loading') {
|
|||
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
optimizeImages();
|
|||
|
|
handleKeyboard();
|
|||
|
|
optimizePerformance();
|
|||
|
|
addDebugInfo();
|
|||
|
|
}, 500);
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
optimizeImages();
|
|||
|
|
handleKeyboard();
|
|||
|
|
optimizePerformance();
|
|||
|
|
addDebugInfo();
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 监听页面加载完成事件
|
|||
|
|
document.addEventListener('pageLoaded', () => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
optimizeImages();
|
|||
|
|
optimizePerformance();
|
|||
|
|
}, 100);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提供外部接口
|
|||
|
|
window.MobileOptimize = {
|
|||
|
|
detectDevice,
|
|||
|
|
isMobile: () => isMobile,
|
|||
|
|
isTablet: () => isTablet,
|
|||
|
|
getCurrentOrientation: () => currentOrientation,
|
|||
|
|
optimizeImages,
|
|||
|
|
optimizePerformance
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 初始化
|
|||
|
|
init();
|
|||
|
|
})();
|