D3學習之動畫和變換

D3學習之動畫和變換

##(17.02.27-02.28)

主要學習到了D3對動畫和緩動函數的一些應用,結合前面的選擇器、監聽事件、自定義插值器等,拓展了動畫的效果和樣式。算法

主要內容

  • 單元素動畫
  • 多元素動畫
  • 使用緩動函數
  • 使用中間幀函數
  • 使用級聯過渡
  • 使用選擇器過渡
  • 監聽過渡事件
  • 自定義插值器
  • 使用計時器

爲何須要動畫?

人類視覺系統是一個精妙的信息處理器,所以推向能夠傳遞海量信息,而且移動的圖像更能在短期內傳達更多的信息。的確,在世界不斷的演變過程當中,人類的視覺系統也在不斷地進化,對於移動的物體,它可以更好地聚焦。 -Parent R.2012app

D3的動畫與過渡

D3過渡使咱們能夠在網頁上使用HTML或SVG創造計算機動畫。D3過渡實現了一種基於插值的動畫(Interpolation-based Animation)。因此D3動畫的基礎是插值。dom

單元素動畫

body.append("div") 
    .classed("box", true)
    .style("background-color", "#e9967a") 
    .transition()     //使用d3.selection.transition函數來定義一個過渡
    .duration(duration)    //使用duration函數來設置過渡效果的持續時間
    .style("background-color", "#add8e6") 
    .style("margin-left", "600px") 
    .style("width", "100px")
    .style("height", "100px")
    .transition()      //要實現單元素連續動畫就直接加在後面
    .duration(duration)
    .style("background-color", "#19e549")
    .style("margin-top", "100px")
    .style("margin-left","100px")
    .style("width", "500px")
    .style("height", "50px");

由於是動畫因此很差截圖顯示,從代碼上也能夠看出是一個長方形變成正方形再變成長方形,同時伴隨着顏色的變化的過程。函數

多元素動畫

原理和單元素相似,可是操做對象變成了一個集合而不是單一的元素:學習

selection   //selection是元素的集合
        .transition().duration(duration) // <-D
        .style("top", function (d) {
            return chartHeight - barHeight(d) + "px";   //top是距離頂的距離
        })
        .style("left", function (d, i) {    //更新left值和height值
            return barLeft(i) + "px";
        })
        .style("height", function (d) {
            return barHeight(d) + "px";
        })
        .style("background-color",randomColor())
        .select("span")
        .text(function (d) {
            return d.value;
        });   //d.value,d是字典

緩動函數

過渡是和時間相關的函數,它將時間進度映射到數值的變化,造成了對象的運動(若是數值表明位置)或者形變(若是數值描述視覺屬性)。時間是均勻變化的,換句話說時間是均勻的,然而結果並不老是須要均勻的。緩動正是控制這一映射,並提供靈活性的典型技術。當一個過渡生成均勻的值變化時,咱們稱之爲線性緩動。動畫

D3內部已經實現了一部分緩動函數及其過渡效果(linear線性,cubic立方,sin正弦等),同時支持自定義函數(代碼內的B處)。this

var data = [ // <-A
        "linear", "cubic", "cubic-in-out", 
        "sin", "sin-out", "exp", "circle", "back", 
        "bounce",
        function(t){ // <-B
            return t * t;
        }
    ]

ease( )的傳入參數是一個字符串,D3會找到同名的緩動函數,不然默認使用線性緩動。而在實現上,須要注意的有一點:spa

d3.selectAll("div").each(function(d){
    d3.select(this)
        .transition().ease(d) //這裏ease()函數不能用上面each()這種方式
        .duration(5000)
        .style("left", "10px");
});

ease()函數不支持如下這種方式,即便用一個函數來定義不一樣的緩動效果:設計

d3.selectAll("div").ease(function(d){
            return d;})
        .duration(5000)
        .style("left", "10px");
});

D3還提供了緩動模式修飾符,它可以和任意緩動函數結合起來,造成特殊的效果,例如sin-out或者quad-out-in。現有的模式修飾符有以下幾個:3d

  • in:默認
  • out:反向
  • in-out:鏡像
  • out-in:反向鏡像

使用中間幀計算

中間幀一詞源於「inbetween」,inbetween是傳統動畫行業的一種通用實踐,當時主設計師建立完關鍵幀後,再由工做人員在其中插入一些中間幀。這一律念被引入現代計算機動畫中,用來表明插入中間幀的各類技術和算法。
如下函數建立了一個自定義中間幀計算函數:

body.append("div").append("input")
    .attr("type", "button")
    .attr("class", "countdown")
    .attr("value", "0")
    .transition().duration(duration).ease("linear")
        .styleTween("width", widthTween) 
        .attrTween("value", valueTween);    //valueTween即中間幀計算函數

來看看valueTween():經過量化尺度對傳入的時間參數插值,最終生成了跳躍的整數效果。

function valueTween(){
    var interpolate = d3.scale.quantize() // 定義了量化尺度,設置了定義域和值域
        .domain([0, 1])
        .range([1, 2, 3, 4, 5, 6, 7, 8, 9]);
    
    return function(t){ // <-D
        return interpolate(t);
    };
}

效果以下:

級聯過濾

級聯過濾的做用就是將複雜的過渡效果進行封裝,從而能夠重複使用,保證了複雜過渡效果的可重用性,這一特性很好地實現了DRY原則(Don't repeat yourself)。

function teleport(s) {  //複雜的過渡效果
    s.transition().duration(300) 
        .style("width", "200px")
        .style("height", "1px")
        .transition().duration(100) 
        .style("left", "600px")
        .transition().duration(300) 
        .style("left", "800px")
        .style("height", "80px")
        .style("width", "80px")
        .transition().duration(300)
        .style("left", "680px")
        .style("height", "1px")
        .style("width", "200px")
        .style("top", "80px")
        .transition().duration(100)
        .style("left", "10px")
        .transition().duration(300)
        .style("height", "80px")
        .style("width", "80px")
        .style("top", "10px");
}

調用這一函數:

body.append("div")
    .attr("class", "dong")
    .style("position", "fixed")
    .style("background-color", "steelblue")
    .style("left", "10px")
    .style("width", "80px")
    .style("height", "80px")
    .call(teleport);    //經過call函數來調用級聯過濾

使用選擇性過渡

選擇性過渡用於對特定選集的部分子集應用過渡效果。如下選擇器限定了data值爲「cat」的對象纔會移動。

.transition() // <- A
            .duration(duration)
                .style("left", "10px")
        .filter(function(d){return d == "Cat";}) // <- B  d就是數據值  選擇器
            .transition() // <- C
            .duration(duration)
                .style("left", "500px");

效果如圖:

級聯過濾

級聯過濾的做用就是將複雜的過渡效果進行封裝,從而能夠重複使用,保證了複雜過渡效果的可重用性,這一特性很好地實現了DRY原則(Don't repeat yourself)。

function teleport(s) {  //複雜的過渡效果
    s.transition().duration(300) 
        .style("width", "200px")
        .style("height", "1px")
        .transition().duration(100) 
        .style("left", "600px")
        .transition().duration(300) 
        .style("left", "800px")
        .style("height", "80px")
        .style("width", "80px")
        .transition().duration(300)
        .style("left", "680px")
        .style("height", "1px")
        .style("width", "200px")
        .style("top", "80px")
        .transition().duration(100)
        .style("left", "10px")
        .transition().duration(300)
        .style("height", "80px")
        .style("width", "80px")
        .style("top", "10px");
}

調用這一函數:

body.append("div")
    .attr("class", "dong")
    .style("position", "fixed")
    .style("background-color", "steelblue")
    .style("left", "10px")
    .style("width", "80px")
    .style("height", "80px")
    .call(teleport);    //經過call函數來調用級聯過濾

使用選擇性過渡

選擇性過渡用於對特定選集的部分子集應用過渡效果。如下選擇器限定了data值爲「cat」的對象纔會右移。

.transition() // <- A
            .duration(duration)
                .style("left", "10px")
        .filter(function(d){return d == "Cat";}) // <- B  d就是數據值  選擇器
            .transition() // <- C
            .duration(duration)
                .style("left", "500px");

效果如圖:

監聽過渡事件

監聽用於在觸發特定動做後進行相應的操做或者在過渡時進行不一樣的處理。

.transition().duration(duration) 
            .delay(1000) 
            .each("start", function(){ // 在過渡開始(start)時觸發,修改text值
                console.log(arguments);
                d3.select(this).text(function (d, i) {
                    return "transitioning";
                });
            })
            .each("end", function(){ // 在過渡動畫結束時修改text值爲done
                d3.select(this).text(function (d, i) {
                    return "done";
                });
            })

效果以下:


實現自定義插值器

先看看自定義插值器的實現:

d3.interpolators.push(function(a, b) { // <-A
  var re = /^([a-z])$/, ma, mb;
  if ((ma = re.exec(a)) && (mb = re.exec(b))) {
    a = a.charCodeAt(0);
    var delta = a - b.charCodeAt(0);
    return function(t) {
      return String.fromCharCode(Math.ceil(a - delta * t));
    };
  }
});     //自定義差值器自動加入全局,而且優先被選取(相似棧結構)

這裏定義的是一個a-z的插值器,設定了檢查和範圍,那麼在調用時只要符合參數的範圍(a-z),D3會調用相應的插值器。

countdown.attr("type", "button")
    .attr("class", "countdown")
    .attr("value", "a")   //a
    .transition().ease("linear") 
    .duration(25000).delay(1000)
    .attr("value", "z"); //z

效果如圖:

使用定時器

D3定時器函數做爲實現D3過渡的底層結構,能夠幫助咱們更靈活地建立自定義動畫。
在下面這個例子中,咱們構造一個自定義動畫,用來顯示不斷變化的從0到100的數字。

function countup(target){ 
    d3.timer(function(){ 
        var value = countdown.attr("value");
        if(value == target) return true;  
        countdown.attr("value", ++value);         
    });
}

d3.timer()函數接受一個自定義函數,而且當即反覆調用這一函數,直到該函數返回true爲止,所以當value等於target(也就是100)時,纔會返回true,不然就會一直自加。

最後

已掌握了對過渡和動畫的初步使用,在之後具體實現中應多思考來實現更復雜的效果。

相關文章
相關標籤/搜索