Skip to content

Commit c2e94d8

Browse files
committed
Add area chart
1 parent 2211b95 commit c2e94d8

File tree

2 files changed

+81
-6
lines changed

2 files changed

+81
-6
lines changed

src/App.vue

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
In the last decade, most block buster hits have happened around or during the summer and winter holidays.
77
<strong>Brush</strong> the histograms to filter the movies by metascore and/or box office figures, and <strong>hover</strong> the movies to see more details.
88
</p>
9+
<!-- AREA CHART -->
10+
<div class='area-container'>
11+
<div class='area-title'>
12+
<strong>$ over/under median box office</strong>
13+
</div>
14+
<AreaChart v-bind='{movies, filtered}' />
15+
</div>
916
<!-- HISTOGRAM -->
1017
<Histogram v-for='({id, label, format}) in histograms'
1118
v-bind='{movies, filtered, id, label, format}' />
@@ -15,15 +22,15 @@ In the last decade, most block buster hits have happened around or during the su
1522
<script>
1623
import _ from 'lodash'
1724
import * as d3 from 'd3'
25+
import AreaChart from './components/AreaChart'
1826
import Histogram from './components/Histogram'
1927
2028
const startYear = 2008;
21-
const numYears = 10;
2229
2330
export default {
2431
name: 'app',
2532
components: {
26-
Histogram,
33+
Histogram, AreaChart,
2734
},
2835
data() {
2936
return {
@@ -61,4 +68,17 @@ export default {
6168
line-height: 2;
6269
margin: 20px auto;
6370
}
71+
72+
.area-container {
73+
position: relative;
74+
}
75+
76+
.area-title {
77+
position: absolute;
78+
left: 65px;
79+
}
80+
81+
.histograms {
82+
display: inline-block;
83+
}
6484
</style>

src/components/AreaChart.vue

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
11
<template>
22
<svg class='areachart' :width='width' :height='height'>
3+
<g class='arcs'>
4+
<path v-for='d in arcs' :d='d.path' :fill='d.fill' stroke='#fff' />
5+
</g>
6+
<g ref='xAxis' :transform='`translate(0, ${y0})`' />
7+
<g ref='yAxis' :transform='`translate(${margin.left}, 0)`' />
38
</svg>
49
</template>
510

611
<script>
712
import _ from 'lodash'
813
import * as d3 from 'd3'
914
10-
const width = 1000;
11-
const height = 300;
12-
const margin = {top: 0, right: 0, bottom: 20, left: 60};
15+
const width = 1000
16+
const height = 300
17+
const margin = {top: 0, right: 0, bottom: 20, left: 60}
1318
1419
export default {
1520
name: 'areachart',
1621
props: ['movies', 'filtered'],
1722
data() {
1823
return {
1924
width, height, margin,
25+
arcs: [],
26+
medianBox: 0,
27+
y0: 0,
2028
}
2129
},
2230
created() {
23-
this.xScale = d3.scaleLinear().range([margin.left, width - margin.right])
31+
this.xScale = d3.scaleTime().range([margin.left, width - margin.right])
2432
this.yScale = d3.scaleLinear().range([height - margin.bottom, margin.top])
2533
this.colorScale = d3.scaleSequential(d3.interpolateViridis)
2634
35+
this.areaGen = d3.area().curve(d3.curveCatmullRom)
36+
2737
this.xAxis = d3.axisBottom().scale(this.xScale).tickFormat(this.format)
38+
this.yAxis = d3.axisLeft().scale(this.yScale)
39+
.tickSizeOuter(0)
40+
.tickFormat(d => `${d3.format('$')(parseInt(d / 1000000))}M`)
2841
},
2942
watch: {
3043
movies: function() {
3144
this.calculateScales()
45+
d3.select(this.$refs.xAxis).call(this.xAxis)
46+
d3.select(this.$refs.yAxis).call(this.yAxis)
47+
.select('.domain').remove()
3248
},
3349
filtered: function() {
3450
this.calculateData()
@@ -37,9 +53,48 @@ export default {
3753
methods: {
3854
calculateScales: function() {
3955
if (!this.movies.length) return
56+
57+
// calculate median box office
58+
this.medianBox = d3.median(this.movies, d => d.boxOffice)
59+
60+
// colors
61+
const colorDomain = d3.extent(this.movies, d => d.score);
62+
this.colorScale.domain(colorDomain).nice()
63+
64+
// x scale with dates
65+
const [minDate, maxDate] = d3.extent(this.movies, d => d.date)
66+
this.xScale.domain([
67+
d3.timeMonth.offset(minDate, -2),
68+
d3.timeMonth.offset(maxDate, 2),
69+
])
70+
71+
// y scale with boxOffice - medianBox
72+
const yExtent = d3.extent(this.movies, d => d.boxOffice - this.medianBox)
73+
this.yScale.domain(yExtent)
74+
this.y0 = this.yScale(0)
75+
76+
// update area generater
77+
this.areaGen.y0(this.y0)
4078
},
4179
calculateData: function() {
4280
if (!this.filtered.length) return
81+
82+
// calculate arcs from the filtered movies
83+
this.arcs = _.chain(this.filtered)
84+
// put biggest arcs in the background
85+
.sortBy(d => -Math.abs(d.boxOffice - this.medianBox))
86+
.map(d => {
87+
// for each arc, just need d & fill
88+
return {
89+
path: this.areaGen([
90+
[this.xScale(d3.timeMonth.offset(d.date, -2)), this.y0],
91+
[this.xScale(d.date), this.yScale(d.boxOffice - this.medianBox)],
92+
[this.xScale(d3.timeMonth.offset(d.date, 2)), this.y0],
93+
]),
94+
fill: this.colorScale(d.score),
95+
data: d,
96+
}
97+
}).value()
4398
}
4499
}
45100
}

0 commit comments

Comments
 (0)