Files
2025-10-03 16:49:53 +08:00

546 lines
9.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 样式处理
PE引擎使用Less作为CSS预处理器提供了强大而灵活的样式管理方案。每个场景可以有对应的样式文件确保样式的作用域隔离和可维护性。
## Less预处理器
Less是一种向后兼容CSS的语言扩展为CSS添加了变量、嵌套、混合等功能让样式编写更加高效和可维护。
### 变量
使用变量来存储常用的值:
```less
// 定义变量
@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支持嵌套语法让样式结构更清晰
```less
.navbar {
background-color: #34495e;
// 嵌套选择器
.nav-item {
color: white;
padding: 10px 15px;
// 伪类嵌套
&:hover {
background-color: #2c3e50;
}
// 子元素嵌套
.icon {
margin-right: 5px;
}
}
}
```
### 混合Mixins
混合允许你定义可复用的样式块:
```less
// 定义混合
.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提供了丰富的内置函数
```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引擎确保每个场景的样式只作用于该场景内的元素避免样式冲突。
### 样式作用域
每个场景的样式文件会自动包装在特定的选择器中:
```less
/* scenes/home/index.less */
// 原始样式
.welcome-box {
background-color: #3498db;
}
// 实际生成的样式
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
background-color: #3498db;
}
```
### 全局样式
你可以在项目根目录创建全局样式文件:
```less
/* 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;
}
```
在主文件中引入全局样式:
```javascript
// main.js
import './styles/base.less'
import PE from 'pe-engine'
```
## 样式组织最佳实践
### 按功能组织样式
```less
/* 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;
}
}
```
### 响应式样式
使用媒体查询创建响应式设计:
```less
/* 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修改样式
```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')
```
### 条件样式
在场景脚本中根据条件动态应用样式:
```html
<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自定义属性
```less
/* 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为每个场景生成的特定选择器
```css
/* 实际生成的样式 */
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
background-color: #3498db;
}
```
### 样式覆盖
如果需要覆盖场景样式,可以使用更具体的选择器:
```less
/* 覆盖特定场景的样式 */
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
background-color: #e74c3c !important;
}
```
## 完整示例
以下是一个完整的样式处理示例:
### styles/variables.less
```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
```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
```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引擎的样式处理机制。在下一章节中我们将探讨事件处理的相关内容。