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)
複製代碼