-
Notifications
You must be signed in to change notification settings - Fork 0
/
TimeSeriesChart.svelte
104 lines (87 loc) · 2.41 KB
/
TimeSeriesChart.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<script>
import { blur } from "svelte/transition"
import { extent } from "d3-array"
import { scaleLinear, scaleSequential } from "d3-scale"
import { interpolateYlGnBu, interpolateYlOrRd, select, axisLeft, axisBottom, format, formatLocale } from "d3"
// should be an array of objects with:
// year
// value
export let data = []
export let valueSuffix = "°C"
export let colourScheme = "cool" // or warm
$: console.log(colourScheme)
$: colourRamp = (colourScheme == "cool") ?
interpolateYlGnBu :
interpolateYlOrRd
// dimensions bound to size of container
export let height = 500
export let width = 300
// add padding to chart
$: padX = [60, width - 10]
$: padY = [height - 30, 10]
$: xDomain = extent(data.map(d => d.year))
$: yDomain = extent(data.map(d => d.value))
// scales (flip the colours if they're cool)
$: xScale = scaleLinear()
.domain(xDomain)
.range(padX)
$: yScale = scaleLinear()
.domain(yDomain)
.range(padY)
$: colourScale = scaleSequential()
.domain(colourScheme == "cool" ? yDomain.reverse() : yDomain)
.interpolator(colourRamp)
// temperature formatter (for x-axis)
const tempFormat = formatLocale({
currency: ["", valueSuffix]
});
// axes
let xAxisGroup
let yAxisGroup
$: select(xAxisGroup)
.transition()
.duration(500)
.call(axisBottom(xScale).tickFormat(format(".0f")))
$: select(yAxisGroup)
.transition()
.duration(500)
.call(axisLeft(yScale).tickFormat(tempFormat.format("$.1f")))
</script>
<style>
svg circle {
transition:
cx 0.5s ease-in-out,
cy 0.5s ease-in-out,
fill 0.5s ease-in-out;
}
#x-axis, #y-axis {
font-family: system-ui, -apple-system;
font-size: 14px;
}
</style>
<main>
<svg width={width} height={height}>
<g>
{#each data as { year, value } (year) }
<!-- points go here-->
<circle
cx="{xScale(year)}px"
cy="{yScale(value)}px"
r="5"
fill="{colourScale(value)}"
in:blur={{ duration: 500 }}
out:blur={{ duration: 500 }}
>
</circle>
{/each}
</g>
<!-- trend line goes here -->
<!-- axes goes here (is rendered imperatively above)-->
<g bind:this={xAxisGroup} id="x-axis"
style:transform="translateY({padY[0]}px)"
/>
<g bind:this={yAxisGroup} id="y-axis"
style:transform="translateX({padX[0]}px)"
/>
</svg>
</main>