Files
Pandona-Engine/guide/essentials/styling.md
2025-10-03 16:49:53 +08:00

9.5 KiB
Raw Blame History

样式处理

PE引擎使用Less作为CSS预处理器提供了强大而灵活的样式管理方案。每个场景可以有对应的样式文件确保样式的作用域隔离和可维护性。

Less预处理器

Less是一种向后兼容CSS的语言扩展为CSS添加了变量、嵌套、混合等功能让样式编写更加高效和可维护。

变量

使用变量来存储常用的值:

// 定义变量
@primary-color: #3498db;
@secondary-color: #2ecc71;
@font-size-large: 18px;
@border-radius: 4px;

// 使用变量
.header {
  background-color: @primary-color;
  font-size: @font-size-large;
  border-radius: @border-radius;
}

嵌套

Less支持嵌套语法让样式结构更清晰

.navbar {
  background-color: #34495e;
  
  // 嵌套选择器
  .nav-item {
    color: white;
    padding: 10px 15px;
    
    // 伪类嵌套
    &:hover {
      background-color: #2c3e50;
    }
    
    // 子元素嵌套
    .icon {
      margin-right: 5px;
    }
  }
}

混合Mixins

混合允许你定义可复用的样式块:

// 定义混合
.border-radius(@radius: 4px) {
  border-radius: @radius;
  -webkit-border-radius: @radius;
  -moz-border-radius: @radius;
}

.box-shadow(@x: 0, @y: 0, @blur: 10px, @color: rgba(0,0,0,0.1)) {
  box-shadow: @arguments;
  -webkit-box-shadow: @arguments;
  -moz-box-shadow: @arguments;
}

// 使用混合
.button {
  .border-radius(8px);
  .box-shadow(0, 2px, 5px, rgba(0,0,0,0.2));
}

函数

Less提供了丰富的内置函数

// 颜色函数
.button {
  background-color: #3498db;
  // 变暗10%
  &:hover {
    background-color: darken(#3498db, 10%);
  }
  // 变亮10%
  &:active {
    background-color: lighten(#3498db, 10%);
  }
}

// 数学函数
.progress-bar {
  width: percentage(0.5); // 50%
  height: unit(20, px);   // 20px
}

场景样式隔离

PE引擎确保每个场景的样式只作用于该场景内的元素避免样式冲突。

样式作用域

每个场景的样式文件会自动包装在特定的选择器中:

/* scenes/home/index.less */
// 原始样式
.welcome-box {
  background-color: #3498db;
}

// 实际生成的样式
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
  background-color: #3498db;
}

全局样式

你可以在项目根目录创建全局样式文件:

/* styles/base.less */
// 全局变量
@primary-color: #3498db;
@secondary-color: #2ecc71;
@text-color: #2c3e50;
@bg-color: #ecf0f1;

// 全局重置样式
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Arial', sans-serif;
  color: @text-color;
  background-color: @bg-color;
}

// 全局工具类
.text-center {
  text-align: center;
}

.flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

在主文件中引入全局样式:

// main.js
import './styles/base.less'
import PE from 'pe-engine'

样式组织最佳实践

按功能组织样式

/* styles/components.less */
// 按钮组件
.btn {
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  
  &.btn-primary {
    background-color: @primary-color;
    color: white;
    
    &:hover {
      background-color: darken(@primary-color, 10%);
    }
  }
  
  &.btn-secondary {
    background-color: @secondary-color;
    color: white;
    
    &:hover {
      background-color: darken(@secondary-color, 10%);
    }
  }
}

// 卡片组件
.card {
  background-color: white;
  border-radius: 8px;
  padding: 20px;
  .box-shadow();
  
  .card-header {
    font-size: 18px;
    font-weight: bold;
    margin-bottom: 15px;
  }
  
  .card-body {
    font-size: 14px;
    line-height: 1.5;
  }
}

响应式样式

使用媒体查询创建响应式设计:

/* styles/responsive.less */
// 响应式断点变量
@screen-sm: 576px;
@screen-md: 768px;
@screen-lg: 992px;
@screen-xl: 1200px;

// 媒体查询混合
.responsive(@rules) {
  @media (max-width: @screen-sm) {
    @rules();
  }
}

.responsive-md(@rules) {
  @media (min-width: @screen-md) {
    @rules();
  }
}

// 响应式网格系统
.container {
  width: 100%;
  padding: 0 15px;
  margin: 0 auto;
  
  .responsive({
    max-width: 100%;
  });
  
  .responsive-md({
    max-width: 750px;
  });
  
  @media (min-width: @screen-lg) {
    max-width: 970px;
  }
  
  @media (min-width: @screen-xl) {
    max-width: 1170px;
  }
}

动态样式

PE支持在运行时动态修改样式

通过JavaScript修改样式

// 获取场景元素
const element = game.getSceneElement('my-element')

// 修改单个样式属性
element.setStyle('backgroundColor', '#e74c3c')

// 修改多个样式属性
element.setStyles({
  backgroundColor: '#3498db',
  color: 'white',
  fontSize: '18px'
})

// 添加/移除CSS类
element.addClass('active')
element.removeClass('inactive')

条件样式

在场景脚本中根据条件动态应用样式:

<sence>
  <box class="status-box" :class="{ 'status-online': isOnline, 'status-offline': !isOnline }">
    状态: {{ isOnline ? '在线' : '离线' }}
  </box>
</sence>

<script>
let isOnline = false

// 模拟状态变化
setInterval(() => {
  isOnline = !isOnline
}, 5000)

onShow(() => {
  // 根据状态更新样式
  const statusBox = game.getSceneElement('status-box')
  if (isOnline) {
    statusBox.setStyles({
      backgroundColor: '#2ecc71',
      color: 'white'
    })
  } else {
    statusBox.setStyles({
      backgroundColor: '#e74c3c',
      color: 'white'
    })
  }
})
</script>

CSS自定义属性CSS变量

PE也支持使用CSS自定义属性

/* styles/theme.less */
:root {
  --primary-color: #3498db;
  --secondary-color: #2ecc71;
  --text-color: #2c3e50;
  --bg-color: #ecf0f1;
}

.themed-element {
  background-color: var(--primary-color);
  color: var(--text-color);
  
  &:hover {
    background-color: var(--secondary-color);
  }
}

样式调试

开发者工具

在浏览器开发者工具中你可以看到PE为每个场景生成的特定选择器

/* 实际生成的样式 */
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
  background-color: #3498db;
}

样式覆盖

如果需要覆盖场景样式,可以使用更具体的选择器:

/* 覆盖特定场景的样式 */
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
  background-color: #e74c3c !important;
}

完整示例

以下是一个完整的样式处理示例:

styles/variables.less

// 颜色变量
@colors: {
  primary: #3498db;
  secondary: #2ecc71;
  success: #27ae60;
  warning: #f39c12;
  danger: #e74c3c;
  info: #3498db;
  light: #f8f9fa;
  dark: #343a40;
}

// 尺寸变量
@sizes: {
  xs: 0.25rem;
  sm: 0.5rem;
  md: 1rem;
  lg: 1.5rem;
  xl: 3rem;
}

// 字体变量
@fonts: {
  family: 'Helvetica Neue', Arial, sans-serif;
  size-base: 16px;
  size-sm: 0.875rem;
  size-lg: 1.25rem;
  weight-normal: 400;
  weight-bold: 700;
}

styles/mixins.less

// 清除浮动
.clearfix() {
  &::after {
    display: block;
    content: "";
    clear: both;
  }
}

// 文本省略
.text-ellipsis() {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

// 三角形
.triangle(@direction: down, @size: 5px, @color: #000) {
  width: 0;
  height: 0;
  border-style: solid;
  
  when (@direction = up) {
    border-width: 0 @size @size @size;
    border-color: transparent transparent @color transparent;
  }
  
  when (@direction = down) {
    border-width: @size @size 0 @size;
    border-color: @color transparent transparent transparent;
  }
  
  when (@direction = left) {
    border-width: @size @size @size 0;
    border-color: transparent @color transparent transparent;
  }
  
  when (@direction = right) {
    border-width: @size 0 @size @size;
    border-color: transparent transparent transparent @color;
  }
}

scenes/dashboard/index.less

@import '../../styles/variables.less';
@import '../../styles/mixins.less';

.dashboard {
  padding: @sizes[md];
  .clearfix();
  
  .header {
    margin-bottom: @sizes[lg];
    
    .title {
      font-size: @fonts[size-lg];
      font-weight: @fonts[weight-bold];
      color: @colors[dark];
      .text-ellipsis();
    }
    
    .subtitle {
      font-size: @fonts[size-sm];
      color: lighten(@colors[dark], 30%);
    }
  }
  
  .stats-container {
    display: flex;
    flex-wrap: wrap;
    gap: @sizes[md];
    margin-bottom: @sizes[lg];
    
    .stat-card {
      flex: 1;
      min-width: 200px;
      background-color: white;
      border-radius: @sizes[sm];
      padding: @sizes[md];
      .box-shadow(0, 2px, 8px, rgba(0,0,0,0.1));
      
      .stat-value {
        font-size: 2rem;
        font-weight: @fonts[weight-bold];
        color: @colors[primary];
        margin-bottom: @sizes[sm];
      }
      
      .stat-label {
        font-size: @fonts[size-sm];
        color: lighten(@colors[dark], 40%);
      }
    }
  }
  
  .chart-container {
    background-color: white;
    border-radius: @sizes[sm];
    padding: @sizes[md];
    .box-shadow(0, 2px, 8px, rgba(0,0,0,0.1));
    
    .chart-title {
      font-size: @fonts[size-base];
      font-weight: @fonts[weight-bold];
      margin-bottom: @sizes[md];
    }
  }
  
  @media (max-width: 768px) {
    .stats-container {
      flex-direction: column;
      
      .stat-card {
        min-width: 100%;
      }
    }
  }
}

通过以上内容你已经了解了PE引擎的样式处理机制。在下一章节中我们将探讨事件处理的相关内容。