上次我们讨论了几何意义上的线之后,我们接下来进行讨论关于graphic包下的文件,这里面主要有drag、gradient、line、link、shape、transform等几个文件夹,分别代表着不同的图像图像操作,下面我们主要学习这几个包中的js内容。
该包中主要有两个文件,分别是DragDropMgr.js和Draggable.js文件,其中DragDropMgr是全局拖拽管理器,支持同时拖拽多个元素,只需按住Ctrl 键就可以实现多选;另一个js文件是具体实现拖拽功能,所有需要拖拽功能的元素都可以mixin此类的实现,此实现依赖我们之前讲的事件处理机制,mixin此实现的类需要预先mixin eventful接口的默认实现,才能提供事件支持。下面我们对这两个类进行分开详细讨论。
这是一个工具类,作为一个全局的拖拽管理器,在该类中主要有构造函数、设置监听、停止监听、开始拖动、在拖动过程中、拖动结束、清除选中、获取当前选中的所有元素等这几个方法的作用,下面我们主要针对其中的关键信息进行分析。
开始监听的主要目的就是绑定鼠标监听,通过按下鼠标来触发事件,具体就是以下代码:进行了mousedown的事件绑定。
startListen() {
this.dispatcher.on('mousedown', this.dragStart, this);
return this;
}
该函数主要的作用是停止监听,这个函数主要就是将所有监听都删除,同时将所有都归0,具体如下
stopListen() {
this.clearSelectionMap();
this._draggingItem = null;
this._dropTarget = null;
this._x = 0;
this._y = 0;
this.dispatcher.off('mousedown', this.dragStart, this);
this.dispatcher.off('pagemousemove', this.dragging, this);
this.dispatcher.off('pagemouseup', this.dragEnd, this);
return this;
}
通过解除mousedown监听、pagemousemove监听、pagemouseup监听这三个监听,然后将解除后的对象返回,这是该函数的主要目的。
这三个函数发生在一个周期中,分别发生在拖拽开始、拖拽过程中以及拖拽结束。在拖拽开始时,先对该对象进行事件的绑定,首先判断事件是否被派遣,然后再将事件进行判断,判断是否按下Ctrl键以及选择元素,然后进行选择操作,分别确定他们的坐标,然后进行绑定pagemousemove、pagemouseup的操作,然后通过一个循环遍历所有的dispatcher,派遣到相应的任务事件。
dragStart(e) {
let el = e.target;
let event = e.event;
this._draggingItem = el;
if (!el) {
this.clearSelectionMap();
return;
}
if (!el.draggable) {
return;
}
if (!event.ctrlKey && !this.selectionMap.get(el.id)) {
this.clearSelectionMap();
}
el.dragging = true;
this.selectionMap.set(el.id, el);
this._x = e.offsetX;
this._y = e.offsetY;
this.dispatcher.on('pagemousemove', this.dragging, this);
this.dispatcher.on('pagemouseup', this.dragEnd, this);
this.selectionMap.forEach(el => {
this.dispatcher.dispatchToElement(this.normalizeParam(el, e), 'dragstart', e.event);
});
}
然后就是拖拽中,这个没有什么内容,主要是确定当前的位置信息,然后绑定鼠标的位置重新赋给当前的拖拽物体,注意要时刻判断当前对象的draggingTtem书香是否与当前的dispatcher相同,然后在进行当前对象的dispatcher的事件分发代理,把事件分发给 canvas 中绘制的元素。这样就实现了实时的拖拽操作。
最后就是结束拖拽,该过程更为简单,主要是解除pagemousemove以及pagemouseup,然后判断当前的dispatcher绑定一个事件分发代理,把事件分发给 canvas 中绘制的元素。
dragEnd(e) {
this.selectionMap.forEach(el => {
el.dragging = false;
this.dispatcher.dispatchToElement(this.normalizeParam(el, e), 'dragend', e.event);
});
this.dispatcher.off('pagemousemove', this.dragging, this);
this.dispatcher.off('pagemouseup', this.dragEnd, this);
if (this._dropTarget) {
this.dispatcher.dispatchToElement(this.normalizeParam(this._dropTarget, e), 'drop', e.event);
}
this._dropTarget = null;
}
该js主要是全局拖拽管理器,处理的是对于一般情况下的拖拽,包括开始监听、停止监听、开始拖拽、正在拖拽、结束拖拽等事件的处理,实现全局拖拽的大致实现。
该文件主要是一个提供拖拽功能,所有需要拖拽的都可以混入该类的实现,该类实现了依赖事件机制。其中最重要的就只有一个move方法。
在该方法中,主要的目的就是移动元素,获取元素的x、y坐标,以及事件,然后通过匹配,是否是水平或者竖直然后对x以及y进行赋值,然后this.trigger('moving', this);
设置位置属性动画时触发移动事件。this.dirty();
把元素标记为dirty,然后更新数据,同时触发afterMove事件,调用afterMove函数,这个函数相当于一个钩子函数,主要是在元素发生移动之后执行。类似于Vue.js中的vm生命周期中的钩子。
以上我们主要对Drag中的两个js文件进行了分析,这两个文件主要是设计拖拽功能的实现,我们从他的关键方法进行了分析处理,然后这些内容又有对于事件部分的涉及,在了解事件处理以后,我们对于这部分内容的认知更加明朗,分析也更加容易。