聊聊Canvas事件機制相關 (非API層,偏框架設計方面)

wlove發表於2022-03-11

image.png

以下分析均採用 Sigmajs 框架原始碼進行分析,有興趣的同學可以去檢視一下。 本文主要介紹下Canvas的事件機制,和一些設計思路。

圖形事件,設計思路及實現介紹。

圖形事件需要支援以下的內容:

  • 支援各類事件型別
  • 事件觸發機制
  • 事件衝突問題

image.png

事件型別

image.png

  • mouse

    • mousedown
    • mousemove
    • mouseup
    • mouseenter
    • mouseleave
    • dblclick
    • contextmenu
    • click
    • wheel
      • drag 基於mousedown move leave做二次開發.
      • dragstart
      • dragend

    image.png

  • touch

    • touchstart
    • touchend
    • touchcancel
    • touchmove

事件觸發機制

根據事件的觸發源不同可以分為兩種:

  • 圖形上觸發
  • Canvas上觸發
所有事件均在Canvas的DOM事件觸發基礎上實現。

以click為例:
滑鼠在畫布上點選 觸發Canvas DOM事件,然後與圖形進行碰撞 如果有就是圖形點選 沒有就是畫布點選。虛擬碼示例:

Canvas.addEventListener("click", function(){
      // MouseCoords == CanvasCoords
      // getShapeAtPoint(Shapes,CanvasCoords)
      if(true){
          // 如果碰撞到圖形
          this.emit("clickGraph",false);
          return
      }
      this.emit("clickCanvas",false);
      // 
}, false);

事件衝突問題

drag 和 click 的衝突

由於 canvas 是一個DOM節點, 不是每個圖元作為DOM 物件可以識別,所以無法通過 dragable 設定圖形是否可以拖拽,這時候使用觸控板時的點選和拖拽會衝突。

在使用 mousedown,mouseup 過程中進行模擬, 對於在兩者之間判定移動的距離和時間差進行一個判斷。
具體是Click還是drag事件。

click 和 dbclick的衝突

其實不算是衝突問題, 是對於dbclick的實現基於click那麼就需要對click事件有一個處理,方便識別。請看程式碼示例:

handleClick(e: MouseEvent): void {
    if (!this.enabled) return;

    this.clicks++;

    if (this.clicks === 2) {
      this.clicks = 0;
      if (typeof this.doubleClickTimeout === "number") {
        clearTimeout(this.doubleClickTimeout);
        this.doubleClickTimeout = null;
      }
      return this.handleDoubleClick(e);
    }

    setTimeout(() => {
      this.clicks = 0;
      this.doubleClickTimeout = null;
    }, DOUBLE_CLICK_TIMEOUT);

    if (this.draggedEvents < DRAGGED_EVENTS_TOLERANCE) this.emit("click", getMouseCoords(e, this.container));
  }
  

其他

關於事件的冒泡, 在Sigma中存在圖形和畫布,以及畫布做了分層。彼此存在事件先後(冒泡)順序。

最後

視覺化相關的架構設計,原始碼學習,日常開發。我會逐步進行深入分享。如果對你有幫助請關注我後續的內容。有需要的同學可以加一下我的聯絡方式(在我的主頁,拉你進群聊)。

相關文章