# d3过渡
过渡是一种特殊类型的选择器(selection),这种操作符的应用随时间平滑而不是瞬间变换。D3过渡实现了一种基于插值的动画。
npm install d3-ease
transition 开启过渡
ease 选择自带的动效函数
duration 持续时间
delay 允许进行转换过了一段时间
- d3.selection.data的第二个参数,通常这个函数称为对象标识函数:确保返回对象的一致性,更稳定的绑定数据和图形。
- d3.selection.exit函数会返回一个选集,在remove之前是可以操作的
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Multi-Element Transition</title>
<link rel="stylesheet" type="text/css" href="../../css/styles.css"/>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<script type="text/javascript">
var id= 0,
data = [],
duration = 500,
chartHeight = 100,
chartWidth = 680;
for(var i = 0; i < 20; i++) push(data);
function render(data) {
var selection = d3.select("body")
.selectAll("div.v-bar")
.data(data, function(d){
console.log(d)
return d.id;
}); // <-A
// enter
selection.enter()
.append("div")
.attr("class", "v-bar")
.style("z-index", "0")
.style("position", "fixed")
.style("top", chartHeight + "px")
.style("left", function(d, i){
return barLeft(i+1) + "px"; // <-B
})
.style("height", "0px") // <-C
.append("span");
// update
selection
.transition().duration(duration) // <-D
.style("top", function (d) {
return chartHeight - barHeight(d) + "px";
})
.style("left", function(d, i){
return barLeft(i) + "px";
})
.style("height", function (d) {
return barHeight(d) + "px";
})
.select("span")
.text(function (d) {return d.value;});
// exit
selection.exit()
.transition().duration(duration) // <-E
.style("left", function(d, i){
return barLeft(-1) + "px"; //<-F
})
.remove(); // <-G
}
function push(data) {
data.push({
id: ++id,
value: Math.round(Math.random() * chartHeight)
});
}
function barLeft(i) {
return i * (30 + 2);
}
function barHeight(d) {
return d.value;
}
setInterval(function () {
data.shift();
push(data);
render(data);
}, 2000);
render(data);
d3.select("body")
.append("div")
.attr("class", "baseline")
.style("position", "fixed")
.style("z-index", "1")
.style("top", chartHeight + "px")
.style("left", "0px")
.style("width", chartWidth + "px");
</script>
</body>
</html>
- .ease函数不支持d选项,所以如果自定义的写法,可以使用each函数里使用
- 当d3.select("body").selectAll("div").data(data).enter().append("div")之后,其实再选择d3.select("body").selectAll("div")因为粘连
_data_,可以直接获取到数据d了
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Easing</title>
<link rel="stylesheet" type="text/css" href="../../css/styles.css"/>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<script type="text/javascript">
var data = [ // <-A
{name: 'Linear', fn: d3.easeLinear},
{name: 'Cubic', fn: d3.easeCubic},
{name: 'CubicIn', fn: d3.easeCubicIn},
{name: 'Sin', fn: d3.easeSin},
{name: 'SinIn', fn: d3.easeSinIn},
{name: 'Exp', fn: d3.easeExp},
{name: 'Circle', fn: d3.easeCircle},
{name: 'Back', fn: d3.easeBack},
{name: 'Bounce', fn: d3.easeBounce},
{name: 'Elastic', fn: d3.easeElastic},
{name: 'Custom', fn: function(t){ return t * t; }}// <-B
],
colors = d3.scaleOrdinal(d3.schemeCategory10);
console.log(d3.selectAll("div"))
d3.select("body").selectAll("div")
.data(data) // <-C
.enter()
.append("div")
.attr("class", "fixed-cell")
.style("top", function (d, i) {
return i * 40 + "px";
})
.style("background-color", function (d, i) {
return colors(i);
})
.style("color", "white")
.style("left", "500px")
.text(function (d) {
return d.name;
});
console.log(d3.selectAll("div"))
d3.selectAll("div").each(function(d){
// console.log(d)
d3.select(this)
.transition().ease(d.fn) // <-D
.duration(1500)
.style("left", "10px");
});
d3.selectAll("div").style("color",d=>{
console.log(d)
})
// d3.selectAll("div").transition().ease(d.fn) // does not work
// .duration(1500)
// .style("left", "10px");
</script>
</body>
</html>
# x.ease(type[, arguments…])
x.transition()
.ease(d3.easeLinear)
- d3.easeLinear() 函数用于线性缓和到特定元素的过渡效果。该线性缓动函数返回一个恒等函数
- 参数:该函数不接受任何参数。
- 返回值:此函数不返回任何值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width,
initial-scale=1.0">
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<h3 style="color:green">GeeksforGeeks</h3>
<svg width="500px" height="500px">
</svg>
<script>
var svg = d3.select("svg");
svg.append("circle")
.attr("r", 30)
.attr("cy",40)
.attr("cx",30)
.attr("fill", "green")
.transition()
// Use of easeLinear
.ease(d3.easeCubic)
.duration(2000)
.attr("cy",40)
.attr("cx",200);
console.log(d3.easeElastic)
</script>
</body>
</html>
- d3.easeBounce 弹跳缓动
# d3-transition中的start和end事件
d3.select("#k2")
.transition()
.delay(200)
.on("start", function() {
console.log(d3.select(this))//指的数据组
d3.select(this).style("color", "green");
})
.on("end",()=>{
console.log(this)//window
console.log(d3.select(this))
// d3.select(this).style("color", "orange"); //end事件再去修改会出错
})
.transition()
.delay(2000)
.style("color", "red");
# d3-transition delay
//整体延迟
.transition()
.duration(1000)
.delay(500)
//个别指定延迟
.transition()
.duration(1000)
.delay(funtion(d,i){
return 200*i;
})
# d3-transition active和interrupt
- selection.interrupt([name]) 中断选择集上活动的名为name的过渡。如果name所表示的过渡还没有开始,则也不用开始了。如果没有指定name,则使用null。
selection.interrupt().selectAll("*").interrupt();
返回指定节点上名为name的活动的过渡。如果没有指定name则使用null。这个方法可以方便的创建链式过渡,比如创建一个循环disco过渡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content=
"width=device-width, initial-scale=1.0">
</head>
<body>
<svg height=500 width=500>
<circle
cx="25" cy="25" r="20"
></circle>
<circle
cx="75" cy="25" r="20"
></circle>
<circle
cx="125" cy="25" r="20"
></circle>
</svg>
<!-- Fetching from CDN of D3.js -->
<script type="text/javascript"
src="https://d3js.org/d3.v6.min.js">
</script>
<script>
let n=0;
d3.selectAll("circle").transition()
.delay(function(d, i) { return i * 1000; })
.on("start", function repeat() {
n++;
console.log(d3.interrupt)
console.log( d3.active(this))
console.log(n)
if(n>=5){
console.log(d3.select(this))
d3.selectAll("circle").interrupt();
console.log("ok")
return
}
d3.active(this)
.style("fill", "red")
.transition()
.delay(600)
.style("fill", "green")
.transition()
.delay(800)
.style("fill", "blue")
.transition()
.delay(1000)
.on("start", repeat)
})
</script>
</body>
</html>
# 中间帧 tween
tween函数式一个工厂函数,用来构造执行中间帧计算的最终函数 ;
.attrTween('d', function(d, i) {
let interpolate = d3.interpolate(d.startAngle, d.endAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc(d);
}
})
如下实际中间帧计算函数通过量化尺度对传入的时间参数进行插值。
<!DOCTYPE html>
<html>
<body>
<link rel="stylesheet" type="text/css" href="./style.css"/>
<script src="http://d3js.org/d3.v5.min.js"></script>
<script type="text/javascript">
var body = d3.select("body"), duration = 5000;
body.append("div").append("input")
.attr("type", "button")
.attr("class", "countdown")
.attr("value", "0")
.style("width", "150px")
.transition().duration(duration).ease(d3.easeLinear)
.style("width", "400px")
.attr("value", "9");
body.append("div").append("input")
.attr("type", "button")
.attr("class", "countdown")
.attr("value", "0")
.transition().duration(duration).ease(d3.easeLinear)
.styleTween("width", widthTween) // <- A
.attrTween("value", valueTween); // <- B
function widthTween(a){
var interpolate = d3.scaleQuantize()
.domain([0, 1])
.range([150, 200, 250, 350, 400]);
return function(t){
return interpolate(t) + "px";
};
}
function valueTween(){
var interpolate = d3.scaleQuantize() // <-C
.domain([0, 1])
.range([1, 2, 3, 4, 5, 6, 7, 8, 9]);
return function(t){ // <-D
return interpolate(t);
};
}
</script>
</body>
</html>
# d3.interpolateRgb
d3.selectAll("circle").transition()
.delay(function(d, i) { return i * 1000; })
.on("start", function repeat() {
n++;
console.log(d3.interrupt)
console.log( d3.active(this))
console.log(n)
d3.active(this)
.style("fill", "red")
.transition()
.delay(800)
.styleTween("fill", function() {
return d3.interpolateRgb("purple","orange");
})
.transition()
.delay(1000)
.on("start", repeat)
})
transition.attrTween("fill", function() {
return function(t) {
return "hsl(" + t * 360 + ",100%,50%)";
};
});
transition.styleTween("fill", function() {
return d3.interpolateRgb(this.style.fill, "blue");
});
# d3监听过渡事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Transition Events</title>
<link rel="stylesheet" type="text/css" href="../../css/styles.css"/>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<script type="text/javascript">
var body = d3.select("body"), duration = 3000;
var div = body.append("div")
.classed("box", true)
.style("background-color", "steelblue")
.style("color", "white")
.text("waiting") // <-A
.transition().duration(duration) // <-B
.delay(1000) // <-C
.on("start", function(){ // <-D
d3.select(this).text(function (d, i) {
return "transitioning";
});
})
.on("end", function(){ // <-E
d3.select(this).text(function (d, i) {
return "done";
});
})
.style("margin-left", "600px");
</script>
</body>
</html>
# d3.timer
npm install d3-timer
用于在特定时间间隔后运行计时器功能。计时器将在指定的延迟后运行。给出的延迟以毫秒为单位。
d3.timer(function[,delay])
let func=function(e) {
console.log(e);
if (e>300){
console.log("Timer stopped")
timer.stop();
}
}
// Delay of 2000ms
var timer = d3.timer(func, 2000);
# timer.stop()
用于停止当前正在进行的计时器函数,从而防止对该函数的进一步调用。仅当计时器尚未停止时,此功能才起作用。
# timer.restart() 重启某个函数
- timer.restart(callback, delay);
- callback:它是在特定延迟后停止或启动的功能。
- delay:这是该功能将被执行或停止的时间
# d3.timeout(cb[,delay[,time]])
在执行完第一次回调之后会自动 stops。可以作为 setTimeout 的替换,因为这个方法不会在页面处于非活动状态时运行(内部使用requestAnimationFrame实现)。callback 参数为 elapsed (定义定时器到回调执行之间的时间)。
# d3.interval(callback[, delay[, time]])
这个方法可以用来替换 setInterval 因为基于 requestAnimationFrame 实现,不会在后台执行