[數據可視化]d3js源碼,selection(四)添加事件,控制流,局部變量

selection模塊結構

  • 選擇元素
  • 修改元素
  • 加入數據
  • 處理事件(本章的內容)
  • 控制流(本章的內容)
  • 局部變量(本章的內容)
  • 命名空間

事件處理

出於交互考慮,selection支持監聽(listening)和分派(dispatching)事件。javascript

selection.on(typenames[, listener[, options]])

根據typenames向元素添加或刪除事件監聽,類型爲字符串如click、mouseover、DOM event type都支持,能夠經過click.foo添加回調函數,多個name用空格分隔。
當一個事件分派到元素上,lisetener老是會看到最後的數據,若是以前已經綁定了事件會更新事件,傳入null刪除事件,.foo刪除全部監聽。
option設置監聽器的特性,capturing仍是passive,詳情查看addEventListener
未指定監聽器時會返回監聽器(第一個)。
源碼實現中給selection添加了一個__on的數組對象存儲事件,並區分了事件類型進行調用filterContextListener/contextListener,首先解析typenames,而後根據參數去判斷添加刪除html

export default function (typename,value,capture) {
    var typenames = parseTypenames(typename + ""),i,n=typenames.length,t;//解析typename字符串
    if(arguments.length < 2){//一個參數返回,查找對應的事件的value
        var on=this.node().__on;
        if(on) for(var j=0,m=on.length,o;j<m;++j){
            for(i=0,o=on[j];i<n;++i){
                if((t=typenames[i]).type === o.type && t.name === o.name){
                    return o.value;
                }
            }
        }
        return ;
    }
    //2個及以上參數的狀況
    on = value?onAdd :  onRemove;//這裏判斷value爲null時刪除不然添加事件
    if(capture == null) capture =false;
    for(i=0;i<n;i++) this.each(on(typenames[i],value,capture));//這裏對每一個typename查找一次,效率比較低,n*n
    return this;

}
複製代碼

selection.dispatch(type[, parameters])

按順序將指定類型的自定義事件發送到元素,parameters有這幾個選項,bubbles,反向樹的順序即冒泡事件,cancelable,event.preventDefault阻止事件傳播,detail,關聯自定義數據,若是是個函數計算後返回上述選項。
代碼實現上使用CustomEvent、createEvent、initEvent建立自定義事件。java

function dispatchEvent(node,type,params) {
    var window = defaultView(node),
        event=window.CustomEvent;
    if(typeof event === "function"){
        event = new event(type,params);//>>>
    }else{
        event = window.document.createEvent("Event");
        if(params) event.initEvent(type,params.bubbles,params.cancelable),event.detail=params.detail;
        else event.initEvent(type,false,false);
    }
    node.dispatchEvent(event);
}
複製代碼

d3.event

保存當前的事件(存在時),這是在調用監聽器時設置並在終止後重置,使用此選項能夠訪問標準事件字段如 event.timeStamp event.preventDefault,雖然可使用event.pageX,event.pageY,但使用d3.mouse,d3.touch,d3.touches,轉化爲本地座標更容易。node

d3.customEvent(event, listener[, that[, arguments]])

使用that做爲this傳入arguments參數調用event事件,在調用期間,d3.event設置爲指定事件,函數返回後回調,此外,將event.sourceEvent設爲先前的d3.event,容許用戶自定義事件對原生事件的引用,返回listener返回的值。
源碼d3.event就是存儲在on.js中的一個變量,回調的這種方式不會影響原來的值;git

export function customEvent(event1,listener,that,args) {
    var event0=event;
    event1.sourceEvent=event;//sourceEvent做者定義的事件層級,返回最頂端的事件
    event=event1;
    try{
        return listener.apply(that,args);
    }finally {
        event=event0;
    }
}
複製代碼

d3.mouse(container)

返回當前事件對應容器(HTML或SVG元素)的[x,y]座標,點擊後的座標位置轉換方法以下(point.js)github

export default function (node, event) {
    var svg=node.ownerSVGElement || node;

    if(svg.createSVGPoint){
        var point=svg.createSVGPoint();
        point.x  =event.clientX, point.y=event.clientY;
        point = point.matrixTransform(node.getScreenCTM().inverse());
        return [point.x,point.y];
    }
    var rect=node.getBoundingClientRect();
    return [event.clientX - rect.left - node.clientLeft,event.clientY-rect.top-node.clientTop];
}
複製代碼

d3.touch(container[, touches], identifier)

返回指定識別碼identifier容器下的觸摸座標[x,y],若是在指定identifier下touch事件,返回null,這對於忽略在觸摸移動實踐中只有部分觸摸移動的狀況很是有效,默認爲changeTouches。
代碼實現中利用changeTouches,去尋找指定的事件點發生的位置。web

d3.touches(container[, touches])

以雙元數組形式返回觸摸事件點[[x1, y1], [x2, y2], …]。segmentfault

d3.clientPoint(container, event)

返回指定事件相對於容器的座標[x,y],如上面(point.js)數組

控制流

對於高級用法,selections提供了用戶自定義控制流的方法。bash

selection.each(function)

相似於數組的each,爲每一個元素調用function,代碼實現固然爲selection本身的數據結構而配置的。對於建立同時訪問父元素和子元素上下文很是有用。

parent.each(function(p, j) {
  d3.select(this)
    .selectAll(".child")
      .text(d => `child ${d.name} of ${p.name}`);
});
複製代碼

稍微分析一下,this是parent的上下文,咱們再去選擇chiild此時text中的d的this就成了孩子節點的上下文,在此同時也是能夠訪問p.namem,就達到同時訪問的目的了。

selection.call(function[, arguments…])

調用指定函數一次,傳入參數返回selection,至關於手動調用函數,但這個方法支持鏈式調用,例以下面可重用的設置樣式的函數:

function name(selection, first, last) {
  selection
      .attr("first-name", first)
      .attr("last-name", last);
}
複製代碼

而後調用:

d3.selectAll("div").call(name, "John", "Snow");
複製代碼

與下面結果相同:

name(d3.selectAll("div"), "John", "Snow");
複製代碼

惟一的區別是selection.call爲了支持鏈式調用會返回selection,而不是name的返回值。

selection.empty()

若是selection不包含元素,返回true

selection.nodes()

返回選擇器中全部元素

selection.node()

返回選擇器第一個非空節點

selection.size()

返回選擇器中節點的數量

局部變量

d3 local容許你定義獨立於數據的狀態,例如在渲染時間序列時,您可能須要使用相同的x比例尺以及不一樣的y比例尺

d3.local()

聲明一個新的局部變量

const foo = d3.local();
複製代碼

與var相同的是每個local是惟一的,不一樣的是local的範圍由DOM肯定。

local.set(node, value)

給該local設置值,並返回value:

selection.each(function(d) { foo.set(this, d.value); });
複製代碼

若是隻添加了一個變量,也能夠這樣:

selection.property(foo, d => d.value);
複製代碼

從這就能夠看出,咱們能夠給元素添加多個local。

local.get(node)

返回節點上的local值,若是未定義local返回最近祖先上的值,沒有返回undefined。

local.remove(node)

刪除該節點上的local值,若是刪除以前定義了local則返回true,該節點沒有local返回false,祖先上的local不受影響,所以local.get仍返回值。

local.toString()

返回local自動生成標識符,相似於一個識別碼用來惟一之別nodelocal上的value的值,換能夠經過element[local](這裏是由於local也是個對象,傳入時會自動去調用toString)或者selection.property來設置或獲取local的value。 源代碼:

var nextId=0;
export default function local() {
    return new Local;
}

function Local() {
    this._="@"+(++nextId).toString(36);
    //Number.prototype.toString([radix])中的
    // radix 指定要用於數字到字符串的轉換的基數(從2到36)。
    // 若是未指定 radix 參數,則默認值爲 10。全部不在範圍內的基數會報錯
}
Local.prototype=local.prototype={
    constructor:Local,
    get:function (node) {
        var id=this._;
        while(!(id in node)) if (!(node=node.parentNode)) return;
        return node[id];
    },
    set:function (node,value) {
        return node[this._]=value;
    },
    remove:function (node) {
        return this._ in node && delete node[this._];
    },
    toString: function () {
        return this._;
    }
}
複製代碼

總之local方便用戶在局部範圍進行操做,這個範圍指元素節點,實現原理就是給節點添加屬性,爲了名字不重複定義了一個自動生成的標識符this._來取得惟一的名字,並把他們統一到一個對象Local中。

總結

selection的事件處理,最核心的是on方法,靈活多變的事件添加,其次就是用戶自定義事件的customEvent和觸發dispatch,對於在svg畫圖的狀況下,咱們很是有必有知道的是觸發事件相對於svg下的座標,mouse、touch、touches、clienPoint解決了這個問題。 對於控制流中的函數,是做者在完成整個selection模塊時產生的一些中間組件,做者把他們抽象出來,可讓用戶在使用時更加便利。
對於局部變量部分,local的定義就是一個對selection局部進行操做的變量。
到這裏就把selection模塊的內容分4篇文章說完了,其中最主要的仍是數據綁定部分,這也是d3數據驅動的最大的特點,算是d3js的核心了。而後目前爲止完成了3個模塊的源碼解析,任重而道遠啊!

深度閱讀:

源碼及解析:github.com/dongoa/evs-…
MouseEvent.relatedTarget事件屬性:developer.mozilla.org/en-US/docs/…
timeStamp 事件屬性:www.w3cschool.cn/jsref/event…
CustomEvent自定義事件:www.jianshu.com/p/1cf1c80c0…
changedTouches:www.cnblogs.com/mengff/p/60…
SVGElement:developer.mozilla.org/zh-CN/docs/…
SVG窗口座標系的轉換:blog.iderzheng.com/something-a…
offsetLeft,Left,clientLeft的區別:www.cnblogs.com/panjun-Done…
changedTouches:segmentfault.com/q/101000000…
TouchEvent:developer.apple.com/documentati…

相關文章
相關標籤/搜索