写在前面:
- 本文是单纯的个人学习总结和课堂笔记整理,大部分知识点来自“开课吧”线上直播课程,以及官方资料。
- 由于目前仍处于前端学习、入门阶段,因此对于知识的掌握和理解难免会有偏差,因此该文章仅仅是个人记录,请查阅者不要以此作为资料依据。
D3.js(D3或Data-Driven Documents)是一个使用动态图形进行资料可视化的JavaScript程序库。与W3C标准兼容,并且利用广泛实现的SVG、JavaScript和CSS标准,改良自早期的Protovis程序库。与其他的程序库相比,D3对视图结果有很大的可控性。D3是2011年面世的,同年的8月发布了2.0.0版。到2018年4月,D3已被更新到了5.5.0版。
D3.js已被数十万个网站使用,最常被运用在在线新闻网站呈现交互式图形、呈现数据资料的图表和呈现含有地理信息的资料。另外SVG的输出功能也使得D3.js能用于印刷出版物的绘制上。(来自wikipedia介绍)
总之,D3类似canvas可操作SVG,不同的是D3可以直接操作DOM。所以我们在这篇文章里面先一起看一下D3对DOM和样式的核心操作。
入门
可以通过npm安装完整依赖环境,也可以图省事,直接在HTML文件里面引入CDN。
<script src="https://lib.baomitu.com/d3/6.2.0/d3.js"></script>
成功引入之后我们就可以使用D3来进行绘图了。
- D3.select() 方法选择绘图容器
- .append() 方法添加对象
- .attr() 方法添加属性
- .on() 方法绑定事件及回调
<body>
<!--svg容器对象-div-->
<div id="main"></div>
</body>
<script>
/*需求:
* 建立一个小球
* 鼠标划入时,变色
* 鼠标划出时,恢复原本颜色
* */
/*d3.select() 方法选择容器*/
const main=d3.select('#main')
/*向容器中添加svg*/
const svg=main.append('svg')
.attr('width','100%')
.attr('height','100%')
/*向svg中添加一个圆circle
* attr() 属性设置
* cx|cy 圆心位
* r 半径
* fill 填充颜色
* cursor 光标形状:pointer 小手
* */
const ball=svg.append('circle')
.attr('cx',300)
.attr('cy',300)
.attr('r',200)
.attr('fill','#00acec')
.attr('cursor','pointer')
/*用on方法监听鼠标划入图形事件mouseover
* attr 方法改变小球填充色fill
* */
ball.on('mouseover',()=>{
ball.attr('fill','orange')
})
/*用on方法监听鼠标划出图形事件mouseout
* attr 方法改变小球填充色fill
* */
ball.on('mouseout',()=>{
ball.attr('fill','#00acec')
})
</script>
选择
因为D3是一种相对底层的JS库,很多语法看上去和原生JavaScript语法很想,比如说D3选择元素的方式就和原生JavaScript很想,有两种方式:
- 通过CSS选择器:class或者id
- 通过标签名:如p、div、li
我们来对比一下原生JavaScript选择器和D3选择器:
<script>
// 用原生js获取title元素
const title=document.querySelector('#title')
// select选择title元素
d3.select(title).classed('sky',true)
d3.select(title).classed('sky',false)
</script>
D3多选,其实叫全选我觉得更合适:
<script>
// 选择所有p标签
d3.selectAll('p').classed('sky',true)
// 选择所有奇数项的p标签
d3.selectAll('p:nth-child(2)').classed('sky',true)
// 选择所有奇数项的p标签
d3.selectAll('p:nth-child(even)').classed('sky',true)
</script>
D3根据条件选择:
filter((ele,ind)=>{})可基于节点元素或节点的索引进行过滤filter(':nth-child(n)')基于元素的索引位置过滤
<script>
const n=[1,2,3]
d3.selectAll('p')
.filter((ele,ind)=>n.includes(ind))
.classed('sky',true)
</script>
样式
D3给我们提供了三大类修改样式的方式,分别是:
style()方法,直接修改内联样式,不建议使用,因为有可能导致全局样式发生变化classed()方法,增加或删除class,从而实现样式变化。attr()方法,增加属性(会覆盖原来的属性),可以修改class、id、内联样式、自定义属性等,最万能。
下面我们来一个一个的来看下这三种方式。
<script>
/*选择标题元素*/
const title=d3.select('#title');
/*style() 方法设置标题颜色*/
// title.style('color','#00acec')
/*attr() 方法通过对style 属性的设置,改变标题颜色*/
// title.attr('style','color:#000')
/*classed(class,true|false) 方法增删标题的class*/
// title.classed('sky',true)
/*attr() 方法设置class属性*/
// title.attr('class','sky')
/*用style的回调函数,让所有p元素偶数行变蓝*/
/*d3.selectAll('p')
.style('color',(ele,ind)=>ind%2?'#00acec':'#00f')*/
/*用data数据绑定,动态设置p元素字体大小*/
const data=[12,16,18,24,56]
d3.selectAll('p')
.data(data)
.style('font-size',(ele,ind)=>{
console.log(ele);
return `${ele}px`
})
</script>
属性
D3提供了两种修改DOM属性的方法:
attr()设置普通的内置属性和自定义属性property()设置特殊的内置属性(如value、checked等)
通过下面的实例我们可以了解到如何使用D3修改DOM元素,首先有一段文本,label标签里main有三个checkbox类型的答案。首先通过selectAll选择所有label标签,并添加class样式。
然后基于预设的answer,选择相应的答案:
- 通过selectAll方法,选择所有input元素;
- 通过filter方法,筛选各个元素索引在answer数组那的元素;
- 通过property方法,设置上述筛选出来元素的checked属性为true。
<div class="question">
<p class="title">《沁园春·长沙》里的诗句有哪些?</p>
<label>
<input type="checkbox"> 独立寒秋,湘江北去,橘子洲头。
</label>
<label>
<input type="checkbox"> 看万山红遍,层林尽染;
</label>
<label>
<input type="checkbox"> 好好学习,天天向上。
</label>
</div>
<script>
d3.selectAll('label').attr('class','opt')
const answer=[0,1]
d3.selectAll('input')
.filter((ele,ind)=>answer.includes(ind))
.property('checked',true)
</script>
内容
设置内容相对比较简单,可以直接在选取的对象那设置文本内容或者html内容,注意:会覆盖原本内容。
text()方法,设置文本内容html()方法,设置html内容
<script>
// 选择标题元素
const title=d3.select('#title');
// 选择内容元素
const cont=d3.select('#cont');
// 设置标题内容
const titleText='沁园春·长沙';
// 设置标签文本
const p=`
<p>独立寒秋,湘江北去,橘子洲头。</p>
<p>看万山红遍,层林尽染;漫江碧透,百舸争流。</p>
<p>鹰击长空,鱼翔浅底,万类霜天竞自由。</p>
<p>怅寥廓,问苍茫大地,谁主沉浮?</p>
<p>携来百侣曾游。忆往昔峥嵘岁月稠。</p>
<p>恰同学少年,风华正茂;书生意气,挥斥方遒。</p>
<p>指点江山,激扬文字,粪土当年万户侯。</p>
<p>曾记否,到中流击水,浪遏飞舟?</p>
`;
// 填充文本内容
title.text(titleText)
// 填充html内容
cont.html(p)
</script>
修改元素
添加DOM元素
接下来看到的各种语法依旧感觉似曾相识,就像是在用原生语法一样,这个D3好用的一点:
append()方法,在当权选择的对象的最后追加制定名称的子元素insert()方法,用来前置元素,在某个元素前追加前置元素data()和join()方法,以完全覆盖的形式,添加元素data()和enter()和append()方法,以插值的形式添加元素
下面我们还是在实例中看看D3是如何以上述四种方式添加DOM元素的。
<h1 id="title">沁园春·长沙</h1>
<article id="cont"></article>
<script>
const arr=[
'作者:毛主席',
'独立寒秋,湘江北去,橘子洲头。',
'看万山红遍,层林尽染;漫江碧透,百舸争流。',
'鹰击长空,鱼翔浅底,万类霜天竞自由。',
'怅寥廓,问苍茫大地,谁主沉浮?',
'携来百侣曾游。忆往昔峥嵘岁月稠。',
'恰同学少年,风华正茂;书生意气,挥斥方遒。',
'指点江山,激扬文字,粪土当年万户侯。',
'曾记否,到中流击水,浪遏飞舟?'
]
// 选择内容元素集合
const cont=d3.select('#cont');
/* 一、append(),向cont中添加p元素
* text()设置元素文本内容
* */
cont.append('p')
.text('独立寒秋,湘江北去,橘子洲头。———— 未使用arr数组填充的数据')
.attr('id','p1')
/* 二、insert(要添加的元素,在哪个元素之前添加)
* 在第一句的前面添加作者
* */
cont.insert('p','#p1')
.text('作者:毛主席')
/* 三、selectAll() 选择所有的p元素
* data() 绑定数据
* join() 基于数据添加p元素
* text(d=>d) 设置DOM元素内容为数据内容
* */
/*cont.selectAll('p')
.data(arr)
.join('p')
.text(d=>d)*/
/*四、selectAll() 选择所有的p元素
* data() 绑定数据
* enter() 将数据和已有DOM元素做插值运算
* append() 基于插值结果添加p元素
* text(d=>d) 设置DOM元素内容为数据内容
* */
cont.selectAll('p')
.data(arr)
.enter()
.append('p')
.text(d=>d)
</script>
删除元素
删除就相对来说比较简单,使用remove() 方法就可以删除所有选择上的元素集合:
<script>
d3.selectAll("p").remove()
</script>