You've already forked Pandona-Engine
546 lines
9.5 KiB
Markdown
546 lines
9.5 KiB
Markdown
# 样式处理
|
||
|
||
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引擎的样式处理机制。在下一章节中,我们将探讨事件处理的相关内容。 |