数据可视化【七】 更新模式

在这里插入图片描述

Enter

以下面这个简单的代码进行分析

const svg = d3.select('svg');
// svg.style('background-color', 'red'); testconst height = +svg.attr('height');
//+ equals paresFloat()
const width = +svg.attr('width');const makeFruit = type =>( {type} );	//这种写法好像能够直接得到一个属性名为'type',值为变量type值的对象const fruits = d3.range(5).map(() => makeFruit('apple') );// console.log(fruits);svg.selectAll('circle').data(fruits).enter().append('circle').attr('cx', (d,i) => i*100+10).attr('cy', height/2).attr('r', 10).attr('fill', 'red');

在选择selectAll()以后相当于选择了上图的Elements部分,data(fruits)相当于上图的Data部分,在一起以后就得到了上面的Data Join。然后我们再根据自己的需求选择想要的部分进行处理。

  • Enter 是不在Elements中的Data中的元素的集合
  • Update是既在Elments集合中又在Data集合中的元素集合
  • Exit是仅在Elements集合中的元素集合

我们得到了集合以后就能使用append函数和attr函数进行设置。
在这里插入图片描述

Exit

经常我们绑定的数据发生变化以后我们想要对已经不在数据集中的元素进行处理。这个时候只需要再绑定一次数据,然后对其中的exit()部分进行设置即可。例如下面的例子:

const svg = d3.select('svg');
// svg.style('background-color', 'red'); testconst height = +svg.attr('height');
//+ equals paresFloat()
const width = +svg.attr('width');const render = (selection, { fruits }) => {const circles = selection.selectAll('circle').data(fruits);circles.enter().append('circle').attr('cx', (d,i) => i*100+10).attr('cy', height/2).attr('r', 10).attr('fill', 'red');circles.exit().remove();// .attr('fill', 'black');
}const makeFruit = type =>( {type} );	//这种写法好像能够直接得到一个属性名为'type',值为变量type值的对象const fruits = d3.range(5).map(() => makeFruit('apple') );render(svg, { fruits } );// console.log(fruits);setTimeout(()=>{fruits.pop();render(svg, { fruits } );
}, 1000);

实现的效果就是原本屏幕上有五个点,经过一秒钟以后最后一个点消失了。

Update

如果我们对数据中的一部分做了一些改动,又希望能够在图形中显示出来,就可以在update中进行修改。需要注意的是:在我们绑定数据以后默认就是update集合。例如:

const svg = d3.select('svg');
// svg.style('background-color', 'red'); testconst height = +svg.attr('height');
//+ equals paresFloat()
const width = +svg.attr('width');const colorScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range(['red', 'yellow']);const radiusScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range([50, 20]);const render = (selection, { fruits }) => {const circles = selection.selectAll('circle').data(fruits);circles.enter().append('circle').attr('cx', (d,i) => i*150+50).attr('cy', height/2).attr('r', d => radiusScale(d.type)).attr('fill', d => colorScale(d.type) );circles.exit().remove();// .attr('fill', 'black');circles.attr('cx', (d,i) => i*150+50).attr('cy', height/2).attr('r', d => radiusScale(d.type)).attr('fill', d => colorScale(d.type) );
}const makeFruit = type =>( {type} );	//这种写法好像能够直接得到一个属性名为'type',值为变量type值的对象const fruits = d3.range(5).map(() => makeFruit('apple') );render(svg, { fruits } );// console.log(fruits);setTimeout(()=>{fruits.pop();render(svg, { fruits } );
}, 1000);setTimeout(()=>{fruits[2].type = 'lemon';render(svg, { fruits } );
}, 2000);

最终的效果:
在这里插入图片描述
在这里插入图片描述

Merge

有时候我们需要对多个部分进行相同的操作,当然可以对每个部分都写相同的代码,只是那样显得有些繁琐。我们可以使用merge()函数将多个selection进行合并,传入的参数是需要合并的部分。例如在上面的代码中我们需要对enterupdate都设置圆的半径的颜色,我们就可以在enter后面合并updateupdate即就是绑定数据后的变量)。进行修改以后就可以得到下面更加简洁的代码。

fruitBowl.js

const colorScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range(['red', 'yellow']);const radiusScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range([50, 20]);export const fruitBowl = (selection, props) => {const {fruits, height} = props;const circles = selection.selectAll('circle').data(fruits);circles.enter().append('circle').attr('cx', (d,i) => i*150+50).attr('cy', height/2).merge(circles)	//both enter section and update section.attr('r', d => radiusScale(d.type)).attr('fill', d => colorScale(d.type));circles.exit().remove();// .attr('fill', 'black');}

index.js

import { fruitBowl } from './fruitBowl.js';const svg = d3.select('svg');
// svg.style('background-color', 'red'); testconst makeFruit = type =>( {type} );	//这种写法好像能够直接得到一个属性名为'type',值为变量type值的对象let fruits = d3.range(5).map(() => makeFruit('apple') );const render = () => {fruitBowl(svg, {fruits, height : +svg.attr('height')})
};render();// console.log(fruits);setTimeout(()=>{fruits.pop();render();
}, 1000);setTimeout(()=>{fruits[2].type = 'lemon';render();
}, 2000);setTimeout(()=>{fruits = fruits.filter((d, i) => i!=1);render();
}, 3000);

Animated Transitions(动画过渡)

为了添加动画效果我们可以使用transition()函数设置属性,这样属性的变化就是渐进的,我们可以同时设置duration(x)来设置变化需要x ms。例如:

const colorScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range(['red', 'yellow']);const radiusScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range([50, 20]);export const fruitBowl = (selection, props) => {const {fruits, height} = props;const circles = selection.selectAll('circle').data(fruits);circles.enter().append('circle').attr('cx', (d,i) => i*150+50).attr('cy', height/2).attr('r', 0).merge(circles)	//both enter section and update section.attr('fill', d => colorScale(d.type)).transition().duration(1000).attr('r', d => radiusScale(d.type));circles.exit().transition().duration(1000).attr('r', 0).remove();// .attr('fill', 'black');
}

Object constancy

在观察上面代码第三秒效果的时候我们会发现,我们想要删除第二个圆点,实际上的效果却是第二个圆点从红色变成了黄色,然后变小了,第三个圆点变成了红色然后变大了,第四个圆点消失了。这显然不是我们想要的结果,我们想要的应该是第二个圆点消失了,然后后面的圆点向前移动。

产生这种现象的原因在于d3中对数据唯一性的处理。默认情况下d3是通过下标唯一标识数据的。因此产生上面现象的原因是我们修改数组以后,d3按照新数组的情况进行了处理:第四个数据已经没有了,所以在exit里面进行删除,前面的属性发生了变化然后直接进行处理。

我们需要做的是帮助d3标识不同的数据,方法就是在绑定数据的时候使用data函数的数组后面再传入一个函数,用于标识数据。

在这个实例中我们给原本的对象数组的每个元素添加新的属性id,用来进行区分,然后传入的函数就是返回每个元素的id属性。这样d3就会将每个元素的id作为区分的标准。

代码如下:
fruitBowl.js

const circles = selection.selectAll('circle').data(fruits,  d => d.id);

index.js

const makeFruit = (type, i) =>( {type,id : i
} );	//这种写法好像能够直接得到一个属性名为'type',值为变量type值的对象let fruits = d3.range(5).map((d,i) => makeFruit('apple', i) );

进行上面的修改以后会发现效果的确是第二个圆点消失了,但是后面的圆点却没有向前移动。为了解决这个问题我们应该将对位置的设置放在merge后的transition函数中,这样每次绑定数据都会对enterupdate中的元素设置位置。

代码如下:
fruitBowl.js

const colorScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range(['red', 'yellow']);const radiusScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range([50, 20]);const xPosition = (d,i) => i*150+50;export const fruitBowl = (selection, props) => {const {fruits, height} = props;const circles = selection.selectAll('circle').data(fruits,  d => d.id);circles.enter().append('circle').attr('cy', height/2).attr('cx', xPosition).attr('r', 0).merge(circles)	//both enter section and update section.attr('fill', d => colorScale(d.type)).transition().duration(1000).attr('cx', xPosition).attr('r', d => radiusScale(d.type));circles.exit().transition().duration(1000).attr('r', 0).remove();// .attr('fill', 'black');}

vizhub 代码:https://vizhub.com/Edward-Elric233/1a1bd422d4b349aba1735868ff453b5f

Nested(嵌套的) elements

只有圆圈可能难以理解表达的是什么意思,我们就需要加上文字text
fruitBowl.js

const colorScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range(['red', 'yellow']);const radiusScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range([50, 20]);const xPosition = (d,i) => i*150+50;export const fruitBowl = (selection, props) => {const {fruits, height} = props;const circles = selection.selectAll('circle').data(fruits,  d => d.id);circles.enter().append('circle').attr('cy', height/2).attr('cx', xPosition).attr('r', 0).merge(circles)	//both enter section and update section.attr('fill', d => colorScale(d.type)).transition().duration(1000).attr('cx', xPosition).attr('r', d => radiusScale(d.type));circles.exit().transition().duration(1000).attr('r', 0).remove();// .attr('fill', 'black');const text = selection.selectAll('text').data(fruits,  d => d.id);text.enter().append('text').attr('y', height/2+100).attr('x', xPosition).merge(text)	//both enter section and update section.text(d => d.type).transition().duration(1000).attr('x', xPosition);text.exit().remove();
}

styles.css

text {font-size: 1.2em;text-anchor : middle;
}

注意text元素设置文字内容的函数是.text()

虽然上面的做法可行,但是当有很多元素组合在一起的时候就有点力不从心。因此我们更常用的做法是将一组元素放在g中,然后再对g的位置属性等进行设置。

大概的做法就是先绑定数据以后给每个元素append一个g,然后再对g进行操作。

const colorScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range(['red', 'yellow']);const radiusScale = d3.scaleOrdinal().domain(['apple', 'lemon']).range([50, 20]);const xPosition = (d,i) => i*150+50;export const fruitBowl = (selection, props) => {const {fruits, height} = props;const groups = selection.selectAll('g').data(fruits,  d => d.id);const groupEnter = groups.enter().append('g').attr('transform', (d,i) => `translate(${i*150+50}, ${height/2})`);;groupEnter.merge(groups)	//both enter section and update section.transition().duration(1000).attr('transform', (d,i) => `translate(${i*150+50}, ${height/2})`);groups.exit().select('circle').transition().duration(1000).style('fill', 'white').attr('r',0)setTimeout(() => groups.exit().select('circle').remove(),1000);// .attr('fill', 'black');groups.exit().select('text').transition().duration(1000).attr('fill', 'white');setTimeout(() => groups.exit().select('text').remove(), 1000);groupEnter.append('circle').attr('r', 0).merge(groups.select('circle'))	//both enter section and update section.attr('fill', d => colorScale(d.type)).transition().duration(1000).attr('r', d => radiusScale(d.type));const text = groups.select('text');groupEnter.append('text').attr('y', 100).merge(text)	//both enter section and update section.text(d => d.type)}

vizhub代码:https://vizhub.com/Edward-Elric233/f33fac2e32134707896521d420d5e255

Singular elements

每次我用对selection操作的时候都会对其中的每个元素进行操作。特殊的,我们如果想要添加一个唯一的元素的话可以绑定一个个数为一的数组,内容可以随意选定,一般情况下选择[null]数组就比较好。然后在这个selection上进行操作。

例如,我们想要给我们的动画添加一个矩形的背景,就可以使用上面的方法添加一个。

const bowl = selection.selectAll('rect').data([null]).enter().append('rect').attr('y', 110).attr('width', 700).attr('height', 300).attr('rx', 300/2) //圆角矩形.attr('fill', '#ebfbfc');

最后的效果图(实际上应该是动图,这里没有制作gif图,可以在网站进行查看)

在这里插入图片描述
在这里插入图片描述
vizhub代码:https://vizhub.com/Edward-Elric233/bc54edb3b722482590f498f3a1047a62

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据可视化【八】根据数据类型选择可视化方式

Marks:Rows PointsLinesAreas Channels:Columns PositionColorShape

数据可视化【九】单向数据流交互

我们使用一下上上篇博客的代码。 例如我们想要当鼠标点击水果的时候会出现黑色的框,再点击一下黑色的框就会消失。 首先,我们应该给组件添加点击事件: fruitBowl.js gruopAll.on(click, d > onClick(d.id));这个on函数第一个参数是事件…

数据库原理及应用【四】数据库管理系统

查询优化 数据库管理系统中非常重要的一部分。 代数优化 按照一定的规则将语句变化成关系代数以后进行优化 操作优化 对代数优化后的查询树使用比较好的方法进行查询。 主要是对连接运算进行优化 嵌套循环归并扫描索引优化哈希连接 恢复机制 备份(完整备份差…

数据库原理及应用【五】安全性和完整性约束

数据库一致性被破坏: 系统故障许多用户的并发访问人为破坏事务本身不正确 保护数据库一致性的方法: 视图/查询修改访问控制 普通用户拥有资源特权的用户DBA 数据库的安全问题 身份验证 口令物理设备 GRANT CONNECT TO John IDENTIFIED BY 123456…

递归式复杂度求解

代换法 猜测复杂度验证是否满足递归式(使用归纳法)找到常数应该满足的条件针对基本情况,常数足够大时总是成立的 需要注意的是,我们猜测的复杂度有可能不满足递归式,这个时候就要通过减去一些低阶项来使得归纳成立。…

斐波那契数列计算

定义 斐波那契数列: F[n]{0,n01,n1F[n−1]F[n−2],elseF[n] \begin{cases} 0,n0 \\ 1,n1\\ F[n-1]F[n-2],else \end{cases} F[n]⎩⎪⎨⎪⎧​0,n01,n1F[n−1]F[n−2],else​ 朴素计算法 根据递归式F[n]F[n−1]F[n−2]F[n]F[n-1]F[n-2]F[n]F[n−1]F[n−2]进行计算…

P、NP、NP完全问题、NP难问题

可以在多项式时间内求解的问题称为易解的,而不能在多项式时间内求解的问题称为难解的。 P类问题:多项式类型,是一类能够用(确定性的)算法在多项式的时间内求解的判定问题。 只有判定问题才属于P 不可判定问题&#…

数据可视化【十】绘制地图

Loading and parsing TOPOJSON 导入Topojson d3文件 地址:https://unpkg.com/topojson3.0.2/dist/topojson.min.js 想要找d3文件的话去unpkg.com好像大部分都能找到的样子 Rendering geographic features 寻找合适的地图数据:谷歌搜索world-atlas npm…

数据可视化【十一】树状图

Constructing a node-link tree visualization 首先将节点之间的连线画出来。 使用json函数读取文件以后,使用hierarchy等函数得到连线的数组,然后绑定这个数组,给每个元素添加一个path,绘画使用的是一个函数linkHorizontal&…

数据可视化【十二】 颜色图例和尺寸图例

有了前面的知识,制作一个图例应该不是很难,关键是我们想要制作一个可以在其他地方进行使用的图例,这样就需要能够动态地设置图例的大小,位置,等等。 这里直接上代码: colorLegend.js export const color…

数据可视化【十三】地区分布图

在前面的博客中已经介绍了如何绘制地图,这一节学习如何绘制地区分布图。如果对绘制地图还不熟悉的话可以了解一下之前我写的博客:数据可视化【十】绘制地图 Intergrating(整合) TopoJSON with tabular data(列表数据) 在前面的博客中没有使用到tsv文件…

3.01【python正则表达式以及re模块】

python正则表达式以及re模块 元字符 正则表达式的语法就由表格中的元字符组成,一般用于搜索、替换、提取文本数据 元字符含义.匹配除换行符以外的任何单个字符*匹配前面的模式0次或1次匹配前面的模式1次或多次?匹配前面的模式0次或1次[]用于定义字符集&#xff…

Linux配置编程环境+云服务器上传文件

Java环境配置 Ubuntu https://www.cnblogs.com/lfri/p/10437266.html Centos https://blog.csdn.net/qq_21077715/article/details/85536399 Tomcat配置 Centos https://blog.csdn.net/qq_21077715/article/details/85541685 https://www.cnblogs.com/newwind/p/9904561…

gbd + cgbd

gbd:传送门 cgbd:传送门 | 传送门

数据可视化【十四】交互式过滤地区分布图

在前面的博客中已经介绍了如何绘制地区分布图,这一节学习如何绘制交互式过滤地区分布图。如果对绘制地区分布图还不熟悉的话可以了解一下之前我写的博客:数据可视化【十三】地区分布图 整体的框架仍然是在之前的基础上进行修改,主要是添加交…

Ubuntu环境搭建

本文记录了一些常用的Ubuntu软件 然后首先修改软件源:软件和更新->Ubuntu软件->下载自:其他站点(修改为阿里云) 在关闭的时候需要更新什么的 然后修改更新方式,将不支持的更新去掉 常用的Windows软件 网易云…

1 两数之和

虽然只是一道很简单的题,但是也给我很多思考。 刚看到这道题的时候没有仔细思考,直接写了个排序和二分查找,想着对每个数字查找另一个数字会不会出现,复杂度是O(nlognnlogn)O(nlognnlogn)O(nlognnlogn),主要训练了一下…

834 树中距离之和

这道题我自己的想法只有对每个点都用一遍Dijkstra然后再求和,显然会超时,所以我都没有尝试。 研究了一下题解,发现题解很巧妙,自己对树的处理还是太稚嫩,之前树链剖分学的都忘光了。 对于固定根节点的,我…

75 颜色分类

题目已经提示可以一遍扫描了但是我还是没有想到,其实双指针的想法我已经有了,但是一想到有问题就觉得无法实现。这也揭示了我思维上的问题:用一种方法解决问题遇到困难第一件事情不是想着如何攻克而是想着换一种方法。对自己的思维也不自信。…

141 环形链表

要求使用空间复杂度为O(1)的方法,可是我并没有想到。我想到的只有用一个哈希表记录一下所有访问过的节点。 题解给出的空间复杂度为O(1)的方法是使用两个指针,然后让一个一次跑一步,一个一次跑两步,如果跑的快的能追上跑的慢的就…