CSS 媒体查询适配
基础适配方案
/* 竖屏样式 */
@media screen and (orientation: portrait) {
.container {
flex-direction: column;
padding: 20px 15px;
}
.content {
width: 100%;
max-width: 480px;
margin: 0 auto;
}
}
/* 横屏样式 */
@media screen and (orientation: landscape) {
.container {
flex-direction: row;
padding: 30px;
}
.sidebar {
width: 280px;
flex-shrink: 0;
}
.content {
flex: 1;
margin-left: 30px;
}
}
JavaScript 检测屏幕方向
检测和监听方向变化
// 检测当前方向
const isPortrait = window.matchMedia("(orientation: portrait)").matches;
// 监听方向变化
function handleOrientationChange() {
if (window.matchMedia("(orientation: portrait)").matches) {
console.log('现在是竖屏模式');
document.body.classList.add('portrait');
document.body.classList.remove('landscape');
} else {
console.log('现在是横屏模式');
document.body.classList.add('landscape');
document.body.classList.remove('portrait');
}
}
// 添加监听
window.addEventListener('resize', handleOrientationChange);
window.addEventListener('orientationchange', handleOrientationChange);
// 初始化
handleOrientationChange();
React/Vue 适配方案
React 示例
import { useState, useEffect } from 'react';
function useOrientation() {
const [isPortrait, setIsPortrait] = useState(
window.matchMedia("(orientation: portrait)").matches
);
useEffect(() => {
const mediaQuery = window.matchMedia("(orientation: portrait)");
const handleChange = (e) => {
setIsPortrait(e.matches);
};
// 现代浏览器
mediaQuery.addEventListener('change', handleChange);
return () => {
mediaQuery.removeEventListener('change', handleChange);
};
}, []);
return { isPortrait, isLandscape: !isPortrait };
}
// 使用示例
function App() {
const { isPortrait } = useOrientation();
return (
<div className={`app ${isPortrait ? 'portrait' : 'landscape'}`}>
{isPortrait ? (
<MobileLayout />
) : (
<DesktopLayout />
)}
</div>
);
}
响应式布局策略
弹性布局方案
/* 基础布局 */
.container {
display: flex;
min-height: 100vh;
}
/* 竖屏:垂直排列 */
@media (orientation: portrait) {
.container {
flex-direction: column;
}
.panel {
width: 100%;
margin-bottom: 20px;
}
}
/* 横屏:水平排列 */
@media (orientation: landscape) {
.container {
flex-direction: row;
}
.panel {
width: 50%;
margin-right: 20px;
}
}
/* 响应式断点组合 */
@media (orientation: portrait) and (max-width: 768px) {
/* 小屏幕竖屏 */
}
@media (orientation: landscape) and (min-height: 600px) {
/* 横屏且有一定高度 */
}
图片和媒体适配
/* 响应式图片 */
.responsive-image {
width: 100%;
height: auto;
}
/* 横竖屏不同比例 */
@media (orientation: portrait) {
.hero-image {
aspect-ratio: 4/3;
object-fit: cover;
}
}
@media (orientation: landscape) {
.hero-image {
aspect-ratio: 16/9;
object-fit: cover;
}
}
特殊设备适配
安全区域适配(全面屏手机)
/* 考虑安全区域 */
.safe-area {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
/* 横屏时的安全区域 */
@media (orientation: landscape) {
.landscape-safe {
padding-left: max(env(safe-area-inset-left), 20px);
padding-right: max(env(safe-area-inset-right), 20px);
}
}
工具函数
// 方向工具类
class OrientationManager {
constructor() {
this.isPortrait = this.checkOrientation();
this.init();
}
checkOrientation() {
return window.matchMedia("(orientation: portrait)").matches;
}
init() {
// 监听变化
window.addEventListener('orientationchange', this.handleChange.bind(this));
window.addEventListener('resize', this.debounce(this.handleChange.bind(this), 100));
}
handleChange() {
const newOrientation = this.checkOrientation();
if (this.isPortrait !== newOrientation) {
this.isPortrait = newOrientation;
this.emit('change', newOrientation);
}
}
// 防抖函数
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 事件监听
listeners = {};
on(event, callback) {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(callback);
}
emit(event, data) {
if (this.listeners[event]) {
this.listeners[event].forEach(callback => callback(data));
}
}
}
// 使用示例
const orientation = new OrientationManager();
orientation.on('change', (isPortrait) => {
console.log(`方向已改变: ${isPortrait ? '竖屏' : '横屏'}`);
});
最佳实践建议
- 移动优先:先设计竖屏布局,再适配横屏
- 渐进增强:为横屏提供更多功能或更好的布局
- 保持一致性:确保横竖屏切换时用户体验连贯
- 测试全面:在不同设备和分辨率下测试适配效果
- 性能优化:避免在方向变化时执行过多重排操作
常见问题解决
// 修复 iOS Safari 横屏时的 100vh 问题
function setVH() {
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
// 初始设置
setVH();
// 方向变化时重新计算
window.addEventListener('orientationchange', setVH);
window.addEventListener('resize', setVH);
// CSS中使用
.full-height {
height: 100vh; /* 回退方案 */
height: calc(var(--vh, 1vh) * 100);
}
这个方案可以确保 OpenClaw 项目在各种设备和屏幕方向上都能提供良好的用户体验。

版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。