# Eventful 类

# 概述

src\mixin\Eventful.js

提供事件的绑定、解绑、触发事件等基础功能

# 关系

# 派生

# Element

Element 是可以被添加到场景中的元素

# Handler

Handler MVC 中的 controller, 用于 zrender 的事件监听、派发等

# HandlerProxy

HandlerProxy 负责将 DOM 中的事件进行统一处理,然后再次触发为 zrender 的标准事件

# Animation

Animation 动画, 值得关注的是,修改成 Dispatcher 然后再继承,容易遗漏。

# 构造函数

var Eventful = function (eventProcessor) {
    this._$handlers = {};
    this._$eventProcessor = eventProcessor;
}

# 方法

# one

单次触发绑定,触发后销毁。

one: function (event, query, handler, context) {
    return on(this, event, query, handler, context, true);
}

# on

绑定事件。

on: function (event, query, handler, context) {
    return on(this, event, query, handler, context, false);
}

# isSilent

是否绑定了事件。

isSilent: function (event) {
    var _h = this._$handlers;
    return !_h[event] || !_h[event].length;
}

# off

解绑事件。

流程:

  1. 如果没传 event , 删除所有的事件监听
  2. 如果没传 handler ,删除所有当前 event
  3. 遍历 event 的所有 handler 重新获取 handler 列表
off: function (event, handler) {
    var _h = this._$handlers;

    if (!event) {
        this._$handlers = {};
        return this;
    }

    if (handler) {
        if (_h[event]) {
            var newList = [];
            for (var i = 0, l = _h[event].length; i < l; i++) {
                if (_h[event][i].h !== handler) {
                    newList.push(_h[event][i]);
                }
            }
            _h[event] = newList;
        }

        if (_h[event] && _h[event].length === 0) {
            delete _h[event];
        }
    }
    else {
        delete _h[event];
    }

    return this;
}

# trigger

触发事件。

关于参数, 在触发事件的时候, 除了第一个参数是事件类型外,其他的参数不限,一般是一个,也可以多个。

官方文档为提及的(Element)参数:

  • opt.filter: function(type:string, query:string):boolean
  • opt.afterTrigger : function(type:string)
  • opt.normalizeQuery : function(host:Element, query: string): string

关于 query 参数的意义也明了了, 格式的话自己定义,反正都是要自己处理的。

流程:

  1. 查看是否绑定了事件, 未绑定事件退出
  2. 检查事件是否被过滤条件过滤
  3. 调用事件
  4. 如果有调用事件钩子函数,调用钩子函数
trigger: function (type) {
    var _h = this._$handlers[type];
    var eventProcessor = this._$eventProcessor;

    if (_h) {
        var args = arguments;
        var argLen = args.length;

        if (argLen > 3) {
            args = arrySlice.call(args, 1);
        }

        var len = _h.length;
        for (var i = 0; i < len;) {
            var hItem = _h[i];
            if (eventProcessor
                && eventProcessor.filter
                && hItem.query != null
                && !eventProcessor.filter(type, hItem.query)
            ) {
                i++;
                continue;
            }

            // Optimize advise from backbone
            switch (argLen) {
                case 1:
                    hItem.h.call(hItem.ctx);
                    break;
                case 2:
                    hItem.h.call(hItem.ctx, args[1]);
                    break;
                case 3:
                    hItem.h.call(hItem.ctx, args[1], args[2]);
                    break;
                default:
                    // have more than 2 given arguments
                    hItem.h.apply(hItem.ctx, args);
                    break;
            }

            if (hItem.one) {
                _h.splice(i, 1);
                len--;
            }
            else {
                i++;
            }
        }
    }

    eventProcessor && eventProcessor.afterTrigger
        && eventProcessor.afterTrigger(type);

    return this;
}

# triggerWithContext

最后一个参数是回调中的 this

triggerWithContext: function (type) {
    var _h = this._$handlers[type];
    var eventProcessor = this._$eventProcessor;

    if (_h) {
        var args = arguments;
        var argLen = args.length;

        if (argLen > 4) {
            args = arrySlice.call(args, 1, args.length - 1);
        }
        var ctx = args[args.length - 1];

        var len = _h.length;
        for (var i = 0; i < len;) {
            var hItem = _h[i];
            if (eventProcessor
                && eventProcessor.filter
                && hItem.query != null
                && !eventProcessor.filter(type, hItem.query)
            ) {
                i++;
                continue;
            }

            // Optimize advise from backbone
            switch (argLen) {
                case 1:
                    hItem.h.call(ctx);
                    break;
                case 2:
                    hItem.h.call(ctx, args[1]);
                    break;
                case 3:
                    hItem.h.call(ctx, args[1], args[2]);
                    break;
                default:
                    // have more than 2 given arguments
                    hItem.h.apply(ctx, args);
                    break;
            }

            if (hItem.one) {
                _h.splice(i, 1);
                len--;
            }
            else {
                i++;
            }
        }
    }

    eventProcessor && eventProcessor.afterTrigger
        && eventProcessor.afterTrigger(type);

    return this;
}

# 通用方法

# 绑定事件

参数:

  • eventful 继承了 eventful 的类的实例
  • event 事件名称
  • query 类似于 jquery 中的 on , query 类似于查询子项的查询字符串
  • handler 事件回调函数
  • context this 的指向
  • isOnce 是否单次执行

流程:

  1. 整理参数,如果 queryfunction 时,交换参数,类似于函数重载
  2. 事件名称和回调函数缺一不可
  3. 如果当前事件数组未创建,创建事件数组
  4. 检查当前回调函数是否已经绑定过,不会重复绑定
  5. 根据 handler.zrEventfulCallAtLast 属性判断插入方式
function on(eventful, event, query, handler, context, isOnce) {
    var _h = eventful._$handlers;

    if (typeof query === 'function') {
        context = handler;
        handler = query;
        query = null;
    }

    if (!handler || !event) {
        return eventful;
    }

    query = normalizeQuery(eventful, query);

    if (!_h[event]) {
        _h[event] = [];
    }

    for (var i = 0; i < _h[event].length; i++) {
        if (_h[event][i].h === handler) {
            return eventful;
        }
    }

    var wrap = {
        h: handler,
        one: isOnce,
        query: query,
        ctx: context || eventful,
        // FIXME
        // Do not publish this feature util it is proved that it makes sense.
        callAtLast: handler.zrEventfulCallAtLast
    };

    var lastIndex = _h[event].length - 1;
    var lastWrap = _h[event][lastIndex];
    (lastWrap && lastWrap.callAtLast)
        ? _h[event].splice(lastIndex, 0, wrap)
        : _h[event].push(wrap);

    return eventful;
}

# normalizeQuery

规范化 query 字符串, opt.normalizeQuery 提供

function normalizeQuery(host, query) {
    var eventProcessor = host._$eventProcessor;
    if (query != null && eventProcessor && eventProcessor.normalizeQuery) {
        query = eventProcessor.normalizeQuery(query);
    }
    return query;
}