让图表动起来

链接 原作者用的是d3.js v3版本写的,我在学习的过程中用的是v4版的库 ,所以把原作者的代码中涉及v3的部分替换为了v4的API。在这里非常感谢原作者十二月的咖啡馆无私的分享!

第九课 让图表动起来

D3 支持制作动态的图表。有时候,图表的变化需要缓慢的发生,以便于让用户看清楚变化的过程,也能给用户不小的友好感。

什么是动态效果

前面几章制作的图表是一蹴而就地出现,然后绘制完成后不再发生变化的,这是静态的图表。

动态的图表,是指图表在某一时间段会发生某种变化,可能是形状、颜色、位置等,而且用户是可以看到变化的过程的。

例如,有一个圆,圆心为 (100, 100)。现在我们希望圆的 x 坐标从 100 移到 300,并且移动过程在** 2 秒**的时间内发生。

这种时候就需要用到动态效果,在 D3 里我们称之为过渡(transition)

实现动态的方法

D3 提供了 4 个方法用于实现图形的过渡:从状态 A 变为状态 B

transition()

启动过渡效果。

其前后是图形变化前后的状态(形状、位置、颜色等等),例如:

 .attr("fill","red")|         //初始颜色为红色
.transition()               //启动过渡
.attr("fill","steelblue")   //终止颜色为铁蓝色
dog | bird | cat

D3 会自动对两种颜色(红色和铁蓝色)之间的颜色值(RGB值)进行插值计算,得到过渡用的颜色值。我们无需知道中间是怎么计算的,只需要享受结果即可。

duration()

指定过渡的持续时间,单位为毫秒。

如 duration(2000) ,指持续 2000 毫秒,即 2 秒。

ease()

指定过渡的方式,常用的有:

  • easeLinear:普通的线性变化
  • easeCircle:慢慢地到达变换的最终状态
  • easeElastic:带有弹跳的到达最终状态
  • easeBounce:在最终状态处弹跳几次

调用时,格式形如: ease(d3.easeBounce)。

delay()

指定延迟的时间,表示一定时间后才开始转变,单位同样为毫秒。此函数可以对整体指定延迟,也可以对个别指定延迟。

例如,对整体指定时:

.transition()
.duration(1000)
.delay(500)

如此,图形整体在延迟 500 毫秒后发生变化,变化的时长为 1000 毫秒。因此,过渡的总时长为1500毫秒。

又如,对一个一个的图形(图形上绑定了数据)进行指定时:

.transition()
.duration(1000)
.delay(funtion(d,i){
    return 200*i;
})

如此,假设有 10 个元素,那么第 1 个元素延迟 0 毫秒(因为 i = 0),第 2 个元素延迟 200 毫秒,第 3 个延迟 400 毫秒,依次类推….整个过渡的长度为 200 * 9 + 1000 = 2800 毫秒。

实现简单的动态效果

下面将在 SVG 画布里添加三个圆,圆出现之后,立即启动过渡效果。

第一个圆,要求移动 x 坐标。

var circle1 = svg.append("circle")
        .attr("cx", 100)
        .attr("cy", 100)
        .attr("r", 45)
        .style("fill","green");

//在1秒(1000毫秒)内将圆心坐标由100变为300
circle1.transition()
    .duration(1000)
    .attr("cx", 300);

第二个圆,要求既移动 x 坐标,又改变颜色。


//在1.5秒(1500毫秒)内将圆心坐标由100变为300,
//将颜色从绿色变为红色
circle2.transition()
    .duration(1500)
    .attr("cx", 300)
    .style("fill","red");

第三个圆,要求既移动 x 坐标,又改变颜色,还改变半径。

var circle3 = svg.append("circle")... //与第一个圆一样,省略部分代码

//在2秒(2000毫秒)内将圆心坐标由100变为300
//将颜色从绿色变为红色
//将半径从45变成25
//过渡方式采用bounce(在终点处弹跳几次)
circle3.transition()
    .duration(2000)
    .ease(d3.easeBounce)
    .attr("cx", 300)
    .style("fill","red")
    .attr("r", 25);

给柱形图加上动态效果

在上一章完整柱形图的基础上稍作修改,即可做成一个带动态效果的、有意思的柱形图。

在添加文字元素和矩形元素的时候,启动过渡效果,让各柱形和文字缓慢升至目标高度,并且在目标处跳动几次。

对于文字元素,代码如下:

.attr("y",function(d){
    var min = yScale.domain()[0];
    return yScale(min);
})
.transition()
.delay(function(d,i){
    return i * 200;
})
.duration(2000)
.ease("bounce")
.attr("y",function(d){
    return yScale(d);
});

文字元素的过渡前后,发生变化的是 y 坐标。其起始状态是在 y 轴等于 0 的位置(但要注意,不能在起始状态直接返回 0,要应用比例尺计算画布中的位置)。终止状态是目标值。

对于矩形元素,思想与文字元素一样,只是在计算起始状态时要稍微复杂一些,请读者自行研读源代码。

完整代码

<html>
<head>
    <meta charset="utf-8">
    <title>完整的柱形图</title>
</head>

<style>
    .axis path,
    .axis line{
        fill: none;
        stroke: black;
        shape-rendering: crispEdges;
    }

    .axis text {
        font-family: sans-serif;
        font-size: 11px;
    }

    .MyRect {
        fill: #ff0f99;
    }

    .MyText {
        fill: white;
        text-anchor: middle;
    }
</style>

<body>
<script src="d3.js"></script>
<script>

    //画布大小
    var width = 400;
    var height = 400;

    //在 body 里添加一个 SVG 画布
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height);

    //画布周边的空白
    var padding = {left:30, right:30, top:20, bottom:20};

    //定义一个数组
    var dataset = [10, 20, 30, 40, 33, 24, 12, 5];

    //x轴的比例尺
    var xScale = d3.scaleBand()
        .domain(d3.range(dataset.length))
        .range([0, width - padding.left - padding.right]);

    //y轴的比例尺
    var yScale = d3.scaleLinear()
        .domain([0,d3.max(dataset)])
        .range([height - padding.top - padding.bottom, 0]);

    //定义x轴
    var xAxis = d3.axisBottom(xScale);

    //定义y轴
    var yAxis = d3.axisLeft(yScale);

    //矩形之间的空白
    var rectPadding = 4;

    //添加矩形元素
    var rects = svg.selectAll(".MyRect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("class","MyRect")
        .attr("transform","translate(" + padding.left + "," + padding.top + ")")
        .attr("x", function(d,i){
            return xScale(i) + rectPadding/2;
        } )
        .attr("width", xScale.bandwidth()- rectPadding )
        .attr("y",function(d){
            var min=yScale.domain()[0];
            return yScale(min);
        })
        .attr("height", function(d){
            return 0;
        })
        .transition()
        .delay(function (d,i) {
            return i *200;
        })
        .duration(2000)
        .ease(d3.easeBounce)
        .attr("y",function (d) {
            return yScale(d);
        })
        .attr("height",function (d) {
            return height - padding.top - padding.bottom - yScale(d);
        });

    //添加文字元素
    var texts = svg.selectAll(".MyText")
        .data(dataset)
        .enter()
        .append("text")
        .attr("class","MyText")
        .attr("transform","translate(" + padding.left + "," + padding.top + ")")
        .attr("x", function(d,i){
            return xScale(i) + rectPadding/2;
        } )
        .attr("y",function(d){
            return yScale(d);
        })
        .attr("dx",function(){
            return (xScale.bandwidth()- rectPadding)/2;
        })
        .attr("dy",function(d){
            return 20;
        })
        .text(function(d){
            return d;
        })
        .attr("y",function(d){
            var min = yScale.domain()[0];
            return yScale(min);
        })
        .transition()
        .delay(function(d,i){
            return i * 200;
        })
        .duration(2000)
        .ease(d3.easeBounce)
        .attr("y",function(d){
            return yScale(d);
        });

    //添加x轴
    svg.append("g")
        .attr("class","axis")
        .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
        .call(xAxis);

    //添加y轴
    svg.append("g")
        .attr("class","axis")
        .attr("transform","translate(" + padding.left + "," + padding.top + ")")
        .call(yAxis);

</script>
</body>
</html>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • 1. 动画与互动 在叙事结构中全面应用创意D3如何帮你在可视化图表中添加动画与互动用地理特征创建D3地图了解别人如...
    esskeetit阅读 834评论 1 0
  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,610评论 1 180
  • 昨天,久待寝室这样一个狭小的空间,总会有些许压抑和烦闷。于是,一个人,来了一场和校园池塘的独特约会。 从寝室出来,...
    下一站_ecc9阅读 160评论 0 0
  • J叔的漫画,擅谈情感 哈哈,J叔本来以为能借机亲到一个美女的, 不是美女一般的也行呀, 谁知道结果竟然是这样~ 原...
    J叔说星座阅读 249评论 0 0