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上

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