初始化提交

This commit is contained in:
2025-10-03 16:49:53 +08:00
parent 157ca32e2d
commit bdd67a65fa
1066 changed files with 373311 additions and 261 deletions

View File

@@ -0,0 +1,84 @@
# 基础教程
在本章节中你将学习PE引擎的核心概念。我们假定你已经完成了[快速上手](../quick-start)指南并对HTML、CSS和JavaScript有基本的了解。
## 目录
- [创建应用实例](./application)
- [元素系统](./elements)
- [场景系统](./scenes)
- [样式处理](./styling)
- [事件处理](./event-handling)
- [生命周期](./lifecycle)
## 什么是PE引擎
PEPandona Engine是一个现代化的前端应用开发框架专注于提供简洁的API和高效的开发体验。它采用编译时架构支持基于路由表的场景管理让开发者能够快速构建交互式Web应用。
PE的核心特性包括
1. **基于路由的场景管理**:通过简单的路由配置,轻松管理应用的不同状态和视图。
2. **声明式模板语法**使用类似HTML的模板语法让代码更直观易懂。
3. **组件化开发**将UI拆分为独立的可复用组件提高代码的可维护性。
4. **内置动画系统**提供丰富的动画API轻松实现流畅的交互动画。
5. **响应式数据绑定**自动追踪数据变化确保UI与数据保持同步。
6. **强大的样式系统**支持CSS预处理器提供灵活的样式管理方案。
## 核心概念概览
在深入了解每个概念之前让我们先快速了解一下PE引擎的核心概念
### 应用实例
每个PE应用都从创建一个应用实例开始
```javascript
import PE from 'pe-engine'
const game = PE.create()
```
这个实例包含了所有PE引擎的功能是整个应用的核心。
### 元素系统
PE提供了丰富的内置元素类型包括
- `sprite`:精灵元素,可用于显示图像或创建基本图形
- `box`:盒子元素,用于创建矩形区域
- `text`:文本元素,用于显示文本内容
- `html`HTML元素用于嵌入原生HTML内容
- `svg`SVG元素用于创建矢量图形
### 场景系统
场景是PE应用的基本构建块。每个场景对应一个路由路径包含
- 模板定义(`<sence>`标签)
- 样式定义(`.less`文件)
- 逻辑代码(`<script>`标签)
### 样式处理
PE使用Less作为CSS预处理器每个场景可以有对应的样式文件。样式会自动作用于场景内的元素避免全局样式污染。
### 事件处理
PE提供简洁的事件绑定语法通过`@`前缀可以直接在模板中绑定事件处理器:
```html
<box @click="handleClick">点击我</box>
```
### 生命周期
每个场景都有完整的生命周期钩子,包括:
- `onLoad`:场景加载时调用
- `onShow`:场景显示时调用
- `onHide`:场景隐藏时调用
- `onDestory`:场景销毁时调用
这些钩子让你能够在适当的时机执行初始化、清理等操作。
在接下来的章节中我们将深入探讨这些概念帮助你掌握PE引擎的使用方法。

View File

@@ -0,0 +1,194 @@
# 创建应用实例
在PE引擎中所有应用都从创建一个应用实例开始。这个实例是整个应用的核心包含了引擎的所有功能。
## 创建应用实例
要创建一个PE应用实例你需要从`pe-engine`包中导入PE对象然后调用`PE.create()`方法:
```javascript
import PE from 'pe-engine'
// 创建应用实例
const game = PE.create()
```
`PE.create()`方法会返回一个引擎实例该实例包含了所有PE引擎的功能包括元素创建、场景管理、动画系统等。
## 应用配置
在创建应用实例时,你可以传入配置选项来自定义应用的行为:
```javascript
import PE from 'pe-engine'
// 带配置的应用实例
const game = PE.create({
width: 1024, // 舞台宽度默认800
height: 768, // 舞台高度默认600
background: '#ffffff', // 舞台背景色,默认'#f0f0f0'
fps: 60 // 帧率默认60
})
```
### 配置选项说明
| 选项 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| width | number | 800 | 应用舞台的宽度(像素) |
| height | number | 600 | 应用舞台的高度(像素) |
| background | string | '#f0f0f0' | 舞台背景色 |
| fps | number | 60 | 应用的帧率 |
## 应用实例的核心功能
创建的应用实例提供了丰富的API来管理你的应用
### 元素创建
使用`create`方法可以创建各种元素:
```javascript
// 创建精灵
const sprite = game.create('sprite')
sprite.setPosition(100, 100)
sprite.setSize(100, 100)
// 创建盒子
const box = game.create('box', {
x: 200,
y: 200,
width: 150,
height: 100
})
// 创建文本
const text = game.create('text', {
text: 'Hello PE!',
x: 300,
y: 300
})
```
### 场景管理
应用实例提供了场景管理功能:
```javascript
// 切换到指定路径的场景
PE.navigateTo('/about')
// 预加载场景
game.preloadScenes(['/home', '/about', '/contact'])
```
### 动画系统
应用实例集成了动画系统:
```javascript
// 创建补间动画
game.tween(sprite.element)
.to({ left: '500px', top: '500px' }, 1000)
.easing('easeInOutQuad')
.start()
// 创建帧动画
game.createFrameAnimation('walk', [
'img/walk1.png',
'img/walk2.png',
'img/walk3.png'
], 10)
game.playFrameAnimation(sprite, 'walk', true)
```
### 资源管理
应用实例提供了资源加载和管理功能:
```javascript
// 加载图像资源
const image = await game.load('path/to/image.png')
// 加载音频资源
game.loadSound('jump', 'path/to/jump.mp3')
game.loadMusic('bgm', 'path/to/background.mp3')
```
### 摄像机控制
应用实例支持摄像机系统:
```javascript
// 设置摄像机位置
game.setCameraPosition(100, 100)
// 跟随目标
game.follow(playerSprite)
// 摄像机震动效果
game.shakeCamera(10, 500)
```
## 完整示例
以下是一个完整的应用实例创建和使用示例:
```javascript
import PE from 'pe-engine'
// 创建应用实例
const game = PE.create({
width: 800,
height: 600,
background: '#2c3e50'
})
// 创建一个精灵
const player = game.create('sprite')
player.setPosition(100, 100)
player.setSize(50, 50)
// 为精灵添加样式
player.element.style.backgroundColor = '#3498db'
player.element.style.borderRadius = '50%'
// 添加点击事件
player.element.addEventListener('click', () => {
// 播放动画
game.tween(player.element)
.to({
left: Math.random() * 700 + 'px',
top: Math.random() * 500 + 'px'
}, 500)
.easing('easeOutQuad')
.start()
})
// 添加键盘控制
document.addEventListener('keydown', (e) => {
const speed = 10
const pos = {
left: parseInt(player.element.style.left) || 0,
top: parseInt(player.element.style.top) || 0
}
switch(e.key) {
case 'ArrowUp':
player.setPosition(pos.left, pos.top - speed)
break
case 'ArrowDown':
player.setPosition(pos.left, pos.top + speed)
break
case 'ArrowLeft':
player.setPosition(pos.left - speed, pos.top)
break
case 'ArrowRight':
player.setPosition(pos.left + speed, pos.top)
break
}
})
```
通过以上内容你已经了解了如何创建和配置PE应用实例以及如何使用其核心功能。在下一章节中我们将深入探讨PE的元素系统。

View File

@@ -0,0 +1,303 @@
# 元素系统
PE引擎提供了丰富的内置元素类型让你能够快速创建各种UI组件和视觉元素。元素是构成PE应用界面的基本单位。
## 元素类型概览
PE引擎目前支持以下几种内置元素类型
1. **Sprite精灵** - 用于显示图像或创建基本图形
2. **Box盒子** - 用于创建矩形区域
3. **Text文本** - 用于显示文本内容
4. **HTML** - 用于嵌入原生HTML内容
5. **SVG** - 用于创建矢量图形
## 创建元素
在PE中所有元素都通过应用实例的`create`方法创建:
```javascript
// 通用创建方法
const element = game.create(type, options)
```
### Sprite精灵
Sprite是最常用的元素类型可以用来显示图像或创建基本图形。
```javascript
// 创建一个默认的sprite
const sprite = game.create('sprite')
// 创建带配置的sprite
const spriteWithConfig = game.create('sprite', {
x: 100,
y: 100,
width: 50,
height: 50
})
// 设置位置和尺寸
sprite.setPosition(200, 200)
sprite.setSize(100, 100)
// 添加样式
sprite.element.style.backgroundColor = '#3498db'
sprite.element.style.borderRadius = '50%'
```
### Box盒子
Box元素用于创建矩形区域常用于创建按钮、面板等UI组件。
```javascript
// 创建一个box
const box = game.create('box', {
x: 100,
y: 100,
width: 200,
height: 100
})
// Box元素的DOM元素可以直接操作样式
box.element.style.backgroundColor = '#e74c3c'
box.element.style.borderRadius = '8px'
```
### Text文本
Text元素用于显示文本内容。
```javascript
// 创建文本元素
const text = game.create('text', {
text: 'Hello PE!',
x: 100,
y: 100
})
// 更新文本内容
text.setText('新的文本内容')
// 设置文本样式
text.element.style.color = '#ffffff'
text.element.style.fontSize = '24px'
text.element.style.fontWeight = 'bold'
```
### HTML元素
HTML元素允许你嵌入原生HTML内容。
```javascript
// 创建HTML元素
const htmlElement = game.create('html', {
html: '<div class="custom-html">这是HTML内容</div>',
x: 100,
y: 100
})
// 更新HTML内容
htmlElement.setHtml('<div class="updated-html">更新的内容</div>')
```
### SVG元素
SVG元素用于创建矢量图形。
```javascript
// 创建SVG元素
const svgElement = game.create('svg', {
x: 100,
y: 100,
width: 200,
height: 200,
content: '<circle cx="100" cy="100" r="50" fill="#3498db" />'
})
```
## 元素的通用方法和属性
所有PE元素都有一些通用的方法和属性
### 位置和尺寸
```javascript
// 设置位置
element.setPosition(100, 200)
// 设置尺寸
element.setSize(300, 150)
// 获取位置
const x = element.getX()
const y = element.getY()
// 获取尺寸
const width = element.getWidth()
const height = element.getHeight()
```
### 样式操作
```javascript
// 设置单个样式属性
element.setStyle('backgroundColor', '#3498db')
// 设置多个样式属性
element.setStyles({
backgroundColor: '#3498db',
borderRadius: '8px',
border: '2px solid #2980b9'
})
// 添加CSS类
element.addClass('my-class')
// 移除CSS类
element.removeClass('my-class')
// 检查是否包含CSS类
if (element.hasClass('my-class')) {
// 执行相应操作
}
```
### 显示和隐藏
```javascript
// 隐藏元素
element.hide()
// 显示元素
element.show()
// 切换显示状态
element.toggle()
```
## 在场景模板中使用元素
在PE的场景系统中你也可以直接在模板中定义元素
```html
<sence>
<!-- 使用sprite元素 -->
<sprite class="player" x="100" y="100" width="50" height="50"></sprite>
<!-- 使用box元素 -->
<box class="panel" x="200" y="200" width="300" height="200">面板内容</box>
<!-- 使用text元素 -->
<text class="title" x="100" y="50">欢迎使用PE引擎</text>
<!-- 使用循环创建多个元素 -->
<box for="{item} in menuItems"
class="menu-item"
x="{item.x}"
y="{item.y}"
@click="handleMenuClick(item.id)">
{{ item.label }}
</box>
</sence>
```
对应的样式文件(.less
```less
.player {
background-color: #3498db;
border-radius: 50%;
transition: all 0.3s ease;
}
.player:hover {
transform: scale(1.1);
}
.panel {
background-color: #ecf0f1;
border: 2px solid #bdc3c7;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.title {
color: #2c3e50;
font-size: 24px;
font-weight: bold;
}
.menu-item {
width: 150px;
height: 40px;
background-color: #e74c3c;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
cursor: pointer;
margin: 5px 0;
}
```
## 元素的事件处理
PE元素支持丰富的事件处理
```javascript
// 添加点击事件
element.element.addEventListener('click', (event) => {
console.log('元素被点击了')
})
// 添加鼠标移入事件
element.element.addEventListener('mouseenter', (event) => {
element.setStyle('backgroundColor', '#e74c3c')
})
// 添加鼠标移出事件
element.element.addEventListener('mouseleave', (event) => {
element.setStyle('backgroundColor', '#3498db')
})
// 在场景模板中直接绑定事件
// <box @click="handleClick" @mouseenter="handleMouseEnter">点击我</box>
```
## 自定义元素
除了内置元素,你还可以创建自定义元素:
```javascript
// 创建自定义元素类
class CustomElement extends BaseElement {
constructor(x, y, width, height, engineId) {
super(x, y, width, height, engineId)
// 初始化自定义元素
this.element.style.backgroundColor = '#9b59b6'
this.element.style.borderRadius = '10px'
}
// 自定义方法
animateColor() {
const colors = ['#9b59b6', '#3498db', '#e74c3c', '#2ecc71']
let index = 0
setInterval(() => {
this.element.style.backgroundColor = colors[index % colors.length]
index++
}, 1000)
}
}
// 注册自定义元素
// 在场景中使用
// <custom-element x="100" y="100" width="100" height="100"></custom-element>
```
通过以上内容你已经了解了PE引擎的元素系统。在下一章节中我们将深入探讨场景系统的使用。

View File

@@ -0,0 +1,605 @@
# 事件处理
PE引擎提供了简洁而强大的事件处理机制让你能够轻松响应用户的交互操作。事件处理是创建交互式应用的关键部分。
## 事件绑定语法
在PE的场景模板中你可以使用`@`前缀直接绑定事件处理器:
```html
<sence>
<!-- 基本事件绑定 -->
<box @click="handleClick">点击我</box>
<!-- 多个事件绑定 -->
<box @click="handleClick" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
悬停我
</box>
<!-- 传递参数 -->
<box @click="handleClickWithParam('button1')">按钮1</box>
<box @click="handleClickWithParam('button2')">按钮2</box>
</sence>
<script>
function handleClick() {
console.log('元素被点击了')
}
function handleMouseEnter() {
console.log('鼠标进入元素')
}
function handleMouseLeave() {
console.log('鼠标离开元素')
}
function handleClickWithParam(buttonId) {
console.log('点击了按钮:', buttonId)
}
</script>
```
## 支持的事件类型
PE支持所有标准的DOM事件类型
### 鼠标事件
```html
<sence>
<box @click="handleClick"
@dblclick="handleDoubleClick"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
@mousedown="handleMouseDown"
@mouseup="handleMouseUp"
@contextmenu="handleContextMenu">
鼠标事件示例
</box>
</sence>
<script>
function handleClick(event) {
console.log('点击事件:', event)
}
function handleDoubleClick(event) {
console.log('双击事件:', event)
}
function handleMouseEnter(event) {
console.log('鼠标进入:', event)
}
function handleMouseLeave(event) {
console.log('鼠标离开:', event)
}
function handleMouseDown(event) {
console.log('鼠标按下:', event)
}
function handleMouseUp(event) {
console.log('鼠标释放:', event)
}
function handleContextMenu(event) {
event.preventDefault() // 阻止右键菜单
console.log('右键点击:', event)
}
</script>
```
### 键盘事件
```html
<sence>
<text @keydown="handleKeyDown"
@keyup="handleKeyUp"
@keypress="handleKeyPress"
tabindex="0">
按键事件示例(点击后按键盘)
</text>
</sence>
<script>
function handleKeyDown(event) {
console.log('按键按下:', event.key, event.code)
}
function handleKeyUp(event) {
console.log('按键释放:', event.key, event.code)
}
function handleKeyPress(event) {
console.log('按键按压:', event.key)
}
</script>
```
### 表单事件
```html
<sence>
<html @input="handleInput"
@change="handleChange"
@focus="handleFocus"
@blur="handleBlur"
html="<input type='text' placeholder='输入内容...' />">
</html>
</sence>
<script>
function handleInput(event) {
console.log('输入事件:', event.target.value)
}
function handleChange(event) {
console.log('值改变事件:', event.target.value)
}
function handleFocus(event) {
console.log('获得焦点')
}
function handleBlur(event) {
console.log('失去焦点')
}
</script>
```
### 触摸事件
```html
<sence>
<box @touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
@touchcancel="handleTouchCancel">
触摸事件示例
</box>
</sence>
<script>
function handleTouchStart(event) {
console.log('触摸开始:', event.touches.length, '个触摸点')
}
function handleTouchMove(event) {
console.log('触摸移动:', event.touches[0].clientX, event.touches[0].clientY)
}
function handleTouchEnd(event) {
console.log('触摸结束')
}
function handleTouchCancel(event) {
console.log('触摸取消')
}
</script>
```
## 事件对象
所有事件处理器都会接收到一个事件对象,包含有关事件的详细信息:
```javascript
function handleClick(event) {
// 事件基本信息
console.log('事件类型:', event.type)
console.log('目标元素:', event.target)
console.log('当前元素:', event.currentTarget)
// 鼠标位置
console.log('页面坐标:', event.pageX, event.pageY)
console.log('客户端坐标:', event.clientX, event.clientY)
// 键盘信息
console.log('是否按下Ctrl键:', event.ctrlKey)
console.log('是否按下Alt键:', event.altKey)
console.log('是否按下Shift键:', event.shiftKey)
// 阻止默认行为
event.preventDefault()
// 阻止事件冒泡
event.stopPropagation()
}
```
## 在JavaScript中绑定事件
除了在模板中绑定事件你也可以在JavaScript代码中直接绑定事件
```javascript
// 获取场景元素
const element = game.getSceneElement('my-button')
// 添加事件监听器
element.element.addEventListener('click', function(event) {
console.log('元素被点击了')
})
// 添加多个事件监听器
element.element.addEventListener('mouseenter', handleMouseEnter)
element.element.addEventListener('mouseleave', handleMouseLeave)
// 移除事件监听器
element.element.removeEventListener('click', handleClick)
```
## 事件修饰符
PE支持一些常用的事件修饰符类似于Vue.js的语法
```html
<sence>
<!-- 阻止默认行为 -->
<box @click.prevent="handleClick">阻止默认行为</box>
<!-- 阻止事件冒泡 -->
<box @click.stop="handleClick">阻止事件冒泡</box>
<!-- 只触发一次 -->
<box @click.once="handleClick">只触发一次</box>
<!-- 组合修饰符 -->
<box @click.stop.prevent="handleClick">阻止冒泡和默认行为</box>
</sence>
```
## 自定义事件
你也可以创建和触发自定义事件:
```javascript
// 创建自定义事件
const customEvent = new CustomEvent('myCustomEvent', {
detail: { message: '这是自定义事件数据' }
})
// 触发自定义事件
element.element.dispatchEvent(customEvent)
// 监听自定义事件
element.element.addEventListener('myCustomEvent', function(event) {
console.log('接收到自定义事件:', event.detail.message)
})
```
## 事件总线
PE引擎内置了事件总线允许你在不同组件间进行通信
```javascript
// 发送事件
game.eventBus.emit('user-login', { username: 'john' })
// 监听事件
game.eventBus.on('user-login', function(data) {
console.log('用户登录:', data.username)
})
// 移除事件监听器
game.eventBus.off('user-login', handlerFunction)
```
## 在场景脚本中处理事件
在场景的`<script>`部分,你可以定义事件处理函数:
```html
<sence>
<box class="counter-button" @click="incrementCounter">计数器: {{ counter }}</box>
<box class="reset-button" @click="resetCounter">重置</box>
</sence>
<script>
// 数据定义
let counter = 0
// 事件处理函数
function incrementCounter() {
counter++
console.log('计数器:', counter)
// 更新UI
const button = game.getSceneElement('counter-button')
button.element.textContent = `计数器: ${counter}`
}
function resetCounter() {
counter = 0
console.log('计数器已重置')
// 更新UI
const button = game.getSceneElement('counter-button')
button.element.textContent = `计数器: ${counter}`
}
// 生命周期钩子中绑定事件
onLoad(() => {
console.log('场景加载完成,计数器初始化为', counter)
})
onShow(() => {
console.log('场景显示,当前计数器值为', counter)
})
</script>
```
## 事件处理最佳实践
### 1. 合理使用事件委托
对于大量相似元素,使用事件委托可以提高性能:
```html
<sence>
<!-- 使用事件委托处理列表项点击 -->
<box class="list-container" @click="handleListItemClick">
<box class="list-item" data-id="1">项目1</box>
<box class="list-item" data-id="2">项目2</box>
<box class="list-item" data-id="3">项目3</box>
</box>
</sence>
<script>
function handleListItemClick(event) {
// 检查点击的是否是列表项
if (event.target.classList.contains('list-item')) {
const id = event.target.getAttribute('data-id')
console.log('点击了项目:', id)
}
}
</script>
```
### 2. 及时移除事件监听器
在适当的时机移除事件监听器,避免内存泄漏:
```javascript
onLoad(() => {
// 添加事件监听器
document.addEventListener('keydown', handleGlobalKeyDown)
})
onDestory(() => {
// 移除事件监听器
document.removeEventListener('keydown', handleGlobalKeyDown)
})
function handleGlobalKeyDown(event) {
// 全局键盘事件处理
if (event.key === 'Escape') {
PE.navigateTo('/')
}
}
```
### 3. 使用防抖和节流
对于频繁触发的事件(如滚动、输入),使用防抖和节流优化性能:
```javascript
// 防抖函数
function debounce(func, wait) {
let timeout
return function(...args) {
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(this, args), wait)
}
}
// 节流函数
function throttle(func, limit) {
let inThrottle
return function(...args) {
if (!inThrottle) {
func.apply(this, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
// 使用防抖处理输入事件
const handleInput = debounce(function(event) {
console.log('输入内容:', event.target.value)
}, 300)
// 使用节流处理滚动事件
const handleScroll = throttle(function(event) {
console.log('页面滚动')
}, 100)
```
## 完整示例
以下是一个完整的事件处理示例:
### scenes/game/index.pe
```html
<sence>
<box class="game-container">
<!-- 玩家角色 -->
<sprite class="player"
@mousedown="startDrag"
@mouseup="stopDrag"
@touchstart="startDrag"
@touchend="stopDrag">
</sprite>
<!-- 敌人 -->
<sprite for="{enemy} in enemies"
class="enemy"
:class="{ 'enemy-active': enemy.active }"
@click="hitEnemy(enemy.id)">
</sprite>
<!-- 控制按钮 -->
<box class="control-panel">
<box class="btn btn-primary" @click="startGame">开始游戏</box>
<box class="btn btn-secondary" @click="pauseGame">暂停</box>
<box class="btn btn-danger" @click="resetGame">重置</box>
</box>
<!-- 游戏信息 -->
<text class="score">得分: {{ score }}</text>
<text class="lives">生命: {{ lives }}</text>
</box>
</sence>
<script>
// 游戏状态
let score = 0
let lives = 3
let gameRunning = false
let dragging = false
// 敌人数据
const enemies = [
{ id: 1, active: true },
{ id: 2, active: true },
{ id: 3, active: true }
]
// 事件处理函数
function startDrag(event) {
if (!gameRunning) return
dragging = true
console.log('开始拖拽玩家')
// 添加鼠标移动事件监听器
document.addEventListener('mousemove', handleDrag)
document.addEventListener('touchmove', handleDrag)
}
function stopDrag(event) {
dragging = false
console.log('停止拖拽玩家')
// 移除鼠标移动事件监听器
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)
}
function handleDrag(event) {
if (!dragging || !gameRunning) return
const player = game.getSceneElement('player')
if (player) {
// 获取鼠标或触摸位置
let x, y
if (event.type === 'touchmove') {
x = event.touches[0].clientX
y = event.touches[0].clientY
} else {
x = event.clientX
y = event.clientY
}
// 更新玩家位置
player.setPosition(x - 25, y - 25) // 考虑精灵尺寸
}
}
function hitEnemy(enemyId) {
if (!gameRunning) return
// 找到被击中的敌人
const enemy = enemies.find(e => e.id === enemyId)
if (enemy && enemy.active) {
// 标记敌人为非活跃状态
enemy.active = false
// 增加得分
score += 10
updateScore()
console.log('击中敌人:', enemyId)
}
}
function startGame() {
gameRunning = true
score = 0
lives = 3
updateScore()
updateLives()
// 重置敌人状态
enemies.forEach(enemy => {
enemy.active = true
})
console.log('游戏开始')
}
function pauseGame() {
gameRunning = !gameRunning
console.log(gameRunning ? '游戏继续' : '游戏暂停')
}
function resetGame() {
gameRunning = false
score = 0
lives = 3
updateScore()
updateLives()
// 重置敌人状态
enemies.forEach(enemy => {
enemy.active = true
})
console.log('游戏重置')
}
// 更新得分显示
function updateScore() {
const scoreElement = game.getSceneElement('score')
if (scoreElement) {
scoreElement.element.textContent = `得分: ${score}`
}
}
// 更新生命显示
function updateLives() {
const livesElement = game.getSceneElement('lives')
if (livesElement) {
livesElement.element.textContent = `生命: ${lives}`
}
}
// 生命周期钩子
onLoad(() => {
console.log('游戏场景加载完成')
})
onShow(() => {
console.log('游戏场景显示')
// 初始化游戏状态
updateScore()
updateLives()
})
onHide(() => {
console.log('游戏场景隐藏')
// 暂停游戏
gameRunning = false
})
onDestory(() => {
console.log('游戏场景销毁')
// 清理事件监听器
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)
})
</script>
```
通过以上内容你已经了解了PE引擎的事件处理机制。在下一章节中我们将探讨生命周期的相关内容。

View File

@@ -0,0 +1,564 @@
# 生命周期
PE引擎为场景提供了完整的生命周期钩子让你能够在适当的时机执行初始化、清理和其他操作。理解生命周期对于开发高效、稳定的PE应用至关重要。
## 生命周期钩子概览
每个PE场景都有四个主要的生命周期钩子
1. **onLoad** - 场景加载时调用(只执行一次)
2. **onShow** - 场景显示时调用(每次显示都会执行)
3. **onHide** - 场景隐藏时调用
4. **onDestory** - 场景销毁时调用(只执行一次)
## onLoad
`onLoad`钩子在场景首次加载时调用,只执行一次。这是进行一次性初始化操作的最佳时机。
```html
<sence>
<box class="welcome">欢迎来到PE引擎</box>
</sence>
<script>
// 在onLoad中进行一次性初始化
onLoad(() => {
console.log('场景加载完成')
// 初始化数据
initializeData()
// 设置初始状态
setupInitialState()
// 加载资源
loadResources()
// 添加全局事件监听器
document.addEventListener('keydown', handleGlobalKeyDown)
})
function initializeData() {
// 初始化应用数据
console.log('初始化数据')
}
function setupInitialState() {
// 设置初始状态
console.log('设置初始状态')
}
function loadResources() {
// 加载必要的资源
console.log('加载资源')
}
function handleGlobalKeyDown(event) {
// 处理全局键盘事件
if (event.key === 'Escape') {
PE.navigateTo('/')
}
}
</script>
```
## onShow
`onShow`钩子在场景每次显示时调用。无论是首次显示还是从其他场景切换回来,都会执行此钩子。
```html
<sence>
<box class="content">场景内容</box>
<text class="last-updated">最后更新: {{ lastUpdated }}</text>
</sence>
<script>
let lastUpdated = ''
onShow(() => {
console.log('场景显示')
// 更新时间戳
updateTimestamp()
// 刷新数据
refreshData()
// 启动动画
startAnimations()
// 恢复定时器
resumeTimers()
})
function updateTimestamp() {
lastUpdated = new Date().toLocaleString()
const timeElement = game.getSceneElement('last-updated')
if (timeElement) {
timeElement.element.textContent = `最后更新: ${lastUpdated}`
}
}
function refreshData() {
// 刷新场景数据
console.log('刷新数据')
}
function startAnimations() {
// 启动场景动画
console.log('启动动画')
}
function resumeTimers() {
// 恢复定时器
console.log('恢复定时器')
}
</script>
```
## onHide
`onHide`钩子在场景隐藏时调用,比如切换到其他场景时。
```html
<sence>
<box class="content">场景内容</box>
</sence>
<script>
onHide(() => {
console.log('场景隐藏')
// 暂停动画
pauseAnimations()
// 暂停定时器
pauseTimers()
// 保存状态
saveState()
})
function pauseAnimations() {
// 暂停场景动画
console.log('暂停动画')
}
function pauseTimers() {
// 暂停定时器
console.log('暂停定时器')
}
function saveState() {
// 保存场景状态
console.log('保存状态')
}
</script>
```
## onDestory
`onDestory`钩子在场景销毁时调用,通常发生在应用关闭或场景被永久移除时。
```html
<sence>
<box class="content">场景内容</box>
</sence>
<script>
onDestory(() => {
console.log('场景销毁')
// 清理资源
cleanupResources()
// 移除事件监听器
removeEventListeners()
// 停止所有动画
stopAllAnimations()
// 清理定时器
clearAllTimers()
})
function cleanupResources() {
// 清理加载的资源
console.log('清理资源')
}
function removeEventListeners() {
// 移除添加的事件监听器
document.removeEventListener('keydown', handleGlobalKeyDown)
console.log('移除事件监听器')
}
function stopAllAnimations() {
// 停止所有动画
console.log('停止所有动画')
}
function clearAllTimers() {
// 清理所有定时器
console.log('清理定时器')
}
</script>
```
## 生命周期执行顺序
理解生命周期钩子的执行顺序对于正确管理场景状态非常重要:
### 首次加载场景
1. `onLoad` - 场景加载时执行(一次)
2. `onShow` - 场景显示时执行
### 切换到其他场景
1. 当前场景: `onHide` - 场景隐藏时执行
### 切换回场景
1. `onShow` - 场景显示时执行
### 销毁场景
1. `onHide` - 场景隐藏时执行(如果当前显示)
2. `onDestory` - 场景销毁时执行(一次)
## 实际应用示例
以下是一个完整的生命周期应用示例:
### scenes/dashboard/index.pe
```html
<sence>
<box class="dashboard">
<text class="title">仪表板</text>
<text class="status">状态: {{ status }}</text>
<text class="last-update">最后更新: {{ lastUpdate }}</text>
<box class="data-container">
<box for="{item} in dataItems" class="data-card">
<text class="card-title">{{ item.title }}</text>
<text class="card-value">{{ item.value }}</text>
</box>
</box>
<box class="controls">
<box class="btn btn-primary" @click="refreshData">刷新数据</box>
<box class="btn btn-secondary" @click="PE.navigateTo('/settings')">设置</box>
</box>
</box>
</sence>
<script>
// 数据状态
let status = '加载中...'
let lastUpdate = ''
let dataItems = []
let refreshTimer = null
let dataRefreshInterval = null
// onLoad - 只执行一次
onLoad(() => {
console.log('仪表板加载')
// 初始化数据
initializeData()
// 设置全局事件监听器
setupEventListeners()
// 加载初始数据
loadData()
})
// onShow - 每次显示时执行
onShow(() => {
console.log('仪表板显示')
// 更新状态
updateStatus('运行中')
// 启动定时刷新
startAutoRefresh()
// 恢复动画
resumeAnimations()
})
// onHide - 隐藏时执行
onHide(() => {
console.log('仪表板隐藏')
// 暂停自动刷新
stopAutoRefresh()
// 暂停动画
pauseAnimations()
// 保存当前状态
saveCurrentState()
})
// onDestory - 销毁时执行
onDestory(() => {
console.log('仪表板销毁')
// 清理所有资源
cleanup()
})
// 初始化数据
function initializeData() {
dataItems = [
{ title: '用户数', value: '0' },
{ title: '订单数', value: '0' },
{ title: '收入', value: '¥0' }
]
updateUI()
}
// 设置事件监听器
function setupEventListeners() {
// 监听网络状态变化
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)
}
// 加载数据
async function loadData() {
try {
updateStatus('加载数据中...')
// 模拟API调用
const data = await fetchDataFromAPI()
dataItems = data
lastUpdate = new Date().toLocaleString()
updateStatus('数据加载完成')
updateUI()
} catch (error) {
console.error('加载数据失败:', error)
updateStatus('数据加载失败')
}
}
// 模拟API调用
function fetchDataFromAPI() {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ title: '用户数', value: Math.floor(Math.random() * 1000) },
{ title: '订单数', value: Math.floor(Math.random() * 500) },
{ title: '收入', value: '¥' + (Math.random() * 10000).toFixed(2) }
])
}, 1000)
})
}
// 启动自动刷新
function startAutoRefresh() {
if (dataRefreshInterval) {
clearInterval(dataRefreshInterval)
}
dataRefreshInterval = setInterval(() => {
loadData()
}, 30000) // 每30秒刷新一次
// 启动更新时间戳定时器
if (refreshTimer) {
clearInterval(refreshTimer)
}
refreshTimer = setInterval(() => {
updateTimestamp()
}, 1000)
}
// 停止自动刷新
function stopAutoRefresh() {
if (dataRefreshInterval) {
clearInterval(dataRefreshInterval)
dataRefreshInterval = null
}
if (refreshTimer) {
clearInterval(refreshTimer)
refreshTimer = null
}
}
// 更新状态显示
function updateStatus(newStatus) {
status = newStatus
const statusElement = game.getSceneElement('status')
if (statusElement) {
statusElement.element.textContent = `状态: ${status}`
}
}
// 更新时间戳显示
function updateTimestamp() {
if (lastUpdate) {
const timeElement = game.getSceneElement('last-update')
if (timeElement) {
const elapsed = Math.floor((new Date() - new Date(lastUpdate)) / 1000)
timeElement.element.textContent = `最后更新: ${lastUpdate} (${elapsed}秒前)`
}
}
}
// 更新UI
function updateUI() {
// 更新数据卡片
const container = game.getSceneElement('data-container')
if (container) {
// 清空现有内容
container.element.innerHTML = ''
// 添加新的数据卡片
dataItems.forEach(item => {
const card = document.createElement('div')
card.className = 'data-card'
card.innerHTML = `
<div class="card-title">${item.title}</div>
<div class="card-value">${item.value}</div>
`
container.element.appendChild(card)
})
}
}
// 刷新数据
function refreshData() {
loadData()
}
// 处理在线状态
function handleOnline() {
updateStatus('在线')
}
// 处理离线状态
function handleOffline() {
updateStatus('离线')
}
// 恢复动画
function resumeAnimations() {
console.log('恢复动画')
}
// 暂停动画
function pauseAnimations() {
console.log('暂停动画')
}
// 保存当前状态
function saveCurrentState() {
console.log('保存当前状态')
}
// 清理资源
function cleanup() {
// 停止所有定时器
stopAutoRefresh()
// 移除事件监听器
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
// 清理数据
dataItems = []
status = ''
lastUpdate = ''
console.log('资源清理完成')
}
</script>
```
## 生命周期最佳实践
### 1. 合理分配操作到不同钩子
```javascript
// 正确的做法
onLoad(() => {
// 一次性初始化操作
initializeDatabase()
setupGlobalEventListeners()
})
onShow(() => {
// 每次显示时的操作
startDataPolling()
resumeAnimations()
})
onHide(() => {
// 隐藏时的操作
stopDataPolling()
pauseAnimations()
})
onDestory(() => {
// 销毁时的操作
cleanupResources()
removeEventListeners()
})
```
### 2. 及时清理资源
```javascript
onLoad(() => {
// 创建定时器
this.timer = setInterval(updateData, 5000)
// 添加事件监听器
document.addEventListener('keydown', handleKeyDown)
})
onDestory(() => {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
// 移除事件监听器
document.removeEventListener('keydown', handleKeyDown)
})
```
### 3. 处理异步操作
```javascript
onLoad(async () => {
try {
// 异步加载数据
this.data = await loadDataFromServer()
updateUI()
} catch (error) {
console.error('加载数据失败:', error)
showErrorMessage()
}
})
onDestory(() => {
// 取消未完成的请求
if (this.currentRequest) {
this.currentRequest.cancel()
}
})
```
通过以上内容你已经了解了PE引擎的生命周期机制。这些生命周期钩子为你提供了强大的控制能力让你能够精确管理场景的创建、显示、隐藏和销毁过程。

377
guide/essentials/scenes.md Normal file
View File

@@ -0,0 +1,377 @@
# 场景系统
PE引擎的场景系统是其核心特性之一它提供了一种结构化的方式来组织和管理应用的不同视图和状态。每个场景都是一个独立的单元包含模板、样式和逻辑代码。
## 场景的基本结构
PE场景由三个主要部分组成
1. **模板Template** - 定义场景的结构和元素
2. **样式Style** - 定义场景的外观和样式
3. **脚本Script** - 定义场景的逻辑和行为
### 场景文件结构
一个典型的PE场景目录结构如下
```
scenes/
├── home/
│ ├── index.pe # 场景模板文件
│ └── index.less # 场景样式文件
└── about/
├── index.pe
└── index.less
```
## 场景模板文件(.pe
场景模板文件使用类似HTML的语法包含`sence`标签和`script`标签:
```html
<sence>
<!-- 场景元素定义 -->
<sprite class="logo"></sprite>
<box class="welcome-box">欢迎来到PE引擎!</box>
<text class="description">这是一个简单的场景示例</text>
<!-- 循环渲染元素 -->
<box for="{item} in menuItems"
class="menu-item"
@click="handleMenuClick(item.id)">
{{ item.label }}
</box>
</sence>
<script>
// 场景数据和逻辑
const menuItems = [
{ id: 'home', label: '首页' },
{ id: 'about', label: '关于' },
{ id: 'contact', label: '联系' }
]
// 生命周期钩子
onLoad(() => {
console.log('场景加载完成')
})
onShow(() => {
console.log('场景显示')
})
// 事件处理函数
function handleMenuClick(id) {
console.log('点击了菜单项:', id)
PE.navigateTo(`/${id}`)
}
</script>
```
### 模板语法
PE场景模板支持以下语法特性
#### 元素定义
```html
<!-- 基本元素 -->
<sprite class="my-sprite" x="100" y="100" width="50" height="50"></sprite>
<box class="my-box">盒子内容</box>
<text class="my-text">文本内容</text>
```
#### 事件绑定
使用`@`前缀绑定事件处理器:
```html
<box @click="handleClick" @mouseenter="handleMouseEnter">点击我</box>
```
#### 循环渲染
使用`for`指令循环渲染元素:
```html
<box for="{item} in items" class="item">{{ item.name }}</box>
```
#### 插值表达式
使用双大括号语法插入数据:
```html
<text>当前计数: {{ count }}</text>
```
## 场景样式文件(.less
每个场景可以有对应的Less样式文件样式会自动作用于该场景内的元素
```less
// home/index.less
.logo {
width: 100px;
height: 100px;
background-color: #3498db;
border-radius: 50%;
position: absolute;
top: 50px;
left: 50px;
}
.welcome-box {
width: 300px;
height: 100px;
background-color: #2c3e50;
color: white;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 200px;
left: 50px;
border-radius: 8px;
}
.description {
color: #7f8c8d;
font-size: 16px;
position: absolute;
top: 320px;
left: 50px;
}
.menu-item {
width: 150px;
height: 40px;
background-color: #e74c3c;
color: white;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 50px;
border-radius: 4px;
cursor: pointer;
// 使用Less变量和嵌套
&:hover {
background-color: darken(#e74c3c, 10%);
}
&:nth-child(1) { top: 380px; }
&:nth-child(2) { top: 430px; }
&:nth-child(3) { top: 480px; }
}
```
## 场景路由配置
场景通过`sences.json`文件进行路由配置:
```json
{
"sence": [
{
"path": "/",
"title": "首页"
},
{
"path": "/about",
"title": "关于我们"
},
{
"path": "/products",
"title": "产品"
}
],
"platform": "PC"
}
```
## 场景生命周期
每个场景都有完整的生命周期钩子,让你能够在适当的时机执行操作:
### onLoad
场景加载时调用,只在场景首次加载时执行一次:
```javascript
onLoad(() => {
console.log('场景加载完成')
// 初始化操作
initializeData()
})
```
### onShow
场景显示时调用,每次场景被显示时都会执行:
```javascript
onShow(() => {
console.log('场景显示')
// 每次显示时的操作
refreshData()
})
```
### onHide
场景隐藏时调用:
```javascript
onHide(() => {
console.log('场景隐藏')
// 清理操作
pauseAnimations()
})
```
### onDestory
场景销毁时调用:
```javascript
onDestory(() => {
console.log('场景销毁')
// 最终清理操作
clearInterval(timer)
})
```
## 场景间导航
PE提供了简单的API来在场景间进行导航
```javascript
// 导航到指定路径的场景
PE.navigateTo('/about')
// 在模板中使用
<box @click="PE.navigateTo('/products')">查看产品</box>
```
## 完整示例
以下是一个完整的场景示例:
### scenes/product/index.pe
```html
<sence>
<box class="header">产品列表</box>
<box for="{product} in products"
class="product-card"
@click="viewProduct(product.id)">
<text class="product-name">{{ product.name }}</text>
<text class="product-price">¥{{ product.price }}</text>
</box>
<box class="back-button" @click="PE.navigateTo('/')">返回首页</box>
</sence>
<script>
// 产品数据
const products = [
{ id: 1, name: '产品A', price: 99.99 },
{ id: 2, name: '产品B', price: 149.99 },
{ id: 3, name: '产品C', price: 199.99 }
]
// 生命周期钩子
onLoad(() => {
console.log('产品页面加载')
})
onShow(() => {
console.log('产品页面显示')
})
// 事件处理函数
function viewProduct(id) {
console.log('查看产品:', id)
// 可以通过全局变量或事件传递数据
window.currentProductId = id
PE.navigateTo('/product-detail')
}
</script>
```
### scenes/product/index.less
```less
.header {
width: 100%;
height: 60px;
background-color: #34495e;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
}
.product-card {
width: 300px;
height: 100px;
background-color: #ecf0f1;
border: 1px solid #bdc3c7;
border-radius: 8px;
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 20px;
cursor: pointer;
&:hover {
background-color: #d5dbdb;
}
&:nth-child(2) { top: 100px; } // 第一个产品卡
&:nth-child(3) { top: 220px; } // 第二个产品卡
&:nth-child(4) { top: 340px; } // 第三个产品卡
}
.product-name {
color: #2c3e50;
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
.product-price {
color: #e74c3c;
font-size: 16px;
font-weight: bold;
}
.back-button {
width: 100px;
height: 40px;
background-color: #95a5a6;
color: white;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
bottom: 20px;
left: 20px;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: #7f8c8d;
}
}
```
通过以上内容你已经了解了PE引擎的场景系统。在下一章节中我们将探讨样式处理的相关内容。

546
guide/essentials/styling.md Normal file
View 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引擎的样式处理机制。在下一章节中我们将探讨事件处理的相关内容。