事件处理

我们提供了一些新的特性来提高开发效率。

简洁模式

一种简洁的事件处理方式:on__()off__(), 可以通过参数绑定 this, 一次可以 off 多个不同的侦听事件。

命名规则

事件名称常量对应的字面值遵循如下规则:

PointerEvent.DOWN = 'pointer.down' // 事件类型.事件名称, 提高可读性

ZoomEvent.ZOOM: 'zoom'  // 事件类型与事件名称一样,可以省略
ZoomEvent.START: 'zoom.start'
ZoomEvent.END: 'zoom.end'

// 例外, 以下 PointerEvent 事件不用加 pointer 前缀
'tap''click''doubleclick''longpress''longtap'

使用字面值代替常量

import { Leafer } from 'leafer-ui'

const leafer = new Leafer({ view: window })

leafer.on('pointer.down', () => {
  console.log('down')
})

命名修改

修改事件名称常量,如 PointerEvent.DOWN = 'pointerdown',可以改变事件名称字面值, 一般用于兼容您已有的业务代码。

冒泡与捕获

UI 事件均支持冒泡与捕获阶段, 通过 e.stop()e.stopNow() ) 来阻止事件传递。

新增属性

相关按钮是否被按下: leftmiddlerightspaceKey

import { Leafer, PointerEvent } from 'leafer-ui'

const leafer = new Leafer({ view: window })

leafer.on(PointerEvent.DOWN, (e: PointerEvent) => {
  if (e.left) {
    console.log('当前只按下鼠标左键')
  }
})

配对事件

在做 web 开发时,经常会遇到这样的问题:比如元素 pointer.down 触发后,因为拖拽或pointer.cancel,不能完全保证元素会触发 pointer.up

针对这种情况,我们在设计事件系统时进行了相关的事件处理,尤其是针对拖拽过程,确保元素 pointer.down 触发后,即使移动了位置抬起、取消,元素也一定会触发 pointer.up

关键方法

on(type: string | string[], listener: IEventListener, options?: IEventListenerOptions | boolean): void

侦听事件

import { Leafer, Rect, PointerEvent } from 'leafer-ui'

const leafer = new Leafer({ view: window })

const rect = new Rect({
  x: 100,
  y: 100,
  width: 200,
  height: 200,
  fill: '#32cd79', // 背景色
  draggable: true,
})

leafer.add(rect)

// bubble
rect.on(PointerEvent.DOWN, () => {
  console.log('冒泡事件')
})

// capture
function downCapture(): void {
  console.log('捕获事件')
}

rect.on(PointerEvent.DOWN, downCapture, true)
// 等于 rect.on(PointerEvent.DOWN, downCapture, { capture: true })

off(type: string | string[], listener: IEventListener, options?: IEventListenerOptions | boolean): void

关闭侦听事件

once(type: string | string[], listener: IEventListener, capture?: boolean): void

只侦听一次事件

function upOnce(): void {
  console.log('只触发一次up')
}

rect.once(PointerEvent.DOWN, upOnce)
// 等于 rect.on(PointerEvent.DOWN, downCapture, { once: true })

emit(type: string, event?: IEvent | IObject, capture?: boolean): void

手动广播事件, event 参数可以为一个自定义的 object 数据对象

rect.on('customEvent', (data) => {
  console.log(data.text)
})

rect.emit('customEvent', { text: '这是一个自定义的事件' })

emitEvent(event?: IEvent, capture?: boolean): void

手动触发事件,event 参数必须为 IEvent 对象

rect.emitEvent(new PointerEvent())

hasEvent(type: string, capture?: boolean): boolean

检查当前元素是否包含某种事件类型,如: PointerEvent.DOWN

capture 不存在时,将同时检查冒泡事件和捕获事件

if (rect.hasEvent(PointerEvent.DOWN)) {
  console.log('rect已侦听pointerdown事件')
}

简洁方法

on__(type: string | string[], listener: IEventListener, bind?: IObject, options?: IEventListenerOptions | boolean): IEventListenerId

侦听事件, 相对于 on()增加了 this 的 bind 对象,并返回侦听 id 用于 off(id)

off__(id: IEventListenerId | IEventListenerId[]): void

关闭侦听事件


使用简洁模式的事件侦听示例:

import { Leafer, Rect, PointerEvent, IEventListenerId } from 'leafer-ui'

class RectListener {
  private rect: Rect
  private events: IEventListenerId[]

  constructor(rect: Rect) {
    this.rect = rect
    this.addListener()
  }

  private addListener(): void {
    const { rect } = this
    this.events = [
      rect.on__(PointerEvent.ENTER, this.enter, this),
      rect.on__(PointerEvent.LEAVE, this.leave, this),
      rect.on__(PointerEvent.DOWN, this.down, this),
      rect.on__(PointerEvent.UP, this.up, this),
    ]
  }

  private removeListener(): void {
    this.rect.off__(this.events)
  }

  private enter(): void {
    this.rect.fill = '#42dd89'
  }

  private leave(): void {
    this.rect.fill = '#32cd79'
  }

  private down(): void {
    this.rect.fill = '#229d49'
  }

  private up(): void {
    this.rect.fill = '#42dd89'
  }

  public destroy(): void {
    this.removeListener()
    this.rect = undefined
  }
}

const leafer = new Leafer({ view: window })

const leaf = new Rect({
  x: 100,
  y: 100,
  width: 200,
  height: 200,
  fill: '#32cd79',
  draggable: true,
})

leafer.add(leaf)

new RectListener(leaf)

对比原始模式:

import { Leafer, Rect, PointerEvent } from 'leafer-ui'

class RectListener {
  private rect: Rect

  constructor(rect: Rect) {
    this.rect = rect
    this.addListener()
  }

  private addListener(): void {
    const { rect } = this
    this.enter = this.enter.bind(this)
    this.leave = this.leave.bind(this)
    this.down = this.down.bind(this)
    this.up = this.up.bind(this)

    rect.on(PointerEvent.ENTER, this.enter)
    rect.on(PointerEvent.LEAVE, this.leave)
    rect.on(PointerEvent.DOWN, this.down)
    rect.on(PointerEvent.UP, this.up)
  }

  private removeListener(): void {
    const { rect } = this
    rect.off(PointerEvent.ENTER, this.enter)
    rect.off(PointerEvent.LEAVE, this.leave)
    rect.off(PointerEvent.DOWN, this.down)
    rect.off(PointerEvent.UP, this.up)
  }

  private enter(): void {
    this.rect.fill = '#42dd89'
  }

  private leave(): void {
    this.rect.fill = '#32cd79'
  }

  private down(): void {
    this.rect.fill = '#229d49'
  }

  private up(): void {
    this.rect.fill = '#42dd89'
  }

  public destroy(): void {
    this.removeListener()
    this.rect = undefined
  }
}

const leafer = new Leafer({ view: window })

const leaf = new Rect({
  x: 100,
  y: 100,
  width: 200,
  height: 200,
  fill: '#32cd79',
  draggable: true,
})

leafer.add(leaf)

new RectListener(leaf)

简洁模式下的代码量更少, 原始模式下的性能更好,占用内存少, 请根据情况灵活使用