我来详细介绍一下 OpenClaw 项目的横竖屏适配方案

openclaw openclaw官方 1

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 ? '竖屏' : '横屏'}`);
});

最佳实践建议

  1. 移动优先:先设计竖屏布局,再适配横屏
  2. 渐进增强:为横屏提供更多功能或更好的布局
  3. 保持一致性:确保横竖屏切换时用户体验连贯
  4. 测试全面:在不同设备和分辨率下测试适配效果
  5. 性能优化:避免在方向变化时执行过多重排操作

常见问题解决

// 修复 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 项目在各种设备和屏幕方向上都能提供良好的用户体验。

我来详细介绍一下 OpenClaw 项目的横竖屏适配方案-第1张图片-OpenClaw开源下载|官方OpenClaw下载

标签: OpenClaw 横竖屏适配

抱歉,评论功能暂时关闭!