CSS 媒体查询适配
/* 竖屏样式 */
@media (orientation: portrait) {
.openclaw-container {
width: 100vw;
height: 60vh;
/* 竖屏下的手势区域布局 */
}
.gesture-panel {
grid-template-columns: repeat(2, 1fr);
}
}
/* 横屏样式 */
@media (orientation: landscape) {
.openclaw-container {
width: 80vw;
height: 80vh;
margin: 0 auto;
}
.gesture-panel {
grid-template-columns: repeat(4, 1fr);
}
}
/* 响应式基础样式 */
.openclaw-container {
touch-action: none; /* 防止浏览器默认手势 */
user-select: none;
position: relative;
}
JavaScript 方向监听与适配
class OpenClawOrientationAdapter {
constructor(options = {}) {
this.options = {
onOrientationChange: null,
portraitConfig: {
maxFingers: 4,
gestureThreshold: 30,
// 竖屏特定配置
},
landscapeConfig: {
maxFingers: 5,
gestureThreshold: 40,
// 横屏特定配置
},
...options
};
this.currentOrientation = this.getOrientation();
this.init();
}
// 获取当前方向
getOrientation() {
return window.innerWidth > window.innerHeight ? 'landscape' : 'portrait';
}
// 初始化 OpenClaw 实例
init() {
const config = this.currentOrientation === 'portrait'
? this.options.portraitConfig
: this.options.landscapeConfig;
this.openclaw = new OpenClaw({
element: document.getElementById('touch-area'),
maxFingers: config.maxFingers,
threshold: config.gestureThreshold,
onGesture: this.handleGesture.bind(this),
// 其他配置...
});
this.setupOrientationListeners();
}
// 设置方向监听
setupOrientationListeners() {
// 监听窗口大小变化(包含方向变化)
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
this.handleOrientationChange();
}, 150);
});
// 监听设备方向变化(移动设备)
if (window.screen.orientation) {
window.screen.orientation.addEventListener('change', () => {
this.handleOrientationChange();
});
}
// 监听 orientationchange 事件
window.addEventListener('orientationchange', () => {
setTimeout(() => this.handleOrientationChange(), 100);
});
}
// 处理方向变化
handleOrientationChange() {
const newOrientation = this.getOrientation();
if (newOrientation !== this.currentOrientation) {
this.currentOrientation = newOrientation;
this.updateOpenClawConfig();
// 触发回调
if (this.options.onOrientationChange) {
this.options.onOrientationChange(newOrientation);
}
}
}
// 更新 OpenClaw 配置
updateOpenClawConfig() {
const config = this.currentOrientation === 'portrait'
? this.options.portraitConfig
: this.options.landscapeConfig;
// 重新配置 OpenClaw
this.openclaw.updateConfig(config);
// 更新 UI 布局
this.updateUILayout();
}
// 更新 UI 布局
updateUILayout() {
const container = document.querySelector('.openclaw-container');
const isPortrait = this.currentOrientation === 'portrait';
// 调整布局
if (isPortrait) {
container.classList.add('portrait-mode');
container.classList.remove('landscape-mode');
} else {
container.classList.add('landscape-mode');
container.classList.remove('portrait-mode');
}
// 调整手势区域大小
this.adjustTouchArea();
}
// 调整触摸区域
adjustTouchArea() {
const touchArea = document.getElementById('touch-area');
const rect = touchArea.getBoundingClientRect();
// 通知 OpenClaw 区域变化
this.openclaw.setBounds(rect);
}
// 手势处理
handleGesture(gesture) {
// 根据方向调整手势处理逻辑
const adjustedGesture = this.adjustGestureForOrientation(gesture);
// 执行手势动作
this.executeGesture(adjustedGesture);
}
// 根据方向调整手势数据
adjustGestureForOrientation(gesture) {
const isPortrait = this.currentOrientation === 'portrait';
if (isPortrait) {
// 竖屏下的手势调整
return {
...gesture,
// 调整坐标或阈值
};
} else {
// 横屏下的手势调整
return {
...gesture,
// 调整坐标或阈值
};
}
}
}
React/Vue 组件适配示例
React 示例:
import React, { useEffect, useState } from 'react';
import OpenClaw from 'openclaw';
const OpenClawComponent = () => {
const [orientation, setOrientation] = useState(
window.innerWidth > window.innerHeight ? 'landscape' : 'portrait'
);
const [openclaw, setOpenclaw] = useState(null);
useEffect(() => {
// 初始化 OpenClaw
const claw = new OpenClaw({
element: document.getElementById('touch-area'),
...getConfigByOrientation(orientation)
});
setOpenclaw(claw);
// 监听方向变化
const handleResize = () => {
const newOrientation = window.innerWidth > window.innerHeight
? 'landscape'
: 'portrait';
if (newOrientation !== orientation) {
setOrientation(newOrientation);
claw.updateConfig(getConfigByOrientation(newOrientation));
}
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
claw.destroy();
};
}, [orientation]);
const getConfigByOrientation = (orient) => {
return orient === 'portrait'
? { maxFingers: 4, threshold: 30 }
: { maxFingers: 5, threshold: 40 };
};
return (
<div className={`openclaw-wrapper ${orientation}`}>
<div
id="touch-area"
className={`touch-area ${orientation}`}
style={orientation === 'portrait'
? { height: '60vh' }
: { height: '80vh' }
}
/>
<div className="gesture-hints">
{orientation === 'portrait'
? '竖屏手势提示'
: '横屏手势提示'
}
</div>
</div>
);
};
移动端特定适配
// 移动端横竖屏适配增强
class MobileOpenClawAdapter extends OpenClawOrientationAdapter {
constructor(options) {
super(options);
// 处理 iOS 和 Android 的差异
this.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
this.isAndroid = /Android/.test(navigator.userAgent);
this.setupMobileSpecifics();
}
setupMobileSpecifics() {
// 防止双击缩放
document.addEventListener('touchstart', (e) => {
if (e.touches.length > 1) {
e.preventDefault();
}
}, { passive: false });
// 处理移动端键盘弹出
if (this.isIOS) {
this.handleIOSKeyboard();
}
}
handleIOSKeyboard() {
// iOS 键盘弹出时调整布局
window.addEventListener('focusin', () => {
this.pauseGestures();
});
window.addEventListener('focusout', () => {
this.resumeGestures();
});
}
pauseGestures() {
if (this.openclaw) {
this.openclaw.disable();
}
}
resumeGestures() {
if (this.openclaw) {
this.openclaw.enable();
this.adjustTouchArea();
}
}
}
最佳实践建议
-
测试策略:

// 在开发和测试阶段添加方向切换测试 function addOrientationTestButtons() { const testDiv = document.createElement('div'); testDiv.innerHTML = ` <button onclick="switchToPortrait()">模拟竖屏</button> <button onclick="switchToLandscape()">模拟横屏</button> `; document.body.appendChild(testDiv); } function switchToPortrait() { window.innerWidth = 375; window.innerHeight = 667; window.dispatchEvent(new Event('resize')); } function switchToLandscape() { window.innerWidth = 667; window.innerHeight = 375; window.dispatchEvent(new Event('resize')); } -
性能优化:
// 防抖处理方向变化 const debouncedOrientationChange = debounce(() => { // 更新逻辑 }, 250); -
CSS 变量动态调整:
:root { --openclaw-max-fingers: 4; --openclaw-threshold: 30px; } @media (orientation: landscape) { :root { --openclaw-max-fingers: 5; --openclaw-threshold: 40px; } }
这样适配后,OpenClaw 可以在不同屏幕方向上提供一致的手势体验,同时确保布局和交互的合理性。
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。