Published
Edited
Aug 15, 2019
1 fork
Insert cell
md`# Line Chart Transitions`
Insert cell
d3 = require("d3@5")
Insert cell
async function drawLineChart() {

// 1. Access data
let dataset = await d3.json("https://gist.githubusercontent.com/chekos/7ee802ef53ba4bbd10a3b8161116d638/raw/e0d655473a57fae5ba54e648429bfd01ca698e12/tijuana_weather_data.json")

// 2. Create chart dimensions

const yAccessor = d => d.temperatureMax
const dateParser = d3.timeParse("%Y-%m-%d")
const xAccessor = d => dateParser(d.date)
dataset = dataset.sort((a,b) => xAccessor(a) - xAccessor(b)).slice(0, 100)

let dimensions = {
width: width,
height: 400,
margin: {
top: 15,
right: 15,
bottom: 40,
left: 60,
},
}
dimensions.boundedWidth = dimensions.width - dimensions.margin.left - dimensions.margin.right
dimensions.boundedHeight = dimensions.height - dimensions.margin.top - dimensions.margin.bottom

// 3. Draw canvas

const wrapper = d3.select(DOM.svg(width, 400))

const bounds = wrapper.append("g")
.style("transform", `translate(${dimensions.margin.left}px, ${dimensions.margin.top}px)`)
bounds.append("defs")
.append("clipPath")
.attr("id", "bounds-clip-path")
.append("rect")
.attr("width", dimensions.boundedWidth)
.attr("height", dimensions.boundedHeight)

// init static elements
bounds.append("rect")
.attr("class", "freezing")
const clip = bounds.append("g")
.attr("clip-path", "url(#bounds-clip-path)")
clip.append("path")
.attr("class","line")
bounds.append("g")
.attr("class", "x-axis")
.style("transform", `translateY(${dimensions.boundedHeight}px)`)
bounds.append("g")
.attr("class", "y-axis")

const drawLine = (dataset) => {

// 4. Create scales

const yScale = d3.scaleLinear()
.domain(d3.extent(dataset, yAccessor))
.range([dimensions.boundedHeight, 0])

const freezingTemperaturePlacement = yScale(32)
const freezingTemperatures = bounds.select(".freezing")
.attr("x", 0)
.attr("width", dimensions.boundedWidth)
.attr("y", freezingTemperaturePlacement)
.attr("height", dimensions.boundedHeight - freezingTemperaturePlacement)

const xScale = d3.scaleTime()
.domain(d3.extent(dataset, xAccessor))
.range([0, dimensions.boundedWidth])

// 5. Draw data

const lineGenerator = d3.line()
.x(d => xScale(xAccessor(d)))
.y(d => yScale(yAccessor(d)))

const lastTwoPoints = dataset.slice(-2)
const pixelsBetweenLastPoints = xScale(xAccessor(lastTwoPoints[1])) - xScale(xAccessor(lastTwoPoints[0]))
const line = bounds.select(".line")
.attr("d", lineGenerator(dataset))
.style("transform", `translateX(${pixelsBetweenLastPoints}px)`)
.transition().duration(1000)
.style("transform", "none")

// 6. Draw peripherals

const yAxisGenerator = d3.axisLeft()
.scale(yScale)

const yAxis = bounds.select(".y-axis")
.transition().duration(600)
.call(yAxisGenerator)

const xAxisGenerator = d3.axisBottom()
.scale(xScale)

const xAxis = bounds.select(".x-axis")
.transition().duration(600)
.call(xAxisGenerator)
}
drawLine(dataset)

// update the line every 1.5 seconds
setInterval(addNewDay, 1500)
function addNewDay() {
dataset = [
...dataset.slice(1),
generateNewDataPoint(dataset)
]
drawLine(dataset)
}

function generateNewDataPoint(dataset) {
const lastDataPoint = dataset[dataset.length - 1]
const nextDay = d3.timeDay.offset(xAccessor(lastDataPoint), 1)

return {
date: d3.timeFormat("%Y-%m-%d")(nextDay),
temperatureMax: yAccessor(lastDataPoint) + (Math.random() * 6 - 3),
}
}
return wrapper.node()
}
Insert cell
drawLineChart()
Insert cell
html`<style>
.freezing {
fill: #e0f3f3;
}

.line {
fill: none;
stroke: #af9358;
stroke-width: 2;
}
.x-axis-label {
fill: black;
font-size: 1.4em;
text-transform: capitalize;
}
</style`
Insert cell
mx_ES = ({
"decimal": ",",
"thousands": ".",
"grouping": [3],
"currency": ["€", ""],
"dateTime": "%a %b %e %X %Y",
"date": "%d/%m/%Y",
"time": "%H:%M:%S",
"periods": ["AM", "PM"],
"days": ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"],
"shortDays": ["Dom", "Lun", "Mar", "Mi", "Jue", "Vie", "Sab"],
"months": ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"],
"shortMonths": ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"]
})
Insert cell
d3.timeFormatDefaultLocale(mx_ES);
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more