D3學習之動畫和變換


D3學習之動畫和變換

(17.02.27-02.28)


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

主要內容

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

為什么需要動畫?

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

D3的動畫與過渡

D3過渡使我們可以在網頁上使用HTML或SVG創造計算機動畫。D3過渡實現了一種基於插值的動畫(Interpolation-based Animation)。所以D3動畫的基礎是插值。

單元素動畫

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處)。

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

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

 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。現有的模式修飾符有如下幾個:

  • 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,否則就會一直自加。

最后

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM