You've already forked Pandona-Engine
初始化提交
This commit is contained in:
84
guide/essentials/README.md
Normal file
84
guide/essentials/README.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 基础教程
|
||||
|
||||
在本章节中,你将学习PE引擎的核心概念。我们假定你已经完成了[快速上手](../quick-start)指南,并对HTML、CSS和JavaScript有基本的了解。
|
||||
|
||||
## 目录
|
||||
|
||||
- [创建应用实例](./application)
|
||||
- [元素系统](./elements)
|
||||
- [场景系统](./scenes)
|
||||
- [样式处理](./styling)
|
||||
- [事件处理](./event-handling)
|
||||
- [生命周期](./lifecycle)
|
||||
|
||||
## 什么是PE引擎?
|
||||
|
||||
PE(Pandona 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引擎的使用方法。
|
||||
194
guide/essentials/application.md
Normal file
194
guide/essentials/application.md
Normal 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的元素系统。
|
||||
303
guide/essentials/elements.md
Normal file
303
guide/essentials/elements.md
Normal 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引擎的元素系统。在下一章节中,我们将深入探讨场景系统的使用。
|
||||
605
guide/essentials/event-handling.md
Normal file
605
guide/essentials/event-handling.md
Normal 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引擎的事件处理机制。在下一章节中,我们将探讨生命周期的相关内容。
|
||||
564
guide/essentials/lifecycle.md
Normal file
564
guide/essentials/lifecycle.md
Normal 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
377
guide/essentials/scenes.md
Normal 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
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