D3.js是一个基于HTML/SVG/CSS的数据可视化库,是领域内很是强大的存在了。javascript
从此会在工做使用,也所以开始了自学之旅。学习过程当中,我主要经过Curran Kelleher老师的系列教程进行学习,这个笔记用于学习、整理和分享,陆续学习、更新和记录中....html
学习目的:java
完成效果图: 在线demo node
D3是实现数据可视化,仍然离不开传统DOM的选择和操做,也所以,D3提供了相似Jquery的DOM操做指令:git
d3.select
:选择第一个指定元素d3.selectAll
: 选择全部的元素const svg = d3.select('svg') //选择svg
const p = svg.selectAll('p') //选择svg下全部的p标签
复制代码
固然,能够使用#id
以及 .class
对id和类进行选择github
d3.select
和d3.selectAll
返回的都是选择集,添加、删除以及修改都须要用到选择集,查看状态有三个函数能够使用:编程
selection.empty()
选择集为空,返回true,不然返回falseselection.node()
返回第一个非空元素,若是选择集为空,返回nullselection.size()
返回选择集中的元素个数使用select
或selectAll
选择后,能够经过attr
获取和设定属性,能够使用append
方法添加元素app
const svg = select('svg');
svg.append('circle')
.attr('r','30')
复制代码
html文件svg
<html lang="en">
<head>
<title>Smile face with d3</title>
</head>
<body>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="index.js"></script>
</body>
</html>
复制代码
主要内容经过index.js实现:函数
circle
构建轮廓const svg = d3.select('svg');
const height = +svg.attr('height');
const width = +svg.attr('width');
svg.append('circle')
.attr('r',height / 2)
.attr('cx', width / 2)
.attr('cy', height / 2)
.attr('fill', 'yellow')
.attr('stroke','black')
const leftEye = svg.append('circle')
.attr('r', 30)
.attr('cx', width / 2 - 100)
.attr('cy', height / 2 - 80)
.attr('fill', 'black')
const rightEye = svg.append('circle')
.attr('r', 30)
.attr('cx', width / 2 + 100)
.attr('cy', height / 2 - 80)
.attr('fill', 'black')
复制代码
要点:
attr
获取的属性是string
类型的,经过parseFloat
或者+
转换为number
类型circle
的属性cx
cy
等,使用变量做为其大小设置的值,而不用一个数字,方便往后的维护1)能够发现,对三个圆圆心的操做cx
和cy
出现了屡次,而做用也仅仅是为了将圆放在中心。所以,能够经过SVG中的<g>
来分组来实现一次性操做。
const g = svg.append('g')
.attr('transform',`translate(${ width / 2}, ${ height / 2})`)
//这样,在画圆的时候,就能够去掉对其圆心的操做,由于默认的位置就在中心了
const circle = g.append('circle')
.attr('r',height / 2)
.attr('fill', 'yellow')
.attr('stroke','black')
复制代码
2)一样的,对于眼睛的操做,也有点繁琐,能够经过变量和分组,提升代码的可维护性能。
const eyeSpacing = 100;
const eyeYoffset = -80
const eyeRadius = 30;
const eyesG = g.append('g')
.attr('transform', `translate(0, ${eyeYoffset})`);
const leftEye = eyesG.append('circle')
.attr('r', eyeRadius)
.attr('cx', - eyeSpacing)
const rightEye = eyesG.append('circle')
.attr('r', eyeRadius)
.attr('cx', + eyeSpacing)
复制代码
嘴巴其实是一个弧线,使用SVG中的path
进行绘制。熟悉path
的,固然能够直接给参数进行绘制,可是d3对圆弧有更好的支持,能够使用d3.arc
函数,方便的绘制圆弧。
根据官方的API手册,函数的使用方法以下:
var arc = d3.arc();
arc({
innerRadius: 0,
outerRadius: 100,
startAngle: 0,
endAngle: Math.PI / 2
}); // "M0,-100A100,100,0,0,1,100,0L0,0Z"
复制代码
其中,innerRadius是内圆半径,outerRadius是外圆半径,startAngle和endAngle分别是开始和结束的弧度(完整的圆是0~2PI)
根据以上内容,代码以下:
const mouth = g.append('path')
.attr('d',d3.arc()({
innerRadius: 150,
outerRadius: 170,
startAngle: Math.PI /2,
endAngle: Math.PI * 3 / 2
}))
复制代码
眉毛用简单的长方形来代替,也就是svg中的<rect>
,使用前文的编程风格,将眉毛归为一个group,并将位置设定。
const eyebrowWidth = 50;
const eyebrowHeight = 10;
const eyebrowYoffset = -150;
const BrowG = g.append('g')
.attr('transform',`translate(${-eyebrowWidth / 2},${eyebrowYoffset})`);
const leftEyebrow = BrowG.append('rect')
.attr('width',eyebrowWidth)
.attr('height',eyebrowHeight)
.attr('x',-eyeSpacing)
const rightEyebrow = BrowG.append('rect')
.attr('width', eyebrowWidth)
.attr('height', eyebrowHeight)
.attr('x', eyeSpacing)
复制代码
使用transition
函数设置眉毛的动画,让笑脸动起来
const BrowG = g.append('g')
.attr('transform',`translate(${-eyebrowWidth / 2},${eyebrowYoffset})`);
BrowG.transition().duration(2000)
.attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset - 50})`)
.transition().duration(2000)
.attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset})`).duration(2000)
复制代码
注意点:
transition
函数要对号入座,即若是加在了<g>
上,对应的增长的属性动画应该在transform
上; 若是动画在<rect>
上,动画应该在y
属性上。append
函数以后链接,好比BrowG.append('g').attr(...).transition()
是不行的。由于过分动画没法绑定在append
的元素上,须要分别操做。const svg = d3.select('svg');
const height = +svg.attr('height');
const width = +svg.attr('width');
const g = svg.append('g')
.attr('transform',`translate(${ width / 2}, ${ height / 2})`)
const circle = g.append('circle')
.attr('r',height / 2)
.attr('fill', 'yellow')
.attr('stroke','black')
const eyeSpacing = 100;
const eyeYoffset = -80
const eyeRadius = 30;
const eyebrowWidth = 50;
const eyebrowHeight = 10;
const eyebrowYoffset = -150;
const eyesG = g.append('g')
.attr('transform', `translate(0, ${eyeYoffset})`);
const leftEye = eyesG.append('circle')
.attr('r', eyeRadius)
.attr('cx', - eyeSpacing)
const rightEye = eyesG.append('circle')
.attr('r', eyeRadius)
.attr('cx', + eyeSpacing)
const mouth = g.append('path')
.attr('d',d3.arc()({
innerRadius: 150,
outerRadius: 170,
startAngle: Math.PI /2,
endAngle: Math.PI * 3 / 2
}))
const BrowG = g.append('g')
.attr('transform',`translate(${-eyebrowWidth / 2},${eyebrowYoffset})`);
BrowG.transition().duration(2000)
.attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset - 50})`)
.transition().duration(2000)
.attr('transform', `translate(${-eyebrowWidth / 2},${eyebrowYoffset})`).duration(2000)
const leftEyebrow = BrowG.append('rect')
.attr('width',eyebrowWidth)
.attr('height',eyebrowHeight)
.attr('x',-eyeSpacing)
const rightEyebrow = BrowG.append('rect')
.attr('width', eyebrowWidth)
.attr('height', eyebrowHeight)
.attr('x', eyeSpacing)
复制代码