Skip to content

响应式UI设计

概述

Eagle2Ae AE 扩展 v2.4.0 引入了全面的响应式UI设计,支持小屏幕模式下的按钮图标化、独显模式下的新面板形式等多种自适应布局。这些改进确保了扩展在不同屏幕尺寸和使用场景下都能提供优秀的用户体验。

核心特性

小屏幕适配

  • 按钮图标化:在小屏幕模式下,按钮文本自动隐藏,仅显示图标
  • 布局优化:自动调整元素间距和尺寸以适应小屏幕
  • 触摸友好:增大触摸目标,优化移动端操作体验

独显模式(专注模式)

  • 全屏面板:最大化显示特定功能区域
  • 沉浸式体验:隐藏非必要元素,专注于核心功能
  • 动态布局:根据屏幕尺寸自动调整布局结构

多尺寸适配

  • 极小屏幕:宽度 ≤ 360px 的设备
  • 小屏幕:宽度 ≤ 480px 的设备
  • 中等屏幕:宽度 ≤ 768px 的设备
  • 大屏幕:宽度 > 768px 的设备

使用指南

小屏幕按钮图标化

自动适配机制

css
/* 响应式按钮样式 */
.icon-btn {
    background: #3a3a3a;
    color: #e0e0e0;
    border: 1px solid #555;
    border-radius: 4px;
    padding: clamp(2px, 0.4vw, 6px) clamp(3px, 0.6vw, 8px);
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: clamp(18px, 3.2vw, 32px);
    height: clamp(18px, 3vw, 28px);
}

.icon-btn .icon {
    font-size: clamp(10px, 1.4vw, 14px);
    line-height: 1;
}

/* 极小尺寸:只显示图标 */
@media (max-width: 360px) {
    .icon-btn {
        min-width: 22px;
        height: 22px;
        padding: 3px 5px;
    }
    
    .icon-btn .icon {
        font-size: 12px;
    }
    
    /* 隐藏按钮文本,只显示图标 */
    .icon-btn .text {
        display: none;
    }
}

按钮HTML结构

html
<!-- 标准按钮结构 -->
<button class="icon-btn" title="主题切换">
    <span class="icon">🌙</span>
    <span class="text">主题</span>
</button>

<!-- 在小屏幕下自动隐藏文本,只显示图标 -->

JavaScript实现

javascript
/**
 * 响应式按钮管理器
 */
class ResponsiveButtonManager {
    constructor() {
        this.init();
    }
    
    /**
     * 初始化响应式按钮管理
     */
    init() {
        // 监听窗口大小变化
        window.addEventListener('resize', this.debounce(() => {
            this.updateResponsiveButtons();
        }, 200));
        
        // 初始更新
        this.updateResponsiveButtons();
    }
    
    /**
     * 更新响应式按钮
     */
    updateResponsiveButtons() {
        const screenWidth = window.innerWidth;
        
        // 根据屏幕宽度调整按钮显示
        if (screenWidth <= 360) {
            this.enableIconOnlyMode();
        } else if (screenWidth <= 480) {
            this.enableCompactMode();
        } else {
            this.enableFullMode();
        }
    }
    
    /**
     * 启用仅图标模式
     */
    enableIconOnlyMode() {
        const buttons = document.querySelectorAll('.icon-btn');
        buttons.forEach(button => {
            const textSpan = button.querySelector('.text');
            if (textSpan) {
                textSpan.style.display = 'none';
            }
            
            // 调整按钮尺寸
            button.style.minWidth = '22px';
            button.style.height = '22px';
            button.style.padding = '3px 5px';
        });
        
        this.log('📱 已启用仅图标模式', 'debug');
    }
    
    /**
     * 启用紧凑模式
     */
    enableCompactMode() {
        const buttons = document.querySelectorAll('.icon-btn');
        buttons.forEach(button => {
            const textSpan = button.querySelector('.text');
            if (textSpan) {
                textSpan.style.display = '';
            }
            
            // 调整按钮尺寸
            button.style.minWidth = '28px';
            button.style.height = '28px';
            button.style.padding = '4px 6px';
        });
        
        this.log('📱 已启用紧凑模式', 'debug');
    }
    
    /**
     * 启用完整模式
     */
    enableFullMode() {
        const buttons = document.querySelectorAll('.icon-btn');
        buttons.forEach(button => {
            const textSpan = button.querySelector('.text');
            if (textSpan) {
                textSpan.style.display = '';
            }
            
            // 恢复按钮尺寸
            button.style.minWidth = '';
            button.style.height = '';
            button.style.padding = '';
        });
        
        this.log('📱 已启用完整模式', 'debug');
    }
    
    /**
     * 防抖函数
     */
    debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }
}

独显模式(专注模式)

功能说明

独显模式(Fullscreen Mode)是一种专注模式,它可以最大化显示特定功能区域,隐藏非必要元素,为用户提供沉浸式的操作体验。

启用方式

  1. 在UI设置面板中启用"占满"选项
  2. 或通过快捷键快速切换(如果已配置)

样式实现

css
/* 独显模式容器样式 */
.content {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* 占满模式 */
.content.fullscreen-mode {
    gap: 0;
    padding: 12px;
    height: 100vh;
    box-sizing: border-box;
}

.content.fullscreen-mode .section {
    display: none;
}

.content.fullscreen-mode #import-mode-section {
    display: flex !important;
    flex-direction: column;
    justify-content: space-evenly;
    gap: clamp(8px, 2vh, 20px);
    flex: 1;
    min-height: 0;
    margin: 0;
    padding: clamp(12px, 3vh, 32px);
    overflow: auto;
    box-sizing: border-box;
    /* 保留外框样式 */
    background: #2a2a2a;
    border: 1px solid #555;
    border-radius: 6px;
}

.content.fullscreen-mode #import-mode-section .section-title {
    display: none;
}

/* 占满模式下的按钮组样式 */
.content.fullscreen-mode #import-mode-section .import-mode-selection,
.content.fullscreen-mode #import-mode-section .import-behavior,
.content.fullscreen-mode #import-mode-section .layer-operations {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    width: 100%;
    min-height: 32px;
}

.content.fullscreen-mode #import-mode-section .mode-buttons,
.content.fullscreen-mode #import-mode-section .import-behavior-buttons,
.content.fullscreen-mode #import-mode-section .layer-operation-buttons {
    display: flex;
    gap: 12px;
    width: 100%;
    height: 100%;
    min-height: 32px;
}

.content.fullscreen-mode #import-mode-section .mode-button,
.content.fullscreen-mode #import-mode-section .import-behavior-button,
.content.fullscreen-mode #import-mode-section .layer-operation-button {
    flex: 1;
    height: 100%;
    min-height: 32px;
    font-size: clamp(10px, 1.5vh, 14px);
    padding: clamp(6px, 1.5vh, 12px) clamp(8px, 2vh, 16px);
}

JavaScript实现

javascript
/**
 * 独显模式管理器
 */
class FullscreenModeManager {
    constructor() {
        this.isEnabled = false;
        this.init();
    }
    
    /**
     * 初始化独显模式管理器
     */
    init() {
        // 绑定独显模式切换事件
        const fullscreenToggle = document.getElementById('fullscreen-toggle');
        if (fullscreenToggle) {
            fullscreenToggle.addEventListener('click', () => {
                this.toggleFullscreenMode();
            });
        }
        
        // 检查初始状态
        const savedState = this.getPanelLocalStorage('fullscreenMode');
        if (savedState === 'true') {
            this.enableFullscreenMode();
        }
    }
    
    /**
     * 切换独显模式
     */
    toggleFullscreenMode() {
        if (this.isEnabled) {
            this.disableFullscreenMode();
        } else {
            this.enableFullscreenMode();
        }
    }
    
    /**
     * 启用独显模式
     */
    enableFullscreenMode() {
        const contentContainer = document.querySelector('.content');
        if (contentContainer) {
            contentContainer.classList.add('fullscreen-mode');
            this.isEnabled = true;
            
            // 保存状态
            this.setPanelLocalStorage('fullscreenMode', 'true');
            
            this.log('🖥️ 已启用独显模式(专注模式)', 'info');
        }
    }
    
    /**
     * 禁用独显模式
     */
    disableFullscreenMode() {
        const contentContainer = document.querySelector('.content');
        if (contentContainer) {
            contentContainer.classList.remove('fullscreen-mode');
            this.isEnabled = false;
            
            // 保存状态
            this.setPanelLocalStorage('fullscreenMode', 'false');
            
            this.log('🖥️ 已禁用独显模式(专注模式)', 'info');
        }
    }
    
    /**
     * 更新独显模式状态
     */
    updateFullscreenMode() {
        const contentContainer = document.querySelector('.content');
        if (contentContainer) {
            if (this.isEnabled) {
                contentContainer.classList.add('fullscreen-mode');
            } else {
                contentContainer.classList.remove('fullscreen-mode');
            }
        }
    }
}

技术实现

响应式断点设计

css
/* 响应式断点 */
/* 大屏幕优化 */
@media (min-width: 1200px) {
    .header {
        gap: 16px;
        padding: 16px;
    }
    
    .title {
        gap: 12px;
        font-size: 18px;
    }
    
    .title-logo {
        width: clamp(24px, 4vw, 36px);
        height: clamp(24px, 4vw, 36px);
    }
    
    .header-actions {
        gap: 12px;
    }
    
    .icon-btn {
        min-width: clamp(32px, 4vw, 40px);
        height: clamp(32px, 4vw, 40px);
        padding: clamp(6px, 0.8vw, 10px) clamp(8px, 1vw, 12px);
    }
    
    .icon-btn .icon {
        font-size: clamp(14px, 1.8vw, 18px);
    }
}

/* 中等屏幕优化 */
@media (max-width: 1199px) and (min-width: 769px) {
    .header {
        gap: 12px;
        padding: 14px;
    }
    
    .title {
        gap: 10px;
        font-size: 16px;
    }
    
    .title-logo {
        width: clamp(22px, 4.5vw, 32px);
        height: clamp(22px, 4.5vw, 32px);
    }
    
    .header-actions {
        gap: 10px;
    }
    
    .icon-btn {
        min-width: clamp(28px, 4.2vw, 36px);
        height: clamp(28px, 4.2vw, 36px);
        padding: clamp(5px, 0.7vw, 8px) clamp(6px, 0.9vw, 10px);
    }
    
    .icon-btn .icon {
        font-size: clamp(12px, 1.6vw, 16px);
    }
}

/* 小屏幕优化 */
@media (max-width: 768px) {
    .header {
        gap: 8px;
        padding: 12px;
    }
    
    .title {
        gap: 8px;
        font-size: 15px;
    }
    
    .title-logo {
        width: clamp(20px, 5vw, 32px);
        height: clamp(20px, 5vw, 32px);
    }
    
    .header-actions {
        gap: 8px;
    }
    
    .icon-btn {
        min-width: clamp(22px, 4.5vw, 36px);
        height: clamp(22px, 4.5vw, 32px);
        padding: clamp(3px, 0.6vw, 8px) clamp(4px, 0.8vw, 10px);
    }
    
    .icon-btn .icon {
        font-size: clamp(12px, 2vw, 16px);
    }
}

/* 极小屏幕优化 */
@media (max-width: 480px) {
    .header {
        gap: 6px;
        padding: 10px;
    }
    
    .title {
        gap: 6px;
        font-size: 14px;
        flex: 0 1 auto;
        width: auto;
        overflow: visible;
        white-space: nowrap;
    }
    
    .title-text {
        display: flex;
        position: relative;
        min-width: 0;
        flex: 0 0 auto;
        width: auto;
        overflow: visible;
        white-space: nowrap;
    }
    
    .char {
        display: inline-block;
        transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
        position: relative;
        flex-shrink: 0;
        margin: 0 clamp(0.5px, 0.3vw, 1.5px);
        color: #9966cc;
    }
    
    .title-logo {
        width: 24px;
        height: 24px;
    }
    
    .header-actions {
        gap: 6px;
    }
    
    .icon-btn {
        min-width: 28px;
        height: 28px;
        padding: 4px 6px;
    }
    
    .icon-btn .icon {
        font-size: 13px;
    }
}

/* 极限尺寸:只显示 logo 和按钮组 */
@media (max-width: 360px) {
    .header {
        gap: 4px;
        padding: 8px;
    }
    
    .title {
        flex: 0 0 auto;
        min-width: auto;
    }
    
    .title-text {
        display: none;
    }
    
    .title-logo {
        width: 22px;
        height: 22px;
    }
    
    .header-actions {
        gap: 4px;
    }
    
    .icon-btn {
        min-width: 26px;
        height: 26px;
        padding: 3px 5px;
    }
    
    .icon-btn .icon {
        font-size: 12px;
    }
}

触摸友好设计

css
/* 触摸设备优化 */
@media (hover: none) and (pointer: coarse) {
    /* 增大触摸目标 */
    .icon-btn {
        min-width: 44px;
        height: 44px;
        padding: 10px 12px;
    }
    
    /* 增加按钮间距 */
    .header-actions {
        gap: 12px;
    }
    
    /* 禁用悬停效果 */
    .icon-btn:hover {
        background: #3a3a3a;
        border-color: #555;
    }
    
    /* 启用触摸反馈 */
    .icon-btn:active {
        background: #2a2a2a;
        transform: scale(0.95);
        transition: all 0.1s ease;
    }
}

/* 高DPI屏幕优化 */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
    .icon-btn .icon {
        font-size: clamp(14px, 2vw, 18px);
    }
    
    .title-logo {
        image-rendering: -webkit-optimize-contrast;
        image-rendering: crisp-edges;
    }
}

最佳实践

响应式设计建议

  1. 渐进式增强

    css
    /* 从移动优先开始 */
    .responsive-element {
        /* 移动端基础样式 */
        width: 100%;
        padding: 10px;
    }
    
    /* 平板端增强 */
    @media (min-width: 768px) {
        .responsive-element {
            width: 50%;
            padding: 15px;
        }
    }
    
    /* 桌面端增强 */
    @media (min-width: 1024px) {
        .responsive-element {
            width: 33.33%;
            padding: 20px;
        }
    }
  2. 弹性布局

    css
    /* 使用Flexbox实现弹性布局 */
    .flex-container {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
    }
    
    .flex-item {
        flex: 1 1 200px; /* 最小宽度200px */
        min-width: 0; /* 允许收缩 */
    }
  3. 相对单位

    css
    /* 使用clamp()实现响应式字体大小 */
    .responsive-text {
        font-size: clamp(12px, 2.5vw, 18px);
        line-height: 1.4;
    }
    
    /* 使用vw单位实现响应式间距 */
    .responsive-padding {
        padding: 2vw 3vw;
    }

独显模式使用建议

  1. 适用场景

    • 需要专注操作的场景
    • 屏幕空间有限的环境
    • 移动设备上的使用
    • 演示和教学用途
  2. 最佳实践

    javascript
    // 根据屏幕尺寸自动启用独显模式
    function autoEnableFullscreenMode() {
        const screenWidth = window.innerWidth;
        const screenHeight = window.innerHeight;
        
        // 在小屏幕设备上自动启用独显模式
        if (screenWidth <= 768 || screenHeight <= 600) {
            fullscreenModeManager.enableFullscreenMode();
            console.log('📱 检测到小屏幕设备,已自动启用独显模式');
        }
    }
    
    // 监听屏幕方向变化
    window.addEventListener('orientationchange', () => {
        setTimeout(() => {
            autoEnableFullscreenMode();
        }, 100);
    });

故障排除

常见问题

  1. 按钮图标化不生效

    • 症状:在小屏幕上按钮文本未隐藏
    • 解决:检查CSS媒体查询是否正确应用,验证屏幕尺寸检测逻辑
  2. 独显模式布局错乱

    • 症状:启用独显模式后界面元素重叠或显示异常
    • 解决:检查CSS选择器优先级,确保fullscreen-mode类正确应用
  3. 触摸反馈不明显

    • 症状:在触摸设备上按钮点击反馈不明显
    • 解决:检查触摸设备媒体查询,验证:active状态样式

调试技巧

  1. 屏幕尺寸模拟

    javascript
    // 在控制台中模拟不同屏幕尺寸
    function simulateScreenSize(width, height) {
        console.log(`📱 模拟屏幕尺寸: ${width}x${height}`);
        
        // 更新视口元标签
        let viewport = document.querySelector('meta[name="viewport"]');
        if (!viewport) {
            viewport = document.createElement('meta');
            viewport.name = 'viewport';
            document.head.appendChild(viewport);
        }
        viewport.content = `width=${width}, height=${height}, initial-scale=1.0`;
        
        // 触发resize事件
        window.dispatchEvent(new Event('resize'));
    }
    
    // 模拟手机屏幕
    simulateScreenSize(375, 667);
  2. 响应式断点调试

    css
    /* 添加调试样式显示当前断点 */
    body::before {
        content: "Desktop";
        position: fixed;
        top: 0;
        left: 0;
        background: rgba(0,0,0,0.8);
        color: white;
        padding: 4px 8px;
        font-size: 12px;
        z-index: 9999;
    }
    
    @media (max-width: 1199px) {
        body::before { content: "Tablet Landscape"; }
    }
    
    @media (max-width: 768px) {
        body::before { content: "Tablet Portrait"; }
    }
    
    @media (max-width: 480px) {
        body::before { content: "Mobile"; }
    }
    
    @media (max-width: 360px) {
        body::before { content: "Small Mobile"; }
    }
  3. 触摸设备检测

    javascript
    // 检测是否为触摸设备
    function isTouchDevice() {
        return 'ontouchstart' in window || 
               navigator.maxTouchPoints > 0 || 
               navigator.msMaxTouchPoints > 0;
    }
    
    // 检测悬停支持
    function supportsHover() {
        return window.matchMedia('(hover: hover)').matches;
    }
    
    // 根据设备类型应用不同优化
    if (isTouchDevice() && !supportsHover()) {
        document.body.classList.add('touch-device');
        console.log('📱 检测到触摸设备');
    } else {
        document.body.classList.add('mouse-device');
        console.log('🖱️ 检测到鼠标设备');
    }

扩展性

自定义响应式断点

javascript
// 允许用户自定义响应式断点
class CustomBreakpointManager {
    constructor() {
        this.breakpoints = {
            xs: 360,   // 极小屏幕
            sm: 480,   // 小屏幕
            md: 768,   // 中等屏幕
            lg: 1024,  // 大屏幕
            xl: 1200   // 超大屏幕
        };
        
        this.loadCustomBreakpoints();
    }
    
    /**
     * 加载自定义断点设置
     */
    loadCustomBreakpoints() {
        try {
            const savedBreakpoints = localStorage.getItem('customBreakpoints');
            if (savedBreakpoints) {
                this.breakpoints = { ...this.breakpoints, ...JSON.parse(savedBreakpoints) };
            }
        } catch (error) {
            console.warn('加载自定义断点失败:', error);
        }
    }
    
    /**
     * 保存自定义断点设置
     */
    saveCustomBreakpoints() {
        try {
            localStorage.setItem('customBreakpoints', JSON.stringify(this.breakpoints));
        } catch (error) {
            console.warn('保存自定义断点失败:', error);
        }
    }
    
    /**
     * 获取断点查询字符串
     * @param {string} breakpoint - 断点名称
     * @returns {string} 媒体查询字符串
     */
    getMediaQuery(breakpoint) {
        const value = this.breakpoints[breakpoint];
        if (!value) return null;
        
        switch (breakpoint) {
            case 'xs':
                return `(max-width: ${value}px)`;
            case 'sm':
                return `(max-width: ${value}px)`;
            case 'md':
                return `(max-width: ${value}px)`;
            case 'lg':
                return `(max-width: ${value}px)`;
            case 'xl':
                return `(min-width: ${value + 1}px)`;
            default:
                return null;
        }
    }
}

动态主题适配

javascript
// 根据屏幕尺寸动态调整主题
class AdaptiveThemeManager {
    constructor() {
        this.init();
    }
    
    /**
     * 初始化自适应主题管理
     */
    init() {
        // 监听屏幕尺寸变化
        window.addEventListener('resize', this.debounce(() => {
            this.adaptThemeToScreenSize();
        }, 300));
        
        // 初始适配
        this.adaptThemeToScreenSize();
    }
    
    /**
     * 根据屏幕尺寸适配主题
     */
    adaptThemeToScreenSize() {
        const screenWidth = window.innerWidth;
        const screenHeight = window.innerHeight;
        
        // 在极小屏幕上简化主题
        if (screenWidth <= 360) {
            document.body.classList.add('minimal-theme');
            this.log('📱 已启用极简主题', 'debug');
        } else {
            document.body.classList.remove('minimal-theme');
            this.log('📱 已恢复标准主题', 'debug');
        }
        
        // 在超大屏幕上启用增强主题
        if (screenWidth >= 1920 && screenHeight >= 1080) {
            document.body.classList.add('enhanced-theme');
            this.log('🖥️ 已启用增强主题', 'debug');
        } else {
            document.body.classList.remove('enhanced-theme');
            this.log('🖥️ 已恢复标准主题', 'debug');
        }
    }
    
    /**
     * 防抖函数
     */
    debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }
}

Released under the MIT License.