1. 程式人生 > >JavaScript-D3入門五-資料和字串陣列對映到顏色模型

JavaScript-D3入門五-資料和字串陣列對映到顏色模型

基於《JavaScript-D3入門四-事件繫結》中的程式碼。

這裡只要soccerviz2.js代替上一篇的soccerviz.js,就可以在Web Server上執行html,一邊修改程式碼一邊觀察執行效果。

下面是soccerviz2.js原始碼

//soccerviz2.js
//演示顏色對映的幾種方式

function createSoccerViz() {
    d3.csv("/data/worldcup.csv", data => {overallTeamViz(data)})

    function overallTeamViz(incomingData) {
        //根據incomingData的行數新增g.overallG
        d3.select("svg")
          .append("g")
          .attr("id", "teamsG")
          .attr("transform", "translate(50,300)")
          .selectAll("g")
          .data(incomingData)
          .enter()
          .append("g")//設incomingData中資料的行數為n,新增n個g物件。
          .attr("class", "overallG")
          .attr("transform", (d, i) =>"translate(" + (i * 50) + ", 0)");//為n個物件設定各自的(x,y)屬性

        //返回class屬性為overallG的所有g節點,即teamG是一個含g節點的陣列。
        var teamG = d3.selectAll("g.overallG");

        //每個g.overallG物件下新建circle,帶動畫效果
        teamG
         .append("circle")
         .attr("r", 0)
         .transition().delay((d, i) => i * 100).duration(500)//多個circle依次變大
         .attr("r", 40)
         .transition().duration(500)
         .attr("r", 20);

        //g.overallG下新建text
        teamG
         .append("text")
         .attr("y", 30)
         .text(d => d.team);


        //改變現有DOM元素在父節點中的先後位置,
        //感覺在這裡呼叫沒有什麼意義,所以把下面兩行註釋掉了
        //d3.select("g.overallG").raise();//往後移動
        //d3.select("g.overallG").lower();//往前移動

        //使text節點不處理滑鼠事件。據說這樣的設定可以讓滑鼠事件穿透當前節點(即當前節點就像不存在一樣)。
        teamG.select("text").style("pointer-events", "none");

        //incomingData第一行資料除了team,region其它column都作為key
        //dataKeys=["win","loss","draw","points","gf","ga","cs","yc","rc"]
        const dataKeys = Object.keys(incomingData[0])
            .filter(d => d !== "team" && d !== "region");

        //在body下新增<div id='controls'>節點,在這個節點下再新增button節點
        //Hint: 由於button.teams是不存在的,所以會新增button節點.
        d3.select("#controls").selectAll("button.teams")
           .data(dataKeys).enter()
           .append("button")
           .on("click", buttonClick)//事件繫結
        .html(d => d);

        //點選某個button物件更新檢視
        /*
        function buttonClick(datapoint) {
            //datapoint的值為["win","loss","draw","points","gf","ga","cs","yc","rc"]陣列中的元素!
            var maxValue = d3.max(incomingData, d => parseFloat(d[datapoint]))


            //color map            
            //採用RGB顏色模型過度,中間色是灰色的,不符合我們的需求
            //var ybRamp = d3.scaleLinear()
            //    .domain([0, maxValue]).range(["blue", "yellow"]);
            

            //中間色用HSL(hue,saturation,lightness)顏色模型過度
            var ybRamp = d3.scaleLinear()
                .interpolate(d3.interpolateHsl)//.interpolate(d3.interpolateLab)//D3也支援Lab色彩模型
                .domain([0, maxValue]).range(["yellow", "blue"]);

            //linear map
            var radiusScale = d3.scaleLinear()
              .domain([0, maxValue]).range([2, 20])

            //更新circle物件的屬性,帶動畫效果
            d3.selectAll("g.overallG").select("circle")
                .transition().duration(1000)//這行添加了動畫效果
                .attr("r", d => {
                    if (radiusScale(d[datapoint]) > 0)
                        return radiusScale(d[datapoint]);
                    else
                        return 0;//r屬性不接受negative number.
                }).style("fill", d => ybRamp(d[datapoint]));
    }//buttonClick
    */
        /*
        function buttonClick(datapoint) {
            var maxValue = d3.max(incomingData, function (el) {
                return parseFloat(el[datapoint])
            })

            //字串陣列到顏色模型的對映.
            var tenColorScale = d3.scaleOrdinal()
                .domain(["UEFA", "CONMEBOL", "CAF", "AFC"])
                .range(d3.schemeCategory10);

            
            //沒在domain裡的字串,顏色對映為unknown裡指定的顏色.
            //var tenColorScale = d3.scaleOrdinal()
            // .domain(["UEFA", "CONMEBOL"])
            // .range(d3.schemeCategory10)
            // .unknown("#c4b9ac");
            

            //radius對映
            var radiusScale = d3.scaleLinear().domain([0, maxValue]).range([2, 20]);

            d3.selectAll("g.overallG").select("circle").transition().duration(1000)
                .style("fill", p => tenColorScale(p.region))
                .attr("r", p => radiusScale(p[datapoint]))
        }
        */

        function buttonClick(datapoint) {
            var maxValue = d3.max(incomingData, d => parseFloat(d[datapoint]));

            //colorbrewer.Reds[3]為包含多個顏色的一個數組。
            var colorQuantize = d3.scaleQuantize()
              .domain([0, maxValue]).range(colorbrewer.Reds[3]);

            var radiusScale = d3.scaleLinear()
              .domain([0, maxValue]).range([2, 20]);

            d3.selectAll("g.overallG").select("circle").transition().duration(1000)
              .style("fill", d => colorQuantize(d[datapoint]))
              .attr("r", d => radiusScale(d[datapoint]))
        }

        //highlight同一個region的物件。
        teamG.on("mouseover", highlightRegion)
        function highlightRegion(d) {
            //這裡的this為teamG陣列中的某一個物件。
            d3.select(this).select("text").classed("active", true).attr("y", 10);
            //修改circle的class屬性。            
            /* 通過修改circle的class屬性來改變circle屬性
               d3.selectAll("g.overallG").select("circle").each(function (p) {
                   p.region == d.region ?
                        d3.select(this).classed("active", true) :
                        d3.select(this).classed("inactive", true);
            });
            */
            //通過修改circle的fill風格來改變circle的顏色            
            var teamColor = d3.rgb("#75739F");
            d3.selectAll("g.overallG").select("circle").style("fill", p => p.region === d.region ?
                  teamColor.darker(.75) : teamColor.brighter(.5));            

            //把this節點,移到parent節點的最後,否則會被同級g節點遮住。
            this.parentElement.appendChild(this);
        }//highlightRegion

        //
        teamG.on("mouseout", unHighlight)
        function unHighlight() {
            d3.selectAll("g.overallG").select("circle").attr("class", "");//修改class屬性的另一種方式
            d3.selectAll("g.overallG").select("text")
            .classed("active", false).attr("y", 30);
        }//unHighlight
    }//overallTeamViz
}//createSoccerViz