You've already forked Pandona-Engine
455 lines
9.3 KiB
Markdown
455 lines
9.3 KiB
Markdown
# 进阶主题
|
||
|
||
在掌握了PE引擎的基础知识后,我们可以探索一些更高级的功能和概念。这些进阶主题将帮助你构建更复杂、更高效的应用。
|
||
|
||
## 目录
|
||
|
||
- [动画系统](./animation)
|
||
- [状态管理](./state-management)
|
||
- [插件系统](./plugins)
|
||
- [性能优化](./performance)
|
||
- [测试](./testing)
|
||
- [部署](./deployment)
|
||
|
||
## 动画系统
|
||
|
||
PE引擎内置了强大的动画系统,支持补间动画、帧动画和序列动画。
|
||
|
||
### 补间动画
|
||
|
||
补间动画允许你在两个状态之间创建平滑的过渡:
|
||
|
||
```javascript
|
||
// 创建补间动画
|
||
const tween = game.tween(element.element)
|
||
.to({
|
||
left: '500px',
|
||
top: '300px',
|
||
opacity: 0.5
|
||
}, 2000) // 持续2秒
|
||
.easing('easeInOutQuad') // 缓动函数
|
||
.onUpdate(() => {
|
||
console.log('动画更新')
|
||
})
|
||
.onComplete(() => {
|
||
console.log('动画完成')
|
||
})
|
||
.start()
|
||
```
|
||
|
||
### 帧动画
|
||
|
||
帧动画通过连续播放一系列图像来创建动画效果:
|
||
|
||
```javascript
|
||
// 创建帧动画
|
||
game.createFrameAnimation('walk', [
|
||
'img/walk1.png',
|
||
'img/walk2.png',
|
||
'img/walk3.png',
|
||
'img/walk4.png'
|
||
], 10) // 10帧每秒
|
||
|
||
// 播放帧动画
|
||
game.playFrameAnimation(sprite, 'walk', true) // 循环播放
|
||
```
|
||
|
||
### 序列动画
|
||
|
||
序列动画允许你按顺序播放多个动画:
|
||
|
||
```javascript
|
||
// 创建动画序列
|
||
game.createSequence('moveAndFade', [
|
||
{
|
||
target: element.element,
|
||
props: { left: '500px' },
|
||
duration: 1000
|
||
},
|
||
{
|
||
target: element.element,
|
||
props: { opacity: 0 },
|
||
duration: 500
|
||
}
|
||
])
|
||
|
||
// 播放序列动画
|
||
game.playSequence('moveAndFade')
|
||
```
|
||
|
||
## 状态管理
|
||
|
||
对于复杂应用,合理的状态管理至关重要。PE引擎提供了灵活的状态管理方案。
|
||
|
||
### 全局状态
|
||
|
||
```javascript
|
||
// 创建全局状态管理器
|
||
class StateManager {
|
||
constructor() {
|
||
this.state = {}
|
||
this.listeners = {}
|
||
}
|
||
|
||
// 设置状态
|
||
setState(key, value) {
|
||
this.state[key] = value
|
||
this.notifyListeners(key, value)
|
||
}
|
||
|
||
// 获取状态
|
||
getState(key) {
|
||
return this.state[key]
|
||
}
|
||
|
||
// 订阅状态变化
|
||
subscribe(key, callback) {
|
||
if (!this.listeners[key]) {
|
||
this.listeners[key] = []
|
||
}
|
||
this.listeners[key].push(callback)
|
||
}
|
||
|
||
// 通知监听器
|
||
notifyListeners(key, value) {
|
||
if (this.listeners[key]) {
|
||
this.listeners[key].forEach(callback => callback(value))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建全局状态管理器实例
|
||
const stateManager = new StateManager()
|
||
|
||
// 在组件中使用
|
||
stateManager.setState('user', { name: '张三', age: 25 })
|
||
stateManager.subscribe('user', (user) => {
|
||
console.log('用户信息更新:', user)
|
||
})
|
||
```
|
||
|
||
### 场景状态
|
||
|
||
每个场景也可以有自己的状态管理:
|
||
|
||
```html
|
||
<script>
|
||
// 场景内部状态
|
||
let sceneState = {
|
||
data: [],
|
||
loading: false,
|
||
error: null
|
||
}
|
||
|
||
// 更新场景状态
|
||
function updateState(newState) {
|
||
sceneState = { ...sceneState, ...newState }
|
||
updateUI()
|
||
}
|
||
|
||
// 异步数据加载
|
||
async function loadData() {
|
||
updateState({ loading: true })
|
||
|
||
try {
|
||
const data = await fetchData()
|
||
updateState({ data, loading: false })
|
||
} catch (error) {
|
||
updateState({ error: error.message, loading: false })
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
## 插件系统
|
||
|
||
PE引擎支持插件系统,允许你扩展引擎的功能。
|
||
|
||
### 创建插件
|
||
|
||
```javascript
|
||
// 定义插件
|
||
const LoggerPlugin = {
|
||
name: 'LoggerPlugin',
|
||
install(engine, options) {
|
||
console.log('LoggerPlugin installed with options:', options)
|
||
|
||
// 扩展引擎功能
|
||
engine.log = function(message) {
|
||
console.log(`[PE Logger]: ${message}`)
|
||
}
|
||
|
||
// 监听引擎事件
|
||
engine.eventBus.on('sceneChanged', (data) => {
|
||
this.log(`Scene changed to: ${data.path}`)
|
||
})
|
||
}
|
||
}
|
||
|
||
// 使用插件
|
||
game.use(LoggerPlugin, { level: 'debug' })
|
||
```
|
||
|
||
### 内置插件
|
||
|
||
PE引擎提供了一些有用的内置插件:
|
||
|
||
```javascript
|
||
// 路由插件
|
||
import { RouterPlugin } from 'pe-engine/plugins'
|
||
|
||
game.use(RouterPlugin, {
|
||
mode: 'history', // 或 'hash'
|
||
routes: [
|
||
{ path: '/', component: 'Home' },
|
||
{ path: '/about', component: 'About' }
|
||
]
|
||
})
|
||
|
||
// 状态管理插件
|
||
import { StatePlugin } from 'pe-engine/plugins'
|
||
|
||
game.use(StatePlugin, {
|
||
initialState: {
|
||
user: null,
|
||
theme: 'light'
|
||
}
|
||
})
|
||
```
|
||
|
||
## 性能优化
|
||
|
||
构建高性能的PE应用需要注意以下几个方面:
|
||
|
||
### 1. 虚拟化列表
|
||
|
||
对于大量数据的列表渲染,使用虚拟化技术:
|
||
|
||
```javascript
|
||
// 虚拟化列表组件
|
||
class VirtualList {
|
||
constructor(container, items, itemHeight) {
|
||
this.container = container
|
||
this.items = items
|
||
this.itemHeight = itemHeight
|
||
this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2
|
||
this.startIndex = 0
|
||
|
||
this.render()
|
||
this.bindEvents()
|
||
}
|
||
|
||
render() {
|
||
const fragment = document.createDocumentFragment()
|
||
const endIndex = Math.min(this.startIndex + this.visibleCount, this.items.length)
|
||
|
||
for (let i = this.startIndex; i < endIndex; i++) {
|
||
const itemElement = this.createItemElement(this.items[i], i)
|
||
itemElement.style.transform = `translateY(${i * this.itemHeight}px)`
|
||
fragment.appendChild(itemElement)
|
||
}
|
||
|
||
this.container.innerHTML = ''
|
||
this.container.appendChild(fragment)
|
||
}
|
||
|
||
createItemElement(item, index) {
|
||
const element = document.createElement('div')
|
||
element.className = 'list-item'
|
||
element.textContent = item.text
|
||
element.style.height = `${this.itemHeight}px`
|
||
element.style.position = 'absolute'
|
||
element.style.width = '100%'
|
||
return element
|
||
}
|
||
|
||
bindEvents() {
|
||
this.container.addEventListener('scroll', () => {
|
||
const newStartIndex = Math.floor(this.container.scrollTop / this.itemHeight)
|
||
if (newStartIndex !== this.startIndex) {
|
||
this.startIndex = newStartIndex
|
||
this.render()
|
||
}
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 防抖和节流
|
||
|
||
对于频繁触发的操作,使用防抖和节流:
|
||
|
||
```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 handleResize = debounce(() => {
|
||
console.log('窗口大小改变')
|
||
}, 300)
|
||
|
||
const handleScroll = throttle(() => {
|
||
console.log('页面滚动')
|
||
}, 100)
|
||
|
||
window.addEventListener('resize', handleResize)
|
||
window.addEventListener('scroll', handleScroll)
|
||
```
|
||
|
||
### 3. 懒加载
|
||
|
||
对于图片和组件,使用懒加载技术:
|
||
|
||
```javascript
|
||
// 图片懒加载
|
||
class ImageLazyLoader {
|
||
constructor() {
|
||
this.images = []
|
||
this.observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
this.loadImage(entry.target)
|
||
this.observer.unobserve(entry.target)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
loadImage(img) {
|
||
const src = img.dataset.src
|
||
if (src) {
|
||
img.src = src
|
||
img.classList.remove('lazy')
|
||
img.classList.add('loaded')
|
||
}
|
||
}
|
||
|
||
observe(img) {
|
||
this.observer.observe(img)
|
||
}
|
||
}
|
||
|
||
// 使用懒加载
|
||
const lazyLoader = new ImageLazyLoader()
|
||
document.querySelectorAll('img[data-src]').forEach(img => {
|
||
lazyLoader.observe(img)
|
||
})
|
||
```
|
||
|
||
## 测试
|
||
|
||
编写测试是确保应用质量的重要环节。
|
||
|
||
### 单元测试
|
||
|
||
使用Jest等测试框架编写单元测试:
|
||
|
||
```javascript
|
||
// 测试元素创建功能
|
||
describe('Element Creation', () => {
|
||
test('should create sprite element', () => {
|
||
const sprite = game.create('sprite', { x: 100, y: 100 })
|
||
expect(sprite).toBeDefined()
|
||
expect(sprite.getX()).toBe(100)
|
||
expect(sprite.getY()).toBe(100)
|
||
})
|
||
|
||
test('should create box element', () => {
|
||
const box = game.create('box', { width: 200, height: 100 })
|
||
expect(box.getWidth()).toBe(200)
|
||
expect(box.getHeight()).toBe(100)
|
||
})
|
||
})
|
||
```
|
||
|
||
### 集成测试
|
||
|
||
测试组件和场景的集成:
|
||
|
||
```javascript
|
||
// 测试场景切换
|
||
describe('Scene Navigation', () => {
|
||
test('should navigate to about page', async () => {
|
||
// 模拟场景切换
|
||
await game.switchToPath('/about')
|
||
|
||
// 验证场景是否正确加载
|
||
const currentScene = game.getCurrentScene()
|
||
expect(currentScene.path).toBe('/about')
|
||
})
|
||
})
|
||
```
|
||
|
||
## 部署
|
||
|
||
将PE应用部署到生产环境需要注意以下几点:
|
||
|
||
### 构建优化
|
||
|
||
```javascript
|
||
// vite.config.js
|
||
import { defineConfig } from 'vite'
|
||
import { resolve } from 'path'
|
||
|
||
export default defineConfig({
|
||
build: {
|
||
// 代码分割
|
||
rollupOptions: {
|
||
output: {
|
||
manualChunks: {
|
||
vendor: ['pe-engine'],
|
||
utils: ['./src/utils'],
|
||
}
|
||
}
|
||
},
|
||
// 压缩
|
||
minify: 'terser',
|
||
terserOptions: {
|
||
compress: {
|
||
drop_console: true,
|
||
drop_debugger: true
|
||
}
|
||
}
|
||
},
|
||
// 静态资源处理
|
||
assetsInclude: ['**/*.pe', '**/*.less']
|
||
})
|
||
```
|
||
|
||
### 环境配置
|
||
|
||
```javascript
|
||
// 配置不同环境
|
||
const config = {
|
||
development: {
|
||
apiBaseUrl: 'http://localhost:3000',
|
||
debug: true
|
||
},
|
||
production: {
|
||
apiBaseUrl: 'https://api.example.com',
|
||
debug: false
|
||
}
|
||
}
|
||
|
||
const currentConfig = config[process.env.NODE_ENV || 'development']
|
||
```
|
||
|
||
通过以上进阶主题的学习,你将能够构建更复杂、更高效的PE应用。在接下来的章节中,我们将深入探讨每个主题的更多细节。 |