OpenLayers的Interaction交互及事件機制解析

interaction

OpenLayers中表達交互功能的基類是interaction,它是一個虛基類,不負責實例化,交互功能都繼承該基類,實現它的子類包括:

  • DoubleClickZoom 雙擊放大交互功能;
  • DragAndDrop 以“拖文件到地圖中”的交互添加圖層;
  • DragBox 拉框,用於劃定一個矩形範圍,常用於放大地圖;
  • DragPan 拖拽平移地圖;
  • DragRotate 拖拽方式旋轉地圖;
  • DragRotateAndZoom 拖拽方式進行縮放和旋轉地圖;
  • DragZoom 拖拽方式縮放地圖;
  • Draw 繪製地理要素功能;
  • Extent 單擊並拖動地圖來繪製矢量框,並可編輯
  • KeyboardPan 鍵盤方式平移地圖;
  • KeyboardZoom 鍵盤方式縮放地圖;
  • Modify 更改要素;
  • MouseWheelZoom 鼠標滾輪縮放功能;
  • PinchRotate 手指旋轉地圖,針對觸摸屏;
  • PinchZoom 手指進行縮放,針對觸摸屏;
  • Pointer 鼠標的用戶自定義事件基類;
  • Select 選擇要素功能;
  • Snap 鼠標捕捉,當鼠標距離某個要素一定距離之內,自動吸附到要素。
  • Translate 拖拽移動選中的要素(新增)
用法
// 先實例化
var select = new Select(); 
var translate = new Translate({
  features: select.getFeatures(),
});
// 可以初始化時添加
var map = new Map({
  interactions: defaultInteractions().extend([select, translate]),
  layers: [raster, vector],
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2,
  }),
});
// 也可以後續追加
  map.addInteraction(select );
  map.addInteraction(translate );

默認交互

地圖初始化時,如果沒有設置交互類型,就會默認綁定一些預設的交互,包括:

  • DragRotate 拖拽旋轉
  • DoubleClickZoom 雙擊放大
  • DragPan 拖拽平移
  • PinchRotate 手指旋轉
  • PinchZoom 手指縮放
  • KeyboardPan 鍵盤平移
  • KeyboardZoom 鍵盤縮放
  • MouseWheelZoom 滾輪縮放
  • DragZoom 拖拽縮放

事件機制解析

看下ol的事件綁定和觸發,入口 Map.js

function Map(options) {
         // ...
        if (!options.interactions) {
            options.interactions = defaultInteractions({  // 沒有指定就加載默認交互
                onFocusOnly: true,
            });
        }
       // ...
    }

Map的父類 PluggableMap.js

// 構造函數
function PluggableMap(options) {
        var _this = _super.call(this) || this;
        var optionsInternal = createOptionsInternal(options);  // 屬性初始化
        _this.viewport_ = document.createElement('div');  // 地圖容器內的主div
        // 監聽各屬性更新事件
        _this.addEventListener(getChangeEventType(MapProperty.LAYERGROUP), _this.handleLayerGroupChanged_);
        _this.addEventListener(getChangeEventType(MapProperty.VIEW), _this.handleViewChanged_);
        _this.addEventListener(getChangeEventType(MapProperty.SIZE), _this.handleSizeChanged_);
        _this.addEventListener(getChangeEventType(MapProperty.TARGET), _this.handleTargetChanged_);
        // 通過setProperties更新屬性,觸發上面監聽事件
        // 這個方法
        _this.setProperties(optionsInternal.values);
}

// 地圖容器更新觸發的方法
PluggableMap.prototype.handleTargetChanged_ = function () {
        var targetElement = this.getTargetElement();  // 獲取到地圖容器
        targetElement.appendChild(this.viewport_);
        // 實例化一個瀏覽器事件處理類
        this.mapBrowserEventHandler_ = new MapBrowserEventHandler(this, this.moveTolerance_);
        for (var key in MapBrowserEventType) {  
                // 循環所有支持的瀏覽器事件類型,逐一綁定,觸發方法內循環遍歷綁定的interaction
                this.mapBrowserEventHandler_.addEventListener(MapBrowserEventType[key], this.handleMapBrowserEvent.bind(this));
        }
        // 綁定右擊事件
        this.viewport_.addEventListener(EventType.CONTEXTMENU, this.boundHandleBrowserEvent_, false);
        // 綁定滾輪事件
        this.viewport_.addEventListener(EventType.WHEEL, this.boundHandleBrowserEvent_, PASSIVE_EVENT_LISTENERS ? { passive: false } : false);
        // 綁定鍵盤事件
        this.keyHandlerKeys_ = [
                listen(keyboardEventTarget, EventType.KEYDOWN, this.handleBrowserEvent, this),
                listen(keyboardEventTarget, EventType.KEYPRESS, this.handleBrowserEvent, this),
        ];
        // 綁定resize事件
        if (!this.handleResize_) {
                this.handleResize_ = this.updateSize.bind(this);
                window.addEventListener(EventType.RESIZE, this.handleResize_, false);
        }
}

// 各交互事件觸發的方法
PluggableMap.prototype.handleMapBrowserEvent = function (mapBrowserEvent) {
        var interactionsArray = this.getInteractions().getArray();  // 取出所有綁定的 Interactions
        if (this.dispatchEvent(mapBrowserEvent) !== false) {
            for (var i = interactionsArray.length - 1; i >= 0; i--) {
                var interaction = interactionsArray[i];
                if (!interaction.getActive()) {  //判斷是否處於激活狀態
                    continue;
                }
                //調用執行各Interactions子類的處理方法
                var cont = interaction.handleEvent(mapBrowserEvent);  
                if (!cont) {
                    break;
                }
            }
        }
}

Interactions的子類DoubleClickZoom爲例,看事件響應部分

    DoubleClickZoom.prototype.handleEvent = function (mapBrowserEvent) {
        var stopEvent = false;
        // 判斷下是否觸發的是自己,再響應
        if (mapBrowserEvent.type == MapBrowserEventType.DBLCLICK) { 
            var browserEvent =  (mapBrowserEvent.originalEvent);
            var map = mapBrowserEvent.map;
            var anchor = mapBrowserEvent.coordinate;
            var delta = browserEvent.shiftKey ? -this.delta_ : this.delta_;
            var view = map.getView();
            zoomByDelta(view, delta, anchor, this.duration_);
            mapBrowserEvent.preventDefault();
            stopEvent = true;
        }
        return !stopEvent;
    };

可以看到 ol 的事件基本都是在viewporttarget上綁着,並沒有直接綁定在canvas上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章