You've already forked Pandona-Engine
13 KiB
13 KiB
事件处理
PE引擎提供了简洁而强大的事件处理机制,让你能够轻松响应用户的交互操作。事件处理是创建交互式应用的关键部分。
事件绑定语法
在PE的场景模板中,你可以使用@前缀直接绑定事件处理器:
<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事件类型:
鼠标事件
<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>
键盘事件
<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>
表单事件
<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>
触摸事件
<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>
事件对象
所有事件处理器都会接收到一个事件对象,包含有关事件的详细信息:
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代码中直接绑定事件:
// 获取场景元素
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的语法:
<sence>
<!-- 阻止默认行为 -->
<box @click.prevent="handleClick">阻止默认行为</box>
<!-- 阻止事件冒泡 -->
<box @click.stop="handleClick">阻止事件冒泡</box>
<!-- 只触发一次 -->
<box @click.once="handleClick">只触发一次</box>
<!-- 组合修饰符 -->
<box @click.stop.prevent="handleClick">阻止冒泡和默认行为</box>
</sence>
自定义事件
你也可以创建和触发自定义事件:
// 创建自定义事件
const customEvent = new CustomEvent('myCustomEvent', {
detail: { message: '这是自定义事件数据' }
})
// 触发自定义事件
element.element.dispatchEvent(customEvent)
// 监听自定义事件
element.element.addEventListener('myCustomEvent', function(event) {
console.log('接收到自定义事件:', event.detail.message)
})
事件总线
PE引擎内置了事件总线,允许你在不同组件间进行通信:
// 发送事件
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>部分,你可以定义事件处理函数:
<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. 合理使用事件委托
对于大量相似元素,使用事件委托可以提高性能:
<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. 及时移除事件监听器
在适当的时机移除事件监听器,避免内存泄漏:
onLoad(() => {
// 添加事件监听器
document.addEventListener('keydown', handleGlobalKeyDown)
})
onDestory(() => {
// 移除事件监听器
document.removeEventListener('keydown', handleGlobalKeyDown)
})
function handleGlobalKeyDown(event) {
// 全局键盘事件处理
if (event.key === 'Escape') {
PE.navigateTo('/')
}
}
3. 使用防抖和节流
对于频繁触发的事件(如滚动、输入),使用防抖和节流优化性能:
// 防抖函数
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
<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引擎的事件处理机制。在下一章节中,我们将探讨生命周期的相关内容。