隨機數(shù)+坐標軸+散點圖
//Width and height
var w = 1000;
var h = 300;
var padding = 30;
//Dynamic, random dataset
var dataset = []; //Initialize empty array
var numDataPoints = 50; //Number of dummy data points to create
var xRange = Math.random() * 1000; //Max range of new x values
var yRange = Math.random() * 1000; //Max range of new y values
for (var i = 0; i < numDataPoints; i++) { //Loop numDataPoints times
var newNumber1 = Math.floor(Math.random() * xRange); //New random integer
var newNumber2 = Math.floor(Math.random() * yRange); //New random integer
dataset.push([newNumber1, newNumber2]); //Add new number to array
}
//Create scale functions
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d[0]; })])
.range([padding, w - padding * 2]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d[1]; })])
.range([h - padding, padding]);
var aScale = d3.scaleSqrt()
.domain([0, d3.max(dataset, function (d) { return d[1]; })])
.range([0, 10]);
//Define X axis
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
//Define Y axis
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create circles
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function (d) {
return xScale(d[0]);
})
.attr("cy", function (d) {
return yScale(d[1]);
})
.attr("r", function (d) {
return aScale(d[1]);
});
//Create X axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
- 更新散點圖翎冲,支持數(shù)據(jù)更新和動態(tài)比
單擊上方的文本可以生成新數(shù)據(jù)并更新圖表
更新數(shù)據(jù)后,使用了動畫過渡
更新x和y軸的比例尺
<p>單擊此文本以使用新數(shù)據(jù)值更新圖表</p>
//Width and height
var w = 500;
var h = 300;
var padding = 30;
//Dynamic, random dataset
var dataset = []; //Initialize empty array
var numDataPoints = 50; //Number of dummy data points to create
var maxRange = Math.random() * 1000; //Max range of new values
for (var i = 0; i < numDataPoints; i++) { //Loop numDataPoints times
var newNumber1 = Math.floor(Math.random() * maxRange); //New random integer
var newNumber2 = Math.floor(Math.random() * maxRange); //New random integer
dataset.push([newNumber1, newNumber2]); //Add new number to array
}
//Create scale functions
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d[0]; })])
.range([padding, w - padding * 2]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d[1]; })])
.range([h - padding, padding]);
//Define X axis
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
//Define Y axis
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create circles
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function (d) {
return xScale(d[0]);
})
.attr("cy", function (d) {
return yScale(d[1]);
})
.attr("r", 2);
//Create X axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
d3.select("p")
.on("click", function () {
//New values for dataset
var numValues = dataset.length;
var maxRange = Math.random() * 1000;
dataset = [];
for (var i = 0; i < numValues; i++) {
var newNumber1 = Math.floor(Math.random() * maxRange);
var newNumber2 = Math.floor(Math.random() * maxRange);
dataset.push([newNumber1, newNumber2]);
}
//Update scale domains
xScale.domain([0, d3.max(dataset, function (d) { return d[0]; })]);
yScale.domain([0, d3.max(dataset, function (d) { return d[1]; })]);
//Update all circles
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.attr("cx", function (d) {
return xScale(d[0]);
})
.attr("cy", function (d) {
return yScale(d[1]);
});
//Update X axis
svg.select(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
//Update Y axis
svg.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
});
- 加載動畫在過渡開始和結束時執(zhí)行操作
//Update all circles
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
// 在過渡開始時執(zhí)行
.on("start", function () {
d3.select(this) // 選擇 'this',即當前元素
// .duration(150) //不能在這里添加新的過渡動畫尊蚁,
.attr("fill", "magenta")
.attr("r", 3)
})
.attr("cx", function (d) {
return xScale(d[0]);
})
.attr("cy", function (d) {
return yScale(d[1]);
})
// 在過渡結束時執(zhí)行
.on("end", function () {
d3.select(this)
.attr("fill", "black")
.attr("r", 2)
})
在默認情況下亡笑,任何元素在任意時刻都只能有一個過渡效果。新過渡效果會打斷并覆蓋原來的過渡效果;
與jQuery不同横朋,默認情況下仑乌,jQuery 會把所有過渡效果排成隊列,然后一個接一個地執(zhí)行它們琴锭。換句話說晰甚,執(zhí)行新過渡不會自動中斷原有過渡。這種設計有時候會導致令人討厭的界面行為决帖,比如鼠標放到菜單上再離開后厕九,菜單并不會馬上就淡出,而是必須完全淡入之后再淡出地回。
由于過渡中存在的這個問題扁远,一定要記住只能在 ("start", ...) 里面執(zhí)行立即變換俊鱼,而不能再添加任何過渡效果。
- 優(yōu)雅收場
在執(zhí)行("end", ...) 的時候畅买,主過渡已經(jīng)結束了并闲,因此再執(zhí)行新過渡不會產(chǎn)生任何副作用。
.on("end", function () {
d3.select(this)
.transition() //新增
.duration(1000) //新增
.attr("fill", "black")
.attr("r", 2);
});
單擊 p 元素的文本谷羞,會發(fā)現(xiàn):
圓形立即變成粉紅并增大帝火;
圓形過渡到新位置;
圓形過渡到原來的顏色和大小湃缎。
- 或者順序地使用連綴的方式
svg.selectAll("circle")
.data(dataset)
.transition() //過渡1
.duration(1000)
.on("start", function() {
d3.select(this)
.attr("fill", "magenta")
.attr("r", 7);
})
.attr("cx", function(d) {
return xScale(d[0]);
})
.attr("cy", function(d) {
return yScale(d[1]);
})
.transition() //過渡2
.duration(1000)
.attr("fill", "black")
.attr("r", 2);
- 在剪切路徑中包含可見元素
好在 SVG 支持剪切路徑犀填,也就是 Photoshop 或 Illustrator 中的蒙 版。剪切路徑就是一個 SVG 元素雁歌,可以包含可見的元素宏浩,并與這個可見元素一起構成可以應用到其他元素的剪切路徑或蒙版。在把蒙版應用到某個元素時靠瞎,只有落在該蒙版圖形內(nèi)部的像素才會顯示比庄。
與 g 元素相似,clipPath 本身也不可見乏盐,但它可以包含可見的元素(這些元素用作蒙版)
<clipPath id="chart-area">
<rect x="30" y="30" width="410" height="240"></rect>
</clipPath>
注意外面的 clipPath 元素有一個 ID佳窑,值為 chart-area。后面會通過這個 ID 來引用它父能。這個剪切路徑內(nèi)部有一個矩形神凑,將被用作蒙版。
- 使用剪切路徑的步驟如下:
定義clipPath并給它一個ID
在這個clipPath中放一個可見的元素(通常是一個 rect何吝,但也可以包含
circle 和其他可見元素)溉委;
//定義剪切路徑
svg.append("clipPath")//創(chuàng)建新的clipPath元素
.attr("id", "chart-area")//為它指定ID,后面綁定時使用
.append("rect")//在clipPath中爱榕,創(chuàng)建并添加新的rect
.attr("x", padding) //設置rect的位置和大小
.attr("y", padding)
.attr("width", w - padding * 3)
.attr("height", h - padding * 2);
我們希望把這個蒙版應用給所有圓形瓣喊,可以分別為每個圓形都添加一個對該clipPath 的引用。但把所有圓形都放到一個分組 g 中黔酥,然后給這個分組添加引用藻三, 會讓代碼更清晰也更簡單:
- 在需要使用蒙版的元素上添加一個對 clipPath 的引用。
//創(chuàng)建圓形
svg.append("g") //創(chuàng)建新的g元素跪者,包裹所有的circle
.attr("id", "circles") //指定它的ID為circles
.attr("clip-path", "url(#chart-area)") //添加對 clipPath 的引用
.selectAll("circle")
.data(dataset)
……
這個 g 元素的 clip?path 屬性指向了新創(chuàng)建的剪切路徑棵帽,語法有點不太常見:url(#chart-area)。但這都是 SVG 標準規(guī)定的渣玲。
最終結果就是逗概,當圓形跑到圖表區(qū)域的邊界時,超出邊界的部分會被剪切掉忘衍。注意最上方和最右邊的那些圓形仗谆。
<p>Click on this text to update the chart with new data values as many times as you like!</p>
//Width and height
var w = 500;
var h = 300;
var padding = 30;
//Dynamic, random dataset
var dataset = []; //Initialize empty array
var numDataPoints = 50; //Number of dummy data points to create
var maxRange = Math.random() * 1000; //Max range of new values
for (var i = 0; i < numDataPoints; i++) { //Loop numDataPoints times
var newNumber1 = Math.floor(Math.random() * maxRange); //New random integer
var newNumber2 = Math.floor(Math.random() * maxRange); //New random integer
dataset.push([newNumber1, newNumber2]); //Add new number to array
}
//Create scale functions
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d[0]; })])
.range([padding, w - padding * 2]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function (d) { return d[1]; })])
.range([h - padding, padding]);
//Define X axis
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
//Define Y axis
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Define clipping path
svg.append("clipPath")
.attr("id", "chart-area")
.append("rect")
.attr("x", padding)
.attr("y", padding)
.attr("width", w - padding * 3)
.attr("height", h - padding * 2);
//Create circles
svg.append("g")
.attr("id", "circles")
.attr("clip-path", "url(#chart-area)")
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function (d) {
return xScale(d[0]);
})
.attr("cy", function (d) {
return yScale(d[1]);
})
.attr("r", 2);
//Create X axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Create Y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
//On click, update with new data
d3.select("p")
.on("click", function () {
//New values for dataset
var numValues = dataset.length;
var maxRange = Math.random() * 1000;
dataset = [];
for (var i = 0; i < numValues; i++) {
var newNumber1 = Math.floor(Math.random() * maxRange);
var newNumber2 = Math.floor(Math.random() * maxRange);
dataset.push([newNumber1, newNumber2]);
}
//Update scale domains
xScale.domain([0, d3.max(dataset, function (d) { return d[0]; })]);
yScale.domain([0, d3.max(dataset, function (d) { return d[1]; })]);
//Update all circles
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.on("start", function () {
d3.select(this)
.attr("fill", "magenta")
.attr("r", 7);
})
.attr("cx", function (d) {
return xScale(d[0]);
})
.attr("cy", function (d) {
return yScale(d[1]);
})
.on("end", function () {
d3.select(this)
.transition()
.duration(1000)
.attr("fill", "black")
.attr("r", 2);
});
//Update X axis
svg.select(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
//Update Y axis
svg.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
});