4.1 Sciter CSS 扩展概述

4.1.1 CSS 扩展特性

Sciter 在标准 CSS 基础上提供了丰富的扩展特性:

graph TD
    A[Sciter CSS] --> B[标准 CSS3]
    A --> C[布局扩展]
    A --> D[动画扩展]
    A --> E[状态选择器]
    A --> F[函数扩展]
    
    B --> G[Flexbox]
    B --> H[Grid]
    B --> I[Transform]
    
    C --> J[Flow 布局]
    C --> K[Stack 布局]
    C --> L[Table 布局]
    
    D --> M[过渡动画]
    D --> N[关键帧动画]
    D --> O[物理动画]
    
    E --> P[:hover]
    E --> Q[:active]
    E --> R[:focus]
    E --> S[:disabled]
    
    F --> T[calc()]
    F --> U[var()]
    F --> V[color()]

4.1.2 样式应用方式

// 1. 内联样式
const element = document.querySelector(".my-element");
element.style.color = "red";
element.style.backgroundColor = "#f0f0f0";
element.style.transform = "translateX(100px)";

// 2. CSS 类操作
element.classList.add("active", "highlighted");
element.classList.remove("inactive");
element.classList.toggle("visible");

// 3. CSS 自定义属性
element.style.setProperty("--primary-color", "#007bff");
element.style.setProperty("--font-size", "16px");

// 4. 动态样式表
const styleSheet = document.createElement("style");
styleSheet.textContent = `
    .dynamic-class {
        color: var(--primary-color);
        font-size: var(--font-size);
        transition: all 0.3s ease;
    }
`;
document.head.appendChild(styleSheet);

// 5. Sciter 特有的样式设置
element.style.behavior = "button"; // 设置行为
element.style.flow = "horizontal"; // 设置流布局
element.style.aspectRatio = "16/9"; // 设置宽高比

4.1.3 CSS 变量和主题系统

/* 主题变量定义 */
:root {
    /* 颜色系统 */
    --primary-color: #007bff;
    --secondary-color: #6c757d;
    --success-color: #28a745;
    --danger-color: #dc3545;
    --warning-color: #ffc107;
    --info-color: #17a2b8;
    
    /* 字体系统 */
    --font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
    --font-size-base: 14px;
    --font-size-lg: 18px;
    --font-size-sm: 12px;
    --line-height-base: 1.5;
    
    /* 间距系统 */
    --spacing-xs: 4px;
    --spacing-sm: 8px;
    --spacing-md: 16px;
    --spacing-lg: 24px;
    --spacing-xl: 32px;
    
    /* 阴影系统 */
    --shadow-sm: 0 1px 3px rgba(0,0,0,0.12);
    --shadow-md: 0 4px 6px rgba(0,0,0,0.16);
    --shadow-lg: 0 10px 25px rgba(0,0,0,0.19);
    
    /* 边框系统 */
    --border-radius-sm: 2px;
    --border-radius-md: 4px;
    --border-radius-lg: 8px;
    --border-width: 1px;
    --border-color: #dee2e6;
}

/* 深色主题 */
[data-theme="dark"] {
    --primary-color: #0d6efd;
    --secondary-color: #6c757d;
    --bg-color: #212529;
    --text-color: #ffffff;
    --border-color: #495057;
}

/* 高对比度主题 */
[data-theme="high-contrast"] {
    --primary-color: #000000;
    --secondary-color: #ffffff;
    --bg-color: #ffffff;
    --text-color: #000000;
    --border-color: #000000;
}

4.2 动态样式管理

4.2.1 样式管理器

class StyleManager {
    constructor() {
        this.styleSheets = new Map();
        this.cssVariables = new Map();
        this.themes = new Map();
        this.currentTheme = "default";
        this.init();
    }
    
    init() {
        this.setupDefaultTheme();
        this.observeSystemTheme();
    }
    
    // 创建样式表
    createStyleSheet(id, css) {
        if (this.styleSheets.has(id)) {
            this.updateStyleSheet(id, css);
            return;
        }
        
        const style = document.createElement("style");
        style.id = id;
        style.textContent = css;
        document.head.appendChild(style);
        
        this.styleSheets.set(id, style);
    }
    
    // 更新样式表
    updateStyleSheet(id, css) {
        const style = this.styleSheets.get(id);
        if (style) {
            style.textContent = css;
        }
    }
    
    // 删除样式表
    removeStyleSheet(id) {
        const style = this.styleSheets.get(id);
        if (style) {
            style.remove();
            this.styleSheets.delete(id);
        }
    }
    
    // 设置 CSS 变量
    setCSSVariable(name, value, element = document.documentElement) {
        element.style.setProperty(`--${name}`, value);
        this.cssVariables.set(name, value);
    }
    
    // 获取 CSS 变量
    getCSSVariable(name, element = document.documentElement) {
        return getComputedStyle(element).getPropertyValue(`--${name}`);
    }
    
    // 批量设置 CSS 变量
    setCSSVariables(variables, element = document.documentElement) {
        Object.entries(variables).forEach(([name, value]) => {
            this.setCSSVariable(name, value, element);
        });
    }
    
    // 注册主题
    registerTheme(name, variables) {
        this.themes.set(name, variables);
    }
    
    // 应用主题
    applyTheme(name) {
        const theme = this.themes.get(name);
        if (!theme) {
            console.warn(`主题 "${name}" 不存在`);
            return;
        }
        
        this.setCSSVariables(theme);
        this.currentTheme = name;
        document.documentElement.dataset.theme = name;
        
        // 触发主题变化事件
        document.dispatchEvent(new CustomEvent("theme-changed", {
            detail: { theme: name, variables: theme }
        }));
    }
    
    // 获取当前主题
    getCurrentTheme() {
        return this.currentTheme;
    }
    
    // 监听系统主题变化
    observeSystemTheme() {
        if (window.matchMedia) {
            const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
            darkModeQuery.addListener((e) => {
                if (this.currentTheme === "auto") {
                    this.applyTheme(e.matches ? "dark" : "light");
                }
            });
        }
    }
    
    // 设置默认主题
    setupDefaultTheme() {
        this.registerTheme("light", {
            "primary-color": "#007bff",
            "bg-color": "#ffffff",
            "text-color": "#212529",
            "border-color": "#dee2e6"
        });
        
        this.registerTheme("dark", {
            "primary-color": "#0d6efd",
            "bg-color": "#212529",
            "text-color": "#ffffff",
            "border-color": "#495057"
        });
        
        this.applyTheme("light");
    }
    
    // 生成响应式样式
    generateResponsiveCSS(breakpoints, styles) {
        let css = "";
        
        Object.entries(styles).forEach(([selector, rules]) => {
            // 基础样式
            if (rules.base) {
                css += `${selector} { ${this.rulesToCSS(rules.base)} }\n`;
            }
            
            // 响应式样式
            Object.entries(breakpoints).forEach(([name, width]) => {
                if (rules[name]) {
                    css += `@media (min-width: ${width}) {\n`;
                    css += `  ${selector} { ${this.rulesToCSS(rules[name])} }\n`;
                    css += `}\n`;
                }
            });
        });
        
        return css;
    }
    
    // 将规则对象转换为 CSS 字符串
    rulesToCSS(rules) {
        return Object.entries(rules)
            .map(([prop, value]) => `${this.camelToKebab(prop)}: ${value}`)
            .join("; ");
    }
    
    // 驼峰转短横线
    camelToKebab(str) {
        return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
    }
}

// 使用示例
const styleManager = new StyleManager();

// 注册自定义主题
styleManager.registerTheme("custom", {
    "primary-color": "#ff6b6b",
    "secondary-color": "#4ecdc4",
    "bg-color": "#f8f9fa",
    "text-color": "#343a40"
});

// 应用主题
styleManager.applyTheme("custom");

// 动态创建响应式样式
const breakpoints = {
    sm: "576px",
    md: "768px",
    lg: "992px",
    xl: "1200px"
};

const responsiveStyles = {
    ".container": {
        base: {
            padding: "var(--spacing-md)",
            margin: "0 auto"
        },
        sm: {
            maxWidth: "540px"
        },
        md: {
            maxWidth: "720px"
        },
        lg: {
            maxWidth: "960px"
        },
        xl: {
            maxWidth: "1140px"
        }
    }
};

const css = styleManager.generateResponsiveCSS(breakpoints, responsiveStyles);
styleManager.createStyleSheet("responsive", css);

4.2.2 动画系统

class AnimationManager {
    constructor() {
        this.animations = new Map();
        this.easingFunctions = new Map();
        this.setupEasingFunctions();
    }
    
    setupEasingFunctions() {
        this.easingFunctions.set("linear", t => t);
        this.easingFunctions.set("easeInQuad", t => t * t);
        this.easingFunctions.set("easeOutQuad", t => t * (2 - t));
        this.easingFunctions.set("easeInOutQuad", t => 
            t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
        );
        this.easingFunctions.set("easeInCubic", t => t * t * t);
        this.easingFunctions.set("easeOutCubic", t => (--t) * t * t + 1);
        this.easingFunctions.set("easeInOutCubic", t => 
            t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
        );
    }
    
    // 创建动画
    animate(element, properties, options = {}) {
        const config = {
            duration: options.duration || 300,
            easing: options.easing || "easeOutQuad",
            delay: options.delay || 0,
            onComplete: options.onComplete || (() => {}),
            onUpdate: options.onUpdate || (() => {})
        };
        
        const animationId = `anim_${Date.now()}_${Math.random()}`;
        
        // 获取初始值
        const startValues = {};
        const endValues = {};
        
        Object.keys(properties).forEach(prop => {
            startValues[prop] = this.getCurrentValue(element, prop);
            endValues[prop] = properties[prop];
        });
        
        const animation = {
            element,
            startValues,
            endValues,
            config,
            startTime: null,
            cancelled: false
        };
        
        this.animations.set(animationId, animation);
        
        // 延迟执行
        setTimeout(() => {
            if (!animation.cancelled) {
                this.startAnimation(animationId);
            }
        }, config.delay);
        
        return {
            cancel: () => {
                animation.cancelled = true;
                this.animations.delete(animationId);
            }
        };
    }
    
    startAnimation(animationId) {
        const animation = this.animations.get(animationId);
        if (!animation) return;
        
        const animate = (currentTime) => {
            if (animation.cancelled) return;
            
            if (!animation.startTime) {
                animation.startTime = currentTime;
            }
            
            const elapsed = currentTime - animation.startTime;
            const progress = Math.min(elapsed / animation.config.duration, 1);
            
            // 应用缓动函数
            const easingFunction = this.easingFunctions.get(animation.config.easing);
            const easedProgress = easingFunction ? easingFunction(progress) : progress;
            
            // 更新属性值
            Object.keys(animation.endValues).forEach(prop => {
                const startValue = animation.startValues[prop];
                const endValue = animation.endValues[prop];
                const currentValue = this.interpolateValue(startValue, endValue, easedProgress);
                this.setElementProperty(animation.element, prop, currentValue);
            });
            
            // 调用更新回调
            animation.config.onUpdate(easedProgress);
            
            if (progress < 1) {
                requestAnimationFrame(animate);
            } else {
                // 动画完成
                animation.config.onComplete();
                this.animations.delete(animationId);
            }
        };
        
        requestAnimationFrame(animate);
    }
    
    getCurrentValue(element, property) {
        const computedStyle = getComputedStyle(element);
        const value = computedStyle.getPropertyValue(property);
        
        // 解析数值
        if (property === "opacity") {
            return parseFloat(value) || 0;
        }
        
        if (property.includes("transform")) {
            return this.parseTransform(value);
        }
        
        // 解析像素值
        const numericValue = parseFloat(value);
        return isNaN(numericValue) ? 0 : numericValue;
    }
    
    setElementProperty(element, property, value) {
        if (property === "opacity") {
            element.style.opacity = value;
        } else if (property.startsWith("translate")) {
            const currentTransform = element.style.transform || "";
            const newTransform = this.updateTransform(currentTransform, property, value);
            element.style.transform = newTransform;
        } else {
            element.style[property] = typeof value === "number" ? `${value}px` : value;
        }
    }
    
    interpolateValue(start, end, progress) {
        if (typeof start === "number" && typeof end === "number") {
            return start + (end - start) * progress;
        }
        
        // 颜色插值
        if (typeof start === "string" && typeof end === "string") {
            if (start.startsWith("#") && end.startsWith("#")) {
                return this.interpolateColor(start, end, progress);
            }
        }
        
        return progress < 0.5 ? start : end;
    }
    
    interpolateColor(startColor, endColor, progress) {
        const start = this.hexToRgb(startColor);
        const end = this.hexToRgb(endColor);
        
        const r = Math.round(start.r + (end.r - start.r) * progress);
        const g = Math.round(start.g + (end.g - start.g) * progress);
        const b = Math.round(start.b + (end.b - start.b) * progress);
        
        return `rgb(${r}, ${g}, ${b})`;
    }
    
    hexToRgb(hex) {
        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : { r: 0, g: 0, b: 0 };
    }
    
    parseTransform(transform) {
        // 简化的 transform 解析
        const translateMatch = transform.match(/translateX?\(([^)]+)\)/);
        if (translateMatch) {
            return parseFloat(translateMatch[1]) || 0;
        }
        return 0;
    }
    
    updateTransform(currentTransform, property, value) {
        // 简化的 transform 更新
        const transforms = currentTransform.split(" ").filter(t => t);
        const newTransform = `${property}(${value}px)`;
        
        // 替换或添加新的 transform
        const existingIndex = transforms.findIndex(t => t.startsWith(property));
        if (existingIndex >= 0) {
            transforms[existingIndex] = newTransform;
        } else {
            transforms.push(newTransform);
        }
        
        return transforms.join(" ");
    }
    
    // 预定义动画
    fadeIn(element, duration = 300) {
        element.style.opacity = "0";
        return this.animate(element, { opacity: 1 }, { duration });
    }
    
    fadeOut(element, duration = 300) {
        return this.animate(element, { opacity: 0 }, { duration });
    }
    
    slideIn(element, direction = "left", duration = 300) {
        const distance = direction === "left" || direction === "right" ? 
            element.offsetWidth : element.offsetHeight;
        
        const startValue = direction === "left" ? -distance :
                          direction === "right" ? distance :
                          direction === "up" ? -distance : distance;
        
        const property = direction === "left" || direction === "right" ? 
            "translateX" : "translateY";
        
        this.setElementProperty(element, property, startValue);
        return this.animate(element, { [property]: 0 }, { duration });
    }
    
    slideOut(element, direction = "left", duration = 300) {
        const distance = direction === "left" || direction === "right" ? 
            element.offsetWidth : element.offsetHeight;
        
        const endValue = direction === "left" ? -distance :
                        direction === "right" ? distance :
                        direction === "up" ? -distance : distance;
        
        const property = direction === "left" || direction === "right" ? 
            "translateX" : "translateY";
        
        return this.animate(element, { [property]: endValue }, { duration });
    }
}

// 使用示例
const animationManager = new AnimationManager();

// 基础动画
const element = document.querySelector(".animate-me");
animationManager.animate(element, {
    translateX: 100,
    opacity: 0.5
}, {
    duration: 500,
    easing: "easeInOutQuad",
    onComplete: () => console.log("动画完成")
});

// 预定义动画
animationManager.fadeIn(element);
animationManager.slideIn(element, "right", 400);

4.3 布局系统

4.3.1 Flexbox 布局

/* Flexbox 容器 */
.flex-container {
    display: flex;
    flex-direction: row; /* row | row-reverse | column | column-reverse */
    flex-wrap: nowrap; /* nowrap | wrap | wrap-reverse */
    justify-content: flex-start; /* flex-start | flex-end | center | space-between | space-around | space-evenly */
    align-items: stretch; /* stretch | flex-start | flex-end | center | baseline */
    align-content: stretch; /* stretch | flex-start | flex-end | center | space-between | space-around */
    gap: var(--spacing-md);
}

/* Flexbox 项目 */
.flex-item {
    flex: 1; /* flex-grow flex-shrink flex-basis */
    flex-grow: 0;
    flex-shrink: 1;
    flex-basis: auto;
    align-self: auto; /* auto | flex-start | flex-end | center | baseline | stretch */
    order: 0;
}

/* 常用 Flexbox 工具类 */
.d-flex { display: flex; }
.d-inline-flex { display: inline-flex; }

.flex-row { flex-direction: row; }
.flex-column { flex-direction: column; }
.flex-row-reverse { flex-direction: row-reverse; }
.flex-column-reverse { flex-direction: column-reverse; }

.flex-wrap { flex-wrap: wrap; }
.flex-nowrap { flex-wrap: nowrap; }
.flex-wrap-reverse { flex-wrap: wrap-reverse; }

.justify-content-start { justify-content: flex-start; }
.justify-content-end { justify-content: flex-end; }
.justify-content-center { justify-content: center; }
.justify-content-between { justify-content: space-between; }
.justify-content-around { justify-content: space-around; }
.justify-content-evenly { justify-content: space-evenly; }

.align-items-start { align-items: flex-start; }
.align-items-end { align-items: flex-end; }
.align-items-center { align-items: center; }
.align-items-baseline { align-items: baseline; }
.align-items-stretch { align-items: stretch; }

.flex-fill { flex: 1 1 auto; }
.flex-grow-0 { flex-grow: 0; }
.flex-grow-1 { flex-grow: 1; }
.flex-shrink-0 { flex-shrink: 0; }
.flex-shrink-1 { flex-shrink: 1; }

4.3.2 Grid 布局

/* Grid 容器 */
.grid-container {
    display: grid;
    grid-template-columns: repeat(12, 1fr); /* 12列网格 */
    grid-template-rows: auto;
    grid-gap: var(--spacing-md);
    grid-auto-flow: row; /* row | column | row dense | column dense */
}

/* Grid 项目 */
.grid-item {
    grid-column: span 1; /* 占用列数 */
    grid-row: span 1; /* 占用行数 */
    grid-area: auto; /* 网格区域 */
}

/* 命名网格线 */
.layout-grid {
    display: grid;
    grid-template-columns: 
        [sidebar-start] 250px 
        [sidebar-end main-start] 1fr 
        [main-end];
    grid-template-rows: 
        [header-start] 60px 
        [header-end content-start] 1fr 
        [content-end footer-start] 40px 
        [footer-end];
    grid-template-areas: 
        "sidebar header"
        "sidebar main"
        "sidebar footer";
}

.sidebar { grid-area: sidebar; }
.header { grid-area: header; }
.main { grid-area: main; }
.footer { grid-area: footer; }

/* 响应式网格 */
.responsive-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: var(--spacing-md);
}

/* Grid 工具类 */
.d-grid { display: grid; }
.d-inline-grid { display: inline-grid; }

.grid-cols-1 { grid-template-columns: repeat(1, 1fr); }
.grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid-cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid-cols-4 { grid-template-columns: repeat(4, 1fr); }
.grid-cols-6 { grid-template-columns: repeat(6, 1fr); }
.grid-cols-12 { grid-template-columns: repeat(12, 1fr); }

.col-span-1 { grid-column: span 1; }
.col-span-2 { grid-column: span 2; }
.col-span-3 { grid-column: span 3; }
.col-span-4 { grid-column: span 4; }
.col-span-6 { grid-column: span 6; }
.col-span-12 { grid-column: span 12; }

.row-span-1 { grid-row: span 1; }
.row-span-2 { grid-row: span 2; }
.row-span-3 { grid-row: span 3; }

4.3.3 Sciter 特有布局

/* Flow 布局 - Sciter 特有 */
.flow-container {
    flow: horizontal; /* horizontal | vertical | horizontal-wrap | vertical-wrap */
    width: 100%;
    height: 100%;
}

.flow-item {
    width: max-content;
    height: max-content;
}

/* Stack 布局 - Sciter 特有 */
.stack-container {
    flow: stack;
    width: 100%;
    height: 100%;
}

.stack-item {
    position: absolute;
    width: 100%;
    height: 100%;
}

/* Table 布局 - Sciter 增强 */
.table-container {
    flow: table;
    width: 100%;
    table-layout: fixed;
}

.table-row {
    flow: table-row;
}

.table-cell {
    flow: table-cell;
    vertical-align: middle;
    padding: var(--spacing-sm);
}

/* 自适应布局 */
.adaptive-layout {
    flow: horizontal;
    width: 100%;
}

.adaptive-layout > * {
    width: max-content;
    min-width: 0;
}

.adaptive-layout > .fill {
    width: *; /* Sciter 特有:占用剩余空间 */
}

4.3.4 响应式布局管理器

class ResponsiveLayoutManager {
    constructor() {
        this.breakpoints = {
            xs: 0,
            sm: 576,
            md: 768,
            lg: 992,
            xl: 1200,
            xxl: 1400
        };
        
        this.currentBreakpoint = this.getCurrentBreakpoint();
        this.observers = new Set();
        this.setupMediaQueries();
    }
    
    setupMediaQueries() {
        Object.entries(this.breakpoints).forEach(([name, width]) => {
            if (width > 0) {
                const mediaQuery = window.matchMedia(`(min-width: ${width}px)`);
                mediaQuery.addListener(() => {
                    const newBreakpoint = this.getCurrentBreakpoint();
                    if (newBreakpoint !== this.currentBreakpoint) {
                        this.currentBreakpoint = newBreakpoint;
                        this.notifyObservers(newBreakpoint);
                    }
                });
            }
        });
    }
    
    getCurrentBreakpoint() {
        const width = window.innerWidth;
        let currentBreakpoint = "xs";
        
        Object.entries(this.breakpoints).forEach(([name, minWidth]) => {
            if (width >= minWidth) {
                currentBreakpoint = name;
            }
        });
        
        return currentBreakpoint;
    }
    
    onBreakpointChange(callback) {
        this.observers.add(callback);
        return () => this.observers.delete(callback);
    }
    
    notifyObservers(breakpoint) {
        this.observers.forEach(callback => {
            try {
                callback(breakpoint, this.breakpoints[breakpoint]);
            } catch (error) {
                console.error("响应式回调错误:", error);
            }
        });
    }
    
    // 根据断点应用不同的布局
    applyResponsiveLayout(element, layouts) {
        const layout = layouts[this.currentBreakpoint] || layouts.xs || {};
        
        Object.entries(layout).forEach(([property, value]) => {
            if (property === "class") {
                // 移除所有响应式类
                Object.values(layouts).forEach(l => {
                    if (l.class) {
                        element.classList.remove(l.class);
                    }
                });
                // 添加当前断点的类
                element.classList.add(value);
            } else {
                element.style[property] = value;
            }
        });
    }
    
    // 创建响应式组件
    createResponsiveComponent(element, config) {
        const applyLayout = () => {
            this.applyResponsiveLayout(element, config.layouts || {});
            
            if (config.onBreakpointChange) {
                config.onBreakpointChange(this.currentBreakpoint, element);
            }
        };
        
        // 初始应用
        applyLayout();
        
        // 监听变化
        const unsubscribe = this.onBreakpointChange(applyLayout);
        
        return {
            destroy: unsubscribe,
            getCurrentBreakpoint: () => this.currentBreakpoint,
            updateConfig: (newConfig) => {
                Object.assign(config, newConfig);
                applyLayout();
            }
        };
    }
}

// 使用示例
const layoutManager = new ResponsiveLayoutManager();

// 监听断点变化
layoutManager.onBreakpointChange((breakpoint, width) => {
    console.log(`断点变化: ${breakpoint} (${width}px)`);
    document.body.dataset.breakpoint = breakpoint;
});

// 创建响应式导航
const nav = document.querySelector(".navbar");
const responsiveNav = layoutManager.createResponsiveComponent(nav, {
    layouts: {
        xs: {
            class: "navbar-mobile",
            flexDirection: "column"
        },
        md: {
            class: "navbar-desktop",
            flexDirection: "row"
        }
    },
    onBreakpointChange: (breakpoint, element) => {
        if (breakpoint === "xs") {
            element.querySelector(".navbar-toggle").style.display = "block";
        } else {
            element.querySelector(".navbar-toggle").style.display = "none";
        }
    }
});

// 创建响应式网格
const grid = document.querySelector(".responsive-grid");
const responsiveGrid = layoutManager.createResponsiveComponent(grid, {
    layouts: {
        xs: {
            gridTemplateColumns: "1fr"
        },
        sm: {
            gridTemplateColumns: "repeat(2, 1fr)"
        },
        md: {
            gridTemplateColumns: "repeat(3, 1fr)"
        },
        lg: {
            gridTemplateColumns: "repeat(4, 1fr)"
        }
    }
});

4.4 实践练习

4.4.1 主题切换器

class ThemeSwitcher {
    constructor(container) {
        this.container = container;
        this.styleManager = new StyleManager();
        this.currentTheme = localStorage.getItem("theme") || "auto";
        this.init();
    }
    
    init() {
        this.setupThemes();
        this.createUI();
        this.setupEvents();
        this.applyTheme(this.currentTheme);
    }
    
    setupThemes() {
        // 注册主题
        this.styleManager.registerTheme("light", {
            "bg-color": "#ffffff",
            "text-color": "#212529",
            "primary-color": "#007bff",
            "secondary-color": "#6c757d",
            "border-color": "#dee2e6",
            "shadow-color": "rgba(0,0,0,0.1)"
        });
        
        this.styleManager.registerTheme("dark", {
            "bg-color": "#212529",
            "text-color": "#ffffff",
            "primary-color": "#0d6efd",
            "secondary-color": "#6c757d",
            "border-color": "#495057",
            "shadow-color": "rgba(255,255,255,0.1)"
        });
        
        this.styleManager.registerTheme("sepia", {
            "bg-color": "#f4f3e8",
            "text-color": "#5c4b37",
            "primary-color": "#8b4513",
            "secondary-color": "#a0522d",
            "border-color": "#d2b48c",
            "shadow-color": "rgba(92,75,55,0.1)"
        });
        
        this.styleManager.registerTheme("high-contrast", {
            "bg-color": "#000000",
            "text-color": "#ffffff",
            "primary-color": "#ffff00",
            "secondary-color": "#00ffff",
            "border-color": "#ffffff",
            "shadow-color": "rgba(255,255,255,0.3)"
        });
    }
    
    createUI() {
        this.container.innerHTML = `
            <div class="theme-switcher">
                <label class="theme-switcher-label">主题设置</label>
                <div class="theme-options">
                    <button class="theme-option" data-theme="auto">
                        <span class="theme-icon">🌓</span>
                        <span class="theme-name">自动</span>
                    </button>
                    <button class="theme-option" data-theme="light">
                        <span class="theme-icon">☀️</span>
                        <span class="theme-name">浅色</span>
                    </button>
                    <button class="theme-option" data-theme="dark">
                        <span class="theme-icon">🌙</span>
                        <span class="theme-name">深色</span>
                    </button>
                    <button class="theme-option" data-theme="sepia">
                        <span class="theme-icon">📜</span>
                        <span class="theme-name">护眼</span>
                    </button>
                    <button class="theme-option" data-theme="high-contrast">
                        <span class="theme-icon">🔆</span>
                        <span class="theme-name">高对比</span>
                    </button>
                </div>
                <div class="theme-preview">
                    <div class="preview-card">
                        <div class="preview-header">预览</div>
                        <div class="preview-content">
                            <p>这是主题预览文本</p>
                            <button class="preview-button">按钮</button>
                        </div>
                    </div>
                </div>
            </div>
        `;
        
        // 添加样式
        this.styleManager.createStyleSheet("theme-switcher", `
            .theme-switcher {
                padding: var(--spacing-lg);
                background: var(--bg-color);
                border: 1px solid var(--border-color);
                border-radius: var(--border-radius-md);
                box-shadow: var(--shadow-md);
            }
            
            .theme-switcher-label {
                display: block;
                font-weight: bold;
                margin-bottom: var(--spacing-md);
                color: var(--text-color);
            }
            
            .theme-options {
                display: grid;
                grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
                gap: var(--spacing-sm);
                margin-bottom: var(--spacing-lg);
            }
            
            .theme-option {
                display: flex;
                flex-direction: column;
                align-items: center;
                padding: var(--spacing-md);
                border: 2px solid var(--border-color);
                border-radius: var(--border-radius-md);
                background: var(--bg-color);
                color: var(--text-color);
                cursor: pointer;
                transition: all 0.2s ease;
            }
            
            .theme-option:hover {
                border-color: var(--primary-color);
                transform: translateY(-2px);
            }
            
            .theme-option.active {
                border-color: var(--primary-color);
                background: var(--primary-color);
                color: white;
            }
            
            .theme-icon {
                font-size: 24px;
                margin-bottom: var(--spacing-xs);
            }
            
            .theme-name {
                font-size: 12px;
                font-weight: 500;
            }
            
            .theme-preview {
                border: 1px solid var(--border-color);
                border-radius: var(--border-radius-md);
                overflow: hidden;
            }
            
            .preview-card {
                background: var(--bg-color);
                color: var(--text-color);
            }
            
            .preview-header {
                padding: var(--spacing-md);
                background: var(--primary-color);
                color: white;
                font-weight: bold;
            }
            
            .preview-content {
                padding: var(--spacing-md);
            }
            
            .preview-button {
                padding: var(--spacing-sm) var(--spacing-md);
                background: var(--primary-color);
                color: white;
                border: none;
                border-radius: var(--border-radius-sm);
                cursor: pointer;
                margin-top: var(--spacing-sm);
            }
        `);
    }
    
    setupEvents() {
        this.container.on("click", ".theme-option", (event) => {
            const theme = event.currentTarget.dataset.theme;
            this.applyTheme(theme);
        });
        
        // 监听系统主题变化
        if (window.matchMedia) {
            const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
            darkModeQuery.addListener(() => {
                if (this.currentTheme === "auto") {
                    this.applyAutoTheme();
                }
            });
        }
    }
    
    applyTheme(theme) {
        this.currentTheme = theme;
        localStorage.setItem("theme", theme);
        
        // 更新按钮状态
        this.container.querySelectorAll(".theme-option").forEach(btn => {
            btn.classList.toggle("active", btn.dataset.theme === theme);
        });
        
        if (theme === "auto") {
            this.applyAutoTheme();
        } else {
            this.styleManager.applyTheme(theme);
        }
        
        // 添加过渡动画
        document.body.style.transition = "background-color 0.3s ease, color 0.3s ease";
        setTimeout(() => {
            document.body.style.transition = "";
        }, 300);
    }
    
    applyAutoTheme() {
        const prefersDark = window.matchMedia && 
            window.matchMedia("(prefers-color-scheme: dark)").matches;
        this.styleManager.applyTheme(prefersDark ? "dark" : "light");
    }
}

// 使用示例
document.ready = function() {
    const themeSwitcher = new ThemeSwitcher(document.querySelector(".theme-switcher-container"));
};

4.5 本章小结

4.5.1 核心概念

  • CSS 扩展:Sciter 在标准 CSS 基础上的增强特性
  • 动态样式:JavaScript 控制的样式管理和主题系统
  • 布局系统:Flexbox、Grid 和 Sciter 特有的 Flow 布局
  • 响应式设计:适配不同屏幕尺寸的布局策略

4.5.2 技术要点

  1. 性能优化:合理使用 CSS 变量和样式缓存
  2. 主题管理:系统化的主题切换和持久化
  3. 动画优化:使用 requestAnimationFrame 和硬件加速
  4. 响应式布局:移动优先的设计理念

4.5.3 最佳实践

  • 使用 CSS 变量构建可维护的主题系统
  • 优先使用 Flexbox 和 Grid 进行布局
  • 实现平滑的主题切换动画
  • 提供完整的响应式支持

4.5.4 下一章预告

下一章将学习 组件开发与模块化,包括: - 组件架构设计 - 生命周期管理 - 组件通信机制 - 模块化开发模式

通过本章的学习,你已经掌握了完整的样式和布局技能,能够创建美观且响应式的用户界面。