You've already forked Pandona-Engine
初始化提交
This commit is contained in:
546
guide/essentials/styling.md
Normal file
546
guide/essentials/styling.md
Normal file
@@ -0,0 +1,546 @@
|
||||
# 样式处理
|
||||
|
||||
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引擎的样式处理机制。在下一章节中,我们将探讨事件处理的相关内容。
|
||||
Reference in New Issue
Block a user