Initial Commit

This commit is contained in:
2021-03-04 20:06:19 -03:00
commit 5d11cb56b8
47 changed files with 2639 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules/

33
app.js Normal file
View File

@@ -0,0 +1,33 @@
const express = require('express')
const handlebars = require('express-handlebars')
const path = require('path')
const routes = require('./routes/index')
const port = 3000
const app = express()
app.engine('.hbs', handlebars({defaultLayout: 'layout', extname: '.hbs'}))
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', '.hbs')
app.use(express.static(path.join(__dirname, 'public')))
app.use('/', routes)
app.use((request, response, next) => {
const err = new Error('Not Found')
err.status = 404
next(err)
})
app.use((err, req, res, next) => {
res.locals.message = err.message
res.locals.error = req.app.get('env') === 'development' ? err : {}
res.status(err.status || 500)
res.render('error')
})
app.listen(port, () => console.log(`Listening on port : ${port}!`))
module.exports = app

1153
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

15
package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "d3",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"express-handlebars": "^5.2.1"
}
}

20
public/css/area-chart.css Normal file
View File

@@ -0,0 +1,20 @@
body {
padding: 0;
margin: 0;
background-color: rgb(21, 22, 26);
}
.axis line {
stroke: #C1A1DF;
}
.axis path{
stroke: #C1A1DF;
}
.axis text{
color: #C1A1DF;
font-family: Work Sans;
}

View File

@@ -0,0 +1,19 @@
body {
padding: 0;
margin: 0;
}
.axis line {
stroke: #C1A1DF;
}
.axis path{
stroke: #C1A1DF;
}
.axis text{
color: #C1A1DF;
font-family: Work Sans;
}

View File

@@ -0,0 +1,15 @@
body {
padding: 0;
margin: 0;
background-color: hsl(201, 81%, 8%);
}
.text {
fill: white;
font-family: Work Sans;
font-weight: bold;
font-size: 1rem;
text-anchor: middle;
text-shadow: 2px 2px hsl(201, 81%, 8%);
}

0
public/css/index.css Normal file
View File

View File

@@ -0,0 +1,15 @@
body {
padding: 0;
margin: 0;
background-color: hsl(200, 77%, 5%);
}
.text {
fill: white;
font-family: Work Sans;
font-weight: bold;
font-size: 1rem;
text-anchor: middle;
text-shadow: 2px 2px hsl(200, 77%, 5%);
}

View File

@@ -0,0 +1,6 @@
body {
padding: 0;
margin: 0;
background-color: hsl(84, 77%, 82%);
}

26
public/css/planets.css Normal file
View File

@@ -0,0 +1,26 @@
body {
padding: 0;
margin: 0;
background-color: rgb(21, 22, 26);
}
.axis line {
stroke: #C1A1DF;
}
.axis path{
stroke: #C1A1DF;
}
.axis text{
color: #C1A1DF;
font-family: Work Sans;
}
.text{
fill: #C1A1DF;
font-family: Work Sans;
font-weight: bold;
font-size: 1rem;
}

26
public/css/scores.css Normal file
View File

@@ -0,0 +1,26 @@
body {
padding: 0;
margin: 0;
background-color: rgb(21, 22, 26);
}
.axis line {
stroke: #C1A1DF;
}
.axis path{
stroke: #C1A1DF;
}
.axis text{
color: #C1A1DF;
font-family: Work Sans;
}
.text{
fill: #C1A1DF;
font-family: Work Sans;
font-weight: bold;
font-size: 1rem;
}

26
public/css/time-chart.css Normal file
View File

@@ -0,0 +1,26 @@
body {
padding: 0;
margin: 0;
background-color: rgb(21, 22, 26);
}
.axis line {
stroke: #C1A1DF;
}
.axis path{
stroke: #C1A1DF;
}
.axis text {
color: #C1A1DF;
font-family: Work Sans;
}
.title {
fill: #C1A1DF;
font-family: Work Sans;
}

View File

@@ -0,0 +1,11 @@
body {
padding: 0;
margin: 0;
background-color: hsl(35, 100%, 91%);
}
.circle {
fill: hsl(29, 100%, 50%);
stroke: hsl(15, 78%, 26%);
stroke-width: 10;
}

View File

@@ -0,0 +1,32 @@
body {
display: flex;
flex-direction: column;
background-color: hsl(71, 59%, 89%);
font-size: 16px;
font-family: Work Sans;
}
.axis line {
stroke: hsl(170, 18%, 13%);
fill: hsl(170, 18%, 13%);
}
.axis path {
stroke: hsl(170, 18%, 13%);
}
.axis-grid line {
stroke: hsl(70, 60%, 96%);
}
.axis text {
color: hsl(170, 18%, 13%);
font-size: 0.8rem;
}
.title {
color: hsl(170, 18%, 13%);
font-size: 1.3rem;
text-anchor: 'middle';
}

View File

@@ -0,0 +1,21 @@
body {
display: flex;
flex-direction: column;
background-color: #293241;
color: #e0fbfc;
font-size: 16px;
font-family: Work Sans;
}
.title {
font-size: 1.3rem;
text-anchor: 'middle';
}
.arc {
stroke: #293241;
stroke-width: 2;
}

View File

@@ -0,0 +1,32 @@
body {
display: flex;
flex-direction: column;
background-color: hsl(71, 59%, 89%);
font-size: 16px;
font-family: Work Sans;
}
.axis line {
stroke: hsl(170, 18%, 13%);
fill: hsl(170, 18%, 13%);
}
.axis path {
stroke: hsl(170, 18%, 13%);
}
.axis-grid line {
stroke: hsl(70, 60%, 96%);
}
.axis text {
color: hsl(170, 18%, 13%);
font-size: 0.8rem;
}
.title {
color: hsl(170, 18%, 13%);
font-size: 1.3rem;
text-anchor: 'middle';
}

6
public/data/bills.csv Normal file
View File

@@ -0,0 +1,6 @@
type,cost_percent
Auto,14.5
Food,28
Student Loan,17
Rent,32.5
Insurance,8
1 type cost_percent
2 Auto 14.5
3 Food 28
4 Student Loan 17
5 Rent 32.5
6 Insurance 8

36
public/data/planets.json Normal file
View File

@@ -0,0 +1,36 @@
{
"planetDiameters": [
{
"planet": "Mercury",
"diameter": 4879
},
{
"planet": "Venus",
"diameter": 12104
},
{
"planet": "Earth",
"diameter": 12756
},
{
"planet": "Mars",
"diameter": 6792
},
{
"planet": "Jupiter",
"diameter": 142984
},
{
"planet": "Saturn",
"diameter": 120536
},
{
"planet": "Uranus",
"diameter": 51118
},
{
"planet": "Neptune",
"diameter": 49528
}
]
}

11
public/data/scorecard.csv Normal file
View File

@@ -0,0 +1,11 @@
player,points
Steven,150
Manisha,220
Nicole,85
Jaime,109
Crystal,99
Alexa,186
Bethany,201
Marie,197
Seth,112
Nick,215
1 player points
2 Steven 150
3 Manisha 220
4 Nicole 85
5 Jaime 109
6 Crystal 99
7 Alexa 186
8 Bethany 201
9 Marie 197
10 Seth 112
11 Nick 215

View File

@@ -0,0 +1,13 @@
date,value
2019-00-20,1000
2019-01-04,536
2019-02-19,106
2019-03-11,478
2019-04-13,391
2019-05-01,99
2019-06-20,963
2019-07-26,223
2019-08-14,701
2019-09-03,111
2019-10-22,388
2019-11-07,604
1 date value
2 2019-00-20 1000
3 2019-01-04 536
4 2019-02-19 106
5 2019-03-11 478
6 2019-04-13 391
7 2019-05-01 99
8 2019-06-20 963
9 2019-07-26 223
10 2019-08-14 701
11 2019-09-03 111
12 2019-10-22 388
13 2019-11-07 604

View File

@@ -0,0 +1,40 @@
const margin = { top: 200, right: 200, bottom: 200, left: 200 }
const width = window.innerWidth - margin.right - margin.left
const height = (window.innerHeight / 2) - margin.top - margin.bottom
const div = d3.select('body')
const svg = div.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.style('background-color', 'rgb(255, 218, 241)')
.append('g').attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const data = Array(100).fill().map(() => d3.randomUniform(1)())
const xScale = d3.scaleLinear()
.domain([0, data.length-1])
.range([0, width])
const yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([height, 0])
const line = d3.line()
.x((d, i) => xScale(i))
.y((d, i) => yScale(d))
.curve(d3.curveNatural)
const path = svg.append('path')
.datum(data)
.style('fill', 'none')
.style('stroke', 'darkblue')
.style('stroke-width', 1)
.attr('d', line)
const totalLength = path.node().getTotalLength()
path.attr('stroke-dasharray', `${totalLength} ${totalLength}`)
.attr('stroke-dashoffset', totalLength)
.transition().duration(5000).ease(d3.easeQuad)
.attr('stroke-dashoffset', 0)

70
public/js/area-chart.js Normal file
View File

@@ -0,0 +1,70 @@
const margin = { top: 200, right: 200, bottom: 200, left: 200 }
const width = window.innerWidth - margin.right - margin.left
const height = window.innerHeight - margin.top - margin.bottom
const data = Array(11).fill().map(() => d3.randomUniform(100)())
const div = d3.select('body')
const svg = div.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append('g').attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const xScale = d3.scaleLinear()
.domain([0, data.length-1])
.range([0, width])
const yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([height, 0])
const area = d3.area()
.x((d, i) => xScale(i))
.y0(yScale(0))
.y1((d, i) => yScale(d))
const xAxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale))
const yAxis = svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(yScale))
const path = svg.append('path')
.datum(data)
.attr('class', 'data-line')
.style('stroke', 'white')
.style('stroke-width', 2)
.style('fill', 'white')
.style('fill-opacity', 0.15)
.attr('d', area)
const circles = svg.selectAll('.circle')
.data(data).enter().append('circle')
.attr('class', 'circle')
.attr('cx', (d, i) => xScale(i))
.attr('cy', -300)
.attr('r', 8)
.style('fill', 'white')
.style('stroke', 'rgb(51, 57, 68)')
.style('stroke-width', 3)
circles.transition()
.duration(1000)
.delay((d, i) => i * 80)
.attr('cy', (d, i) => yScale(d))
circles.on('mouseover', function(d) {
d3.select(this)
.transition().duration(500)
.attr('r', 14)
})
circles.on('mouseout', function(d) {
d3.select(this)
.transition().duration(500)
.attr('r', 8)
})

View File

@@ -0,0 +1,63 @@
const margin = { top: 200, right: 200, bottom: 200, left: 200 }
const width = window.innerWidth - margin.right - margin.left
const height = window.innerHeight - margin.top - margin.bottom
const data = Array(11).fill().map(() => d3.randomUniform(100)())
const div = d3.select('body')
const svg = div.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.style('background-color', 'rgb(51, 57, 68)')
.append('g').attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const xScale = d3.scaleLinear()
.domain([0, data.length-1])
.range([0, width])
const yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([height, 0])
const line = d3.line()
.x((d, i) => xScale(i))
.y((d, i) => yScale(d))
.curve(d3.curveCatmullRom)
const xAxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale))
const yAxis = svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(yScale))
const path = svg.append('path')
.datum(data)
.style('fill', 'none')
.style('stroke', 'white')
.style('stroke-width', 2)
.attr('d', line)
const circles = svg.selectAll('.circle')
.data(data).enter().append('circle')
.attr('class', 'circle')
.attr('cx', (d, i) => xScale(i))
.attr('cy', (d, i) => yScale(d))
.attr('r', '5')
.style('fill', 'white')
.style('stroke', 'rgb(51, 57, 68)')
.style('stroke-width', 3)
circles.on('mouseover', function(d) {
d3.select(this)
.transition().duration(500)
.attr('r', '10')
})
circles.on('mouseout', function(d) {
d3.select(this)
.transition().duration(500)
.attr('r', '5')
})

29
public/js/confetti.js Normal file
View File

@@ -0,0 +1,29 @@
const width = window.innerWidth
const height = window.innerHeight
const svg = d3.select('body').style('margin', 0)
.append('svg')
.style('width', width)
.style('height', height)
//.style('background', 'darkblue')
const data = Array(3000).fill().map(_ => {
return {
cx: Math.round(Math.random() * width),
cy: Math.round(Math.random() * height)
}
});
const color = d3.scaleOrdinal(d3.schemePastel1)
const circle = svg.selectAll('circle')
.data(data).enter()
.append('circle')
.attr('cx', d => d.cx)
.attr('cy', d => d.cy)
.attr('r', (d, i) => i % 2 == 0 ? 10 : 5)
.style('stroke', (d, i) => color(i))
.style('stroke-width', 2)
.style('fill', 'none')

52
public/js/flower-chart.js Normal file
View File

@@ -0,0 +1,52 @@
const data = [12, 6, 15, 4, 28, 5, 14, 4, 7, 5]
const width = window.innerWidth
const height = window.innerHeight - 200
const radius = Math.min(width, height) * .50
const colorScale = d3.scaleOrdinal(d3.schemeSet3)
const svg = d3.select('body').append('svg')
.attr('height', height)
.attr('width', width)
.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`)
const pie = d3.pie().value(d => d).sort(null)
const zeroArc = d3.arc().outerRadius(1).innerRadius(0).cornerRadius(1)
const arc = d3.arc().outerRadius(radius).innerRadius(0).cornerRadius(radius)
const hoverArc = d3.arc().outerRadius(radius + 30).innerRadius(0).cornerRadius(radius)
const labelArc = d3.arc().outerRadius(radius * 1.7).innerRadius(0)
const g = svg.selectAll('.arc')
.data(pie(data))
.enter().append('g').attr('class', 'arc')
g.append('path')
.attr('d', zeroArc)
.attr('class', 'arc')
.style('fill', (d, i) => colorScale(i))
.style('fill-opacity', .7)
.style('stroke', 'hsl(201, 81%, 8%)')
.style('stroke-width', 4)
.on('mouseover', function(d, i) {
d3.select(this)
.style('fill-opacity', 1)
.transition().duration(250)
.attr('d', hoverArc)
})
.on('mouseout', function(d, i) {
d3.select(this)
.style('fill-opacity', .7)
.transition().duration(250)
.attr('d', arc)
})
.transition().duration(500).delay((d, i) => i * 100)
.attr('d', arc)
g.append('text')
.attr('transform', d => `translate(${labelArc.centroid(d)})`)
.text(d => `${d.data}%`)
.attr('class', 'text')
.attr('fill-opacity', 0)
.transition().duration(500).delay((d, i) => i * 100)
.attr('fill-opacity', 1)

0
public/js/index.js Normal file
View File

52
public/js/montly-bills.js Normal file
View File

@@ -0,0 +1,52 @@
const data = d3.csv('/data/bills.csv').then(rawData => {
const data = rawData.map(d => {
return {
type: d.type,
cost_percent: +d.cost_percent
}
})
const width = window.innerWidth
const height = window.innerHeight
const radius = Math.min(width, height) / 2
const colorScale = d3.scaleOrdinal(['#7326AB', '#2A59A9', '#E5A1D4', '#00A0B0', '#1C9FE9'])
const svg = d3.select('body').append('svg')
.attr('height', height)
.attr('width', width)
.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`)
const pie = d3.pie().value(d => d.cost_percent).sort(null)
const arc = d3.arc().outerRadius(radius * .75).innerRadius(0)
const hoverArc = d3.arc().outerRadius(radius * .85).innerRadius(0)
const g = svg.selectAll('.arc')
.data(pie(data))
.enter().append('g').attr('class', 'arc')
g.append('path')
.attr('d', arc)
.attr('class', 'arc')
.style('fill', (d, i) => colorScale(i))
.style('fill-opacity', 0.8)
.style('stroke', 'hsl(200, 77%, 5%)')
.style('stroke-width', 4)
.on('mouseover', function(d, i) {
d3.select(this)
.style('fill-opacity', 1)
.transition().duration(500)
.attr('d', hoverArc)
})
.on('mouseout', function(d, i) {
d3.select(this)
.style('fill-opacity', .8)
.transition().duration(500)
.attr('d', arc)
})
g.append('text')
.attr('transform', d => `translate(${arc.centroid(d)})`)
.text(d => `${d.data.type} ${d.data.cost_percent}%`)
.attr('class', 'text')
})

15
public/js/one-box.js Normal file
View File

@@ -0,0 +1,15 @@
const svg = d3.select('body')
.attr('margin', 0)
.append('svg')
.style('width', window.innerWidth)
.style('height', window.innerHeight)
.style('background', 'darkblue')
svg.append('rect')
.attr('x', (window.innerWidth / 2) - 100)
.attr('y', (window.innerHeight / 2) - 150)
.attr('width', 100)
.attr('height', 150)
.style('fill', 'white')
.style('stroke', 'yellow')
.style('stroke-width', 5)

32
public/js/one-circle.js Normal file
View File

@@ -0,0 +1,32 @@
const width = 1000
const height = 700
const svg = d3.select('body')
.attr('margin', 0)
.append('svg')
.attr('width', width)
.attr('height', height)
const circle = svg.append('circle')
.attr('cx', width / 2)
.attr('cy', height / 2)
.attr('r', 100)
.style('fill', 'purple')
circle.on('click', function(d, i) {
d3.select(this)
.transition().duration(1000).ease(d3.easeQuad)
.attr('r', 200)
})
circle.on('mouseover', function(d, i) {
d3.select(this)
.transition().duration(1000).ease(d3.easeQuad)
.style('fill', 'red')
})
circle.on('mouseout', function(d, i) {
d3.select(this)
.transition().duration(1000).ease(d3.easeQuad)
.style('fill', 'purple')
})

25
public/js/one-line.js Normal file
View File

@@ -0,0 +1,25 @@
const width = window.innerWidth
const height = window.innerHeight
const svg = d3.select('body')
.attr('margin', 0)
.append('svg')
svg
.attr('width', width)
.attr('height', height)
.style('background-color', '#8AF2F7')
const coordinates = [
{ x: width / 4, y: height / 2 },
{ x: (width / 4) * 3, y: height / 2 }
]
const line = d3.line().x(d => d.x).y(d => d.y)
const path = svg.append('path')
.datum(coordinates)
.style('fill', 'none')
.style('stroke', '#0e0e0e')
.style('stroke-width', '5')
.attr('d', line)

27
public/js/peace-sign.js Normal file
View File

@@ -0,0 +1,27 @@
const data = [35, 15, 15, 35]
const width = window.innerWidth
const height = window.innerHeight
const radius = Math.min(width, height) / 2
const colorScale = d3.scaleOrdinal(['#7326AB', '#2A59A9', '#E5A1D4', '#00A0B0'])
const svg = d3.select('body').append('svg')
.attr('height', height)
.attr('width', width)
.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`)
const pie = d3.pie().value(d => d).sort(null)
const arc = d3.arc().outerRadius(radius * .75).innerRadius(0)
const g = svg.selectAll('.arc')
.data(pie(data))
.enter().append('g').attr('class', 'arc')
g.append('path')
.attr('d', arc)
.attr('class', 'arc')
.style('fill', (d, i) => colorScale(i))
.style('stroke', 'hsl(84, 77%, 82%)')
.style('stroke-width', 4)

98
public/js/planets.js Normal file
View File

@@ -0,0 +1,98 @@
const data = d3.json('/data/planets.json').then(data => {
const margin = { top: 200, right: 200, bottom: 200, left: 200 }
const width = window.innerWidth - margin.right - margin.left
const height = window.innerHeight - margin.top - margin.bottom
const planets = data.planetDiameters
const div = d3.select('body')
const svg = div.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append('g').attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const xScale = d3.scaleBand()
.domain(planets.map(d => d.planet))
.range([0, width])
.padding(0.1)
const yScale = d3.scaleLinear()
.domain([0, d3.max(planets.map(d => d.diameter))])
.rangeRound([height, 0])
const xAxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale))
.append('text')
.attr('class', 'text')
.attr('x', width / 2)
.attr('y', 30)
.style('text-anchor', 'middle')
.text('Planets')
const yAxis = svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(yScale))
.append('text').attr('class', 'text')
.style('text-anchor', 'end')
.attr('class', 'text')
.attr('y', 6)
.attr('dy', '0.75em')
.attr('transform', 'rotate(-90)')
.text('Diameter (km)')
const bars = svg.selectAll('.bar')
.data(planets)
.enter().append('rect')
.attr('class', 'bar')
.style('fill', 'white')
.style('fill-opacity', .2)
.style('stroke', 'white')
.style('stroke-width', 2)
bars.on('mouseover', function(d, i) {
d3.select(this)
.transition().duration(500)
.style('fill-opacity', 1)
})
bars.on('mouseout', function(d, i) {
d3.select(this)
.transition().duration(500)
.style('fill-opacity', .2)
})
bars.attr('x', d => xScale(d.planet))
.attr('y', height)
.attr('width', xScale.bandwidth())
.transition().duration(250).delay((d, i) => i * 200)
.attr('y', d => yScale(d.diameter))
.attr('height', d => height - yScale(d.diameter))
const formatter = d3.format(',')
const barText = svg.selectAll('.diameters')
.data(planets).enter().append('text')
.attr('class', 'diameters text')
.attr('x', d => xScale(d.planet))
.attr('dx', d => xScale.bandwidth() / 2)
.attr('y', d => yScale(d.diameter))
.attr('dy', "-0.75em")
.style('fill', 'white')
.style('text-anchor', 'middle')
.style('opacity', 0)
.text(d => formatter(d.diameter))
.transition().duration(500).delay((d, i) => i * 200)
.style('opacity', 1)
const title = svg.append('text')
.attr('class', 'text')
.attr('x', width / 2)
.attr('y', -100)
.style('text-anchor', 'middle')
.style('text-decoration', 'underline')
.style('fill', 'white')
.text('Planet diameters')
})

32
public/js/scaled-line.js Normal file
View File

@@ -0,0 +1,32 @@
const margin = { top: 200, right: 200, bottom: 200, left: 200 }
const width = window.innerWidth - margin.right - margin.left
const height = window.innerHeight - margin.top - margin.bottom
const div = d3.select('body')
const svg = div.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append('g').attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const data = Array(25).fill().map(() => d3.randomUniform(1)())
const xScale = d3.scaleLinear()
.domain([0, data.length-1])
.range([0, width])
const yScale = d3.scaleLinear()
.domain(d3.extent(data))
.range([0, height])
const line = d3.line()
.x((d, i) => xScale(i))
.y((d, i) => yScale(d))
.curve(d3.curveNatural)
svg.append('path')
.datum(data)
.style('fill', 'none')
.style('stroke', 'darkblue')
.style('stroke-width', 1)
.attr('d', line)

100
public/js/scores.js Normal file
View File

@@ -0,0 +1,100 @@
const data = d3.csv('/data/scorecard.csv').then(rawData => {
const margin = { top: 200, right: 200, bottom: 200, left: 200 }
const width = window.innerWidth - margin.right - margin.left
const height = window.innerHeight - margin.top - margin.bottom
const data = rawData.map(d => {
return {
player: d.player,
points: +d.points
}
})
const div = d3.select('body')
const svg = div.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append('g').attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const xScale = d3.scaleLinear()
.domain([0, d3.max(data.map(d => d.points))])
.rangeRound([0, width])
const yScale = d3.scaleBand()
.domain(data.map(d => d.player))
.range([height, 0])
.padding(0.1)
const xAxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale))
.append('text')
.attr('class', 'text')
.attr('x', width / 2)
.attr('y', 30)
.attr('dy', '.75em')
.style('text-anchor', 'middle')
.text('Points')
const yAxis = svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(yScale))
.append('text').attr('class', 'text')
.style('text-anchor', 'end')
.attr('class', 'text')
.attr('y', -20)
.text('Players')
const bars = svg.selectAll('.bar')
.data(data)
.enter().append('rect')
.attr('class', 'bar')
.style('fill', 'white')
.style('fill-opacity', .2)
.style('stroke', 'white')
.style('stroke-width', 2)
bars.on('mouseover', function(d, i) {
d3.select(this)
.transition().duration(500)
.style('fill-opacity', .75)
})
bars.on('mouseout', function(d, i) {
d3.select(this)
.transition().duration(500)
.style('fill-opacity', .2)
})
bars
.attr('x', 0)
.attr('y', d => yScale(d.player))
.attr('height', yScale.bandwidth())
.transition().duration(250).delay((d, i) => i * 100)
.attr('width', d => xScale(d.points))
const barText = svg.selectAll('.points')
.data(data).enter().append('text')
.attr('class', 'points text')
.attr('x', d => xScale(d.points))
.attr('dx', -30)
.attr('y', d => yScale(d.player))
.attr('dy', yScale.bandwidth() / 2 + 5)
.text(d => d.points)
.style('fill', 'white')
.style('text-anchor', 'middle')
.style('opacity', 0)
.transition().duration(250).delay((d, i) => i * 100)
.style('opacity', 1)
const title = svg.append('text')
.attr('class', 'text')
.attr('x', width / 2)
.attr('y', -100)
.style('text-anchor', 'middle')
.style('text-decoration', 'underline')
.style('fill', 'white')
.text('Game 1 Final Scores')
})

View File

@@ -0,0 +1,57 @@
const width = 1000
const height = 700
const svg = d3.select('body').style('margin', 0)
.append('svg')
.style('width', width)
.style('height', height)
const data = Array(1000).fill().map(_ => {
return {
cx: Math.round(Math.random() * width),
cy: Math.round(Math.random() * height)
}
});
const color = d3.scaleOrdinal(d3.schemeSet3)
const circles_1 = svg.selectAll('.circle_1')
.data(data).enter()
.append('circle')
.attr('class', '.circle_1')
.attr('cx', d => d.cx)
.attr('cy', -10)
.style('fill', 'none')
.attr('r', 10)
circles_1.transition().duration(1000).delay((d, i) => i).ease(d3.easeElastic)
.attr('cy', d => d.cy)
.style('fill', (d, i) => color(i))
const circles_2 = svg.selectAll('.circle_2')
.data(data).enter()
.append('circle')
.attr('class', '.circle_2')
.attr('cx', d => d.cx)
.attr('cy', -10)
.style('stroke', 'none')
.attr('r', 20)
.style('fill', 'none')
.style('stroke-width', 2)
circles_2.transition().duration(1000).delay((d, i) => i).ease(d3.easeElastic)
.attr('cy', d => d.cy)
.style('stroke', (d, i) => color(i))
circles_1.on('click', function(d, i) {
d3.select(this)
.transition().duration(1000)
.style('fill', 'red')
})
circles_2.on('click', function(d, i) {
d3.select(this)
.transition().duration(1000)
.style('stroke', 'red')
})

86
public/js/time-chart.js Normal file
View File

@@ -0,0 +1,86 @@
const data = d3.csv('/data/timeScaleData.csv').then(data => {
const margin = { top: 200, right: 200, bottom: 200, left: 200 }
const width = window.innerWidth - margin.right - margin.left
const height = window.innerHeight - margin.top - margin.bottom
const parseDate = d3.timeParse('%Y-%m-%d')
const formatedData = data.map(d => { return {
date: parseDate(d.date),
value: +d.value
}});
const div = d3.select('body')
const svg = div.append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.left + margin.right)
.append('g').attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const xScale = d3.scaleTime()
.domain(d3.extent(formatedData.map(d => d.date)))
.range([0, width])
const yScale = d3.scaleLinear()
.domain([0, d3.max(formatedData.map(d => d.value))])
.range([height, 0])
const xAxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale))
const yAxis = svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(yScale))
.append('text')
.attr('x', 0)
.attr('y', -25)
.text('Orders')
const title = svg.append('text')
.attr('class', 'title')
.attr('x', width / 2)
.attr('y', -25)
.text('Orders in 2019')
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value))
.curve(d3.curveCardinal)
svg.append('path')
.datum(formatedData)
.attr('class', 'line')
.attr('d', line)
.style('stroke', 'white')
.style('fill', 'none')
.style('stroke-width', 2)
const circles = svg.selectAll('.circle')
.data(formatedData).enter().append('circle')
.attr('class', 'circle')
.attr('cx', d => xScale(d.date))
.attr('cy', d => -400)
.attr('r', '5')
.style('fill', 'white')
.style('stroke', 'rgb(51, 57, 68)')
.style('stroke-width', 3)
circles.transition()
.duration(1000)
.delay((d, i) => xScale(d.date))
.attr('cy', (d, i) => yScale(d.value))
circles.on('mouseover', function(d) {
d3.select(this)
.transition().duration(500)
.attr('r', '10')
})
circles.on('mouseout', function(d) {
d3.select(this)
.transition().duration(500)
.attr('r', '5')
})
})

View File

@@ -0,0 +1,33 @@
const width = window.innerWidth
const height = window.innerHeight - 200
const radius = Math.min(width, height) * .50
const svg = d3.select('body').append('svg')
.attr('height', height)
.attr('width', width)
.append('g')
const update = (data) => {
const circle = svg.selectAll('.circle').data(data)
circle.enter().append('circle')
.merge(circle)
.attr('class', 'circle')
.attr('cx', (d, i) => (i + 1) * 350)
.attr('cy', height / 2)
.transition().duration(600)
.attr('r', d => d)
circle.exit().remove()
}
d3.interval(() => {
update([
d3.randomUniform(50, 300)(),
d3.randomUniform(50, 300)(),
d3.randomUniform(50, 300)(),
d3.randomUniform(50, 300)()
])
}, 600)

View File

@@ -0,0 +1,73 @@
const margin = { top: 100, right: 100, bottom: 100, left: 100 }
const width = window.innerWidth - margin.left - margin.right
const height = window.innerHeight - margin.bottom - margin.top
const svg = d3.select('body').append('svg')
.attr('height', height + margin.left + margin.right)
.attr('width', width + margin.bottom + margin.top)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const stores = ['Walmart', 'Sams Club', 'Target', 'Costco', 'BJs']
const getData = () => stores.map((store) => {
return {
store: stores[Math.floor(Math.random() * stores.length)],
profit: Math.round(d3.randomUniform(100000000)())
}
})
const colorScale = d3.scaleOrdinal().domain(stores).range(d3.schemePastel2)
const xScale = d3.scaleBand().rangeRound([0, width]).padding(.1)
const yScale = d3.scaleLinear().range([height, 0])
const xAxis = d3.axisBottom(xScale)
const yAxis = d3.axisLeft(yScale)
svg.append('g').attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.append('text').attr('class', 'title')
.attr('x', width / 2)
.attr('y', 50)
.text('Stores')
svg.append('g').attr('class', 'y axis')
.append('text').attr('class', 'title')
.attr('x', 0)
.attr('y', -20)
.text('Profit (USD)')
const update = (data) => {
xScale.domain(data.map(d => d.store))
yScale.domain([0, d3.max(data.map(d => d.profit))])
svg.select('.x.axis')
.transition().duration(1000)
.call(xAxis)
svg.select('.y.axis')
.transition().duration(1000)
.call(yAxis)
const bar = svg.selectAll('.bar').data(data)
bar.enter().append('rect')
.merge(bar).attr('class', 'bar')
.attr('x', d => xScale(d.store))
.attr('y', d => yScale(0))
.attr('width', xScale.bandwidth())
.attr('height', 0)
.transition().duration(500)
.attr('y', d => yScale(d.profit))
.attr('height', d => height - yScale(d.profit))
.attr('fill', d => colorScale(d.store))
bar.exit().remove()
}
update(getData())
d3.interval(() => update(getData()), 5000)

View File

@@ -0,0 +1,49 @@
const width = 800
const height = 600
const radius = Math.min(width, height) / 2
const colorScale = d3.scaleOrdinal(['#9b5de5', '#f15bb5', '#fee440', '#00bbf9', '#00f5d4'])
const svg = d3.select('body').append('svg')
.attr('height', height)
.attr('width', width)
.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`)
const pie = d3.pie().value(d => d).sort(null)
const arc = d3.arc().outerRadius(radius * .75).innerRadius(0)
const data = [
[2.380952380952381+ 16.666666666666664, 5.714285714285714, 8.095238095238095, 36.666666666666664, 30.476190476190478],
[6.451612903225806+ 14.336917562724013, 11.469534050179211, 32.97491039426524, 21.863799283154123, 12.903225806451612],
[5.405405405405405+ 13.127413127413126, 25.482625482625483, 15.444015444015443, 31.27413127413127, 9.266409266409266],
[14.24802110817942+ 19.788918205804748, 16.62269129287599, 17.41424802110818, 12.66490765171504, 19.261213720316622],
[12.558139534883722+ 10.930232558139535, 10.0, 22.790697674418606, 22.55813953488372, 21.16279069767442],
[18.663594470046082+ 22.350230414746544, 23.04147465437788, 9.67741935483871, 17.972350230414747, 8.294930875576037],
[21.38364779874214+ 21.38364779874214, 5.031446540880504, 30.81761006289308, 13.836477987421384, 7.547169811320755],
[34.78260869565217+ 9.565217391304348, 38.69565217391304, 0.43478260869565216, 6.086956521739131, 10.434782608695652]
]
function arcTween(a) {
const i = d3.interpolate(this._current, a)
this._current = i(1)
return t => arc(i(t))
}
const update = data => {
const path = svg.selectAll('path').data(pie(data))
path.transition().duration(700).attrTween('d', arcTween)
path.enter().append('path')
.attr('class', 'arc')
.attr('d', arc)
.each(function(d) { this._current = d })
.style('fill', (d, i) => colorScale(i))
}
update(data[0])
let i = 1
d3.interval(() => {
update(data[i++])
i %= data.length
}, 1000)

View File

@@ -0,0 +1,105 @@
const margin = { top: 100, right: 100, bottom: 100, left: 100 }
const width = window.innerWidth - margin.left - margin.right
const height = window.innerHeight - margin.bottom - margin.top
const svg = d3.select('body').append('svg')
.attr('height', height + margin.left + margin.right)
.attr('width', width + margin.bottom + margin.top)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.bottom})`)
const stores = ['Walmart', 'Sams Club', 'Target', 'Costco', 'BJs']
const years = ['2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019', '2020', '2021']
const getData = () => {
return new Array(5000).fill().map(_ => {
return {
store: stores[Math.floor(Math.random() * stores.length)],
profit: Math.round(d3.randomUniform(100000000)()),
year: years[Math.floor(Math.random() * years.length)],
}
})
}
const filterDataByYear = data => year => data.filter(d => d.year === year)
const formatData = data => {
const obj = data.reduce((acc, d) => {
if(!acc[d.store]) acc[d.store] = { profit: 0, year: d.year, store: d.store }
acc[d.store].profit += d.profit
return acc
}, {})
return Object.values(obj)
}
const colorScale = d3.scaleOrdinal().domain(stores).range(d3.schemeCategory10)
const xScale = d3.scaleBand().rangeRound([0, width]).padding(.1)
const yScale = d3.scaleLinear().range([height, 0])
const xAxis = d3.axisBottom(xScale)
const yAxis = d3.axisLeft(yScale)
svg.append('g').attr('class', 'x axis')
.attr('transform', `translate(0, ${height})`)
.append('text').attr('class', 'title')
.attr('x', width / 2)
.attr('y', 50)
.text('Stores')
svg.append('g').attr('class', 'y axis')
.append('text').attr('class', 'title')
.attr('x', 0)
.attr('y', -20)
.text('Profit (USD)')
const update = (data) => {
xScale.domain(data.map(d => d.store))
yScale.domain([0, d3.max(data.map(d => d.profit))])
svg.select('.x.axis')
.transition().duration(1000)
.call(xAxis)
svg.select('.y.axis')
.transition().duration(1000)
.call(yAxis)
const bar = svg.selectAll('.bar').data(data)
bar.enter().append('rect')
.merge(bar).attr('class', 'bar')
.attr('x', d => xScale(d.store))
.attr('y', d => yScale(0))
.attr('width', xScale.bandwidth())
.attr('height', 0)
.transition().duration(500)
.attr('y', d => yScale(d.profit))
.attr('height', d => height - yScale(d.profit))
.attr('fill', d => colorScale(d.store))
bar.exit().remove()
}
const select = d3.select('body')
.append('select').attr('class', 'select')
const options = select.selectAll('option')
.data(years).enter()
.append('option').attr('class', 'option')
.text(d => d)
const dataset = getData()
const originalData = formatData(filterDataByYear(dataset)('2010'))
update(originalData)
select.on('change', function(d, i) {
const s = d3.select(this).node()
const year = s.options[s.selectedIndex].textContent
const updatedData = formatData(filterDataByYear(dataset)(year))
update(updatedData)
})

16
routes/index.js Normal file
View File

@@ -0,0 +1,16 @@
const express = require('express')
const pages = require('./pages')
const router = express.Router()
pages.map(page => {
router.get(page.location, (request, response, next) => {
const p = page.page ? page.page : 'viz'
response.render(p, {script : `/js/${page.file}.js`,
style : `/css/${page.file}.css`})
})
})
module.exports = router

24
routes/pages.js Normal file
View File

@@ -0,0 +1,24 @@
const pages = [
{location: '/', file: 'index', page: 'index'},
{location: '/one-circle', file: 'one-circle'},
{location: '/one-line', file: 'one-line'},
{location: '/one-box', file: 'one-box'},
{location: '/confetti', file: 'confetti'},
{location: '/stacked-circles', file: 'stacked-circles'},
{location: '/scaled-line', file: 'scaled-line'},
{location: '/animated-scaled-line', file: 'animated-scaled-line'},
{location: '/basic-line-chart', file: 'basic-line-chart'},
{location: '/area-chart', file: 'area-chart'},
{location: '/time-chart', file: 'time-chart'},
{location: '/planets', file: 'planets'},
{location: '/scores', file: 'scores'},
{location: '/peace-sign', file: 'peace-sign'},
{location: '/montly-bills', file: 'montly-bills'},
{location: '/flower-chart', file: 'flower-chart'},
{location: '/updated-circle', file: 'updated-circle'},
{location: '/updating-bar-chart', file: 'updating-bar-chart'},
{location: '/user-updating-bar-chart', file: 'user-updating-bar-chart'},
{location: '/updating-pie-chart', file: 'updating-pie-chart'},
]
module.exports = pages

3
views/error.hbs Normal file
View File

@@ -0,0 +1,3 @@
<h1>{{message}}</h1>
<h2>{{error.status}}</h2>
<pre>{{error.stack}}</pre>

25
views/index.hbs Normal file
View File

@@ -0,0 +1,25 @@
<h1 class="underline">D3.js Data Visualizations</h1>
<ul>
<li><a href="one-circle">One Circle</a></li>
<li><a href="one-line">One Line</a></li>
<li><a href="one-box">One Box</a></li>
<li><a href="confetti">Confetti</a></li>
<li><a href="stacked-circles">Stacked Circles</a></li>
<li><a href="scaled-line">Scaled Line</a></li>
<li><a href="animated-scaled-line">Animated Scaled Line</a></li>
<li><a href="basic-line-chart">Basic Line Chart</a></li>
<li><a href="area-chart">Area Chart</a></li>
<li><a href="time-chart">Time Chart</a></li>
<li><a href="planets">Planets</a></li>
<li><a href="scores">Scores</a></li>
<li><a href="peace-sign">Peace Sign</a></li>
<li><a href="montly-bills">Montly Bills</a></li>
<li><a href="flower-chart">Flower Chart</a></li>
<li><a href="updated-circle">Updated Circle</a></li>
<li><a href="updating-bar-chart">Updating Bar Chart</a></li>
<li><a href="user-updating-bar-chart">User Updating Bar Chart</a></li>
<li><a href="updating-pie-chart">Updating Pie Chart</a></li>
</ul>
<script type="module" src="{{ script }}"></script>

10
views/layouts/layout.hbs Normal file
View File

@@ -0,0 +1,10 @@
<html>
<head>
<title>D3.js Course</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js" integrity="sha512-0XfwGD1nxplHpehcSVI7lY+m/5L37PNHDt+DOc7aLFckwPXjnjeA1oeNbru7YeI4VLs9i+ADnnHEhP69C9CqTA==" crossorigin="anonymous"></script>
<link rel="stylesheet" href="{{ style }}" >
</head>
<body>
{{{ body }}}
</body>
</html>

6
views/viz.hbs Normal file
View File

@@ -0,0 +1,6 @@
<a class="home-btn" href="/">Home</a>
<div id="app">
</div>
<script type="module" src="{{ script }}"></script>