Published
Edited
Mar 18, 2022
2 forks
1 star
Insert cell
Insert cell
Insert cell
Insert cell
viewof select1 = {
const WIDTH = 300
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
container.innerHTML = `<circle cx=${WIDTH / 2} cy=${HEIGHT / 2} r="30" />`
// prendre l'élément "svg" défini plus haut
const svg = d3.select(container)
// et l'élément "circle" appartenant à l'élément "svg"
const circle = svg.select('circle')
// y ajouter un attribut "fill" avec la valeur "red"
circle.attr('fill', 'red')
return container
}
Insert cell
Insert cell
viewof select2 = {
const WIDTH = 300
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
container.innerHTML = `<circle cx=${WIDTH / 2} cy=${HEIGHT / 2} r="30" />`
// prendre l'élément "circle"
const circle = container.getElementsByTagName('circle')[0]
// y ajouter un attribut "fill" avec la valeur "red"
circle.setAttribute('fill', 'red')
return container
}
Insert cell
Insert cell
viewof select3 = {
const WIDTH = 300
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
// prendre l'élément "svg"
const svg = d3.select(container)
// et y ajouter un élément "rect"
const rect = svg.append('rect')
.attr('x', 20)
.attr('y', 20)
.attr('width', WIDTH - 40)
.attr('height', HEIGHT - 40)
return container
}
Insert cell
Insert cell
viewof select4 = {
const WIDTH = 300
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
// créer un élément "rect"
// pour les éléments HTML de base nous pouvons utiliser .createElement('p') par exemple
// pour les éléments SVG, il nous faut déclarer le "name space"
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
rect.setAttribute('x', 20)
rect.setAttribute('y', 20)
rect.setAttribute('width', WIDTH - 40)
rect.setAttribute('height', HEIGHT - 40)
// ajouter le rectangle à l'élément parent
container.appendChild(rect)
return container
}
Insert cell
Insert cell
viewof select5 = {
const WIDTH = 500
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
container.innerHTML = `
<circle cx="100" cy=${HEIGHT / 2} r="30" />
<circle cx="200" cy=${HEIGHT / 2} r="30" />
<circle cx="300" cy=${HEIGHT / 2} r="30" />
`
const svg = d3.select(container)
const circles = svg.selectAll('circle')
.attr('fill', 'yellow')
return container
}
Insert cell
Insert cell
viewof select6 = {
const WIDTH = 500
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
container.innerHTML = `
<circle cx="100" cy=${HEIGHT / 2} r="30" />
<circle cx="200" cy=${HEIGHT / 2} r="30" />
<circle cx="300" cy=${HEIGHT / 2} r="30" />
`
// nous utilisons Array.from parce qu'une liste .getElementsByTagName n'est pas itérable avec .forEach
const circles = Array.from(container.getElementsByTagName('circle'))
// une fonction pour ajouter l'attribut "fill"
const addYellowFill = circle => circle.setAttribute('fill', 'yellow')
// appliquer la fonction aux cercles
circles.forEach(addYellowFill)
return container
}
Insert cell
Insert cell
viewof select7 = {
const WIDTH = 300
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
container.innerHTML = `<circle cx=${WIDTH / 2} cy=${HEIGHT / 2} r="10" />`
let r = 10
const svg = d3.select(container)
const circle = svg.select('circle')
.on('click', () => {
r++
circle.attr('r', r)
})
return container
}
Insert cell
Insert cell
viewof select8 = {
const WIDTH = 300
const HEIGHT = 100
const container = DOM.svg(WIDTH, HEIGHT)
container.innerHTML = `<circle cx=${WIDTH / 2} cy=${HEIGHT / 2} r="10" />`
let r = 10
const circle = container.getElementsByTagName('circle')[0]
circle.addEventListener('click', () => {
r++
circle.setAttribute('r', r)
})
return container
}
Insert cell
Insert cell
viewof data1 = {
const container = DOM.element('ul')
// les données
const DATA = [4, 6, 2, 8, 1]
// l'élément <ul>
const ul = d3.select(container)
/*
.selectAll() selectionne des éléments qui n'existent pas encore
nous voulons créer un élément <li> pour chaque donnée dans DATA
.data() défini les données que nous souhaitons joindre
quand une donnée est ajoutée --> .enter()
nous ajoutons un élément <li> à <ul> --> .append('li')
.text() ajoute chaque donnée à l'intérieur de <li> --> <li>4</li>
*/
ul.selectAll('li')
.data(DATA)
.enter()
.append('li')
.text(d => d)
return container
}
Insert cell
Insert cell
viewof data2 = {
const container = DOM.element('ul')
const DATA = [4, 6, 2, 8, 1]
const ul = d3.select(container)
ul.selectAll('li')
.data(DATA)
.enter()
.append('li')
.text((d, i) => `Donnée: ${d}, indexe: ${i}`)
return container
}
Insert cell
Insert cell
viewof data3 = {
const container = DOM.element('ul')
const DATA = [4, 6, 2, 8, 1]
const ul = d3.select(container)
ul.selectAll('li')
.data(DATA)
.enter()
.append('li')
// si d est plus grand que 4, la couleur est rouge
.style('color', d => d > 4 ? 'red' : 'black')
// si i est plus petit que 3, le texte est en gras
.style('font-weight', (d, i) => i < 3 ? 'bold' : 'normal')
.text((d, i) => `Donnée: ${d}, indexe: ${i}`)
return container
}
Insert cell
Insert cell
viewof bar1 = {
const WIDTH = width
const HEIGHT = width / 3
const container = DOM.svg(WIDTH, HEIGHT)
const DATA = [4, 6, 2, 8, 1]
const MARGIN = 5 // espace entre les bâtons
const BAR_WIDTH = WIDTH / DATA.length // largeur des bâtons
const svg = d3.select(container)
svg.selectAll('rect')
.data(DATA)
.enter()
.append('rect')
// la position horizontale
.attr('x', (d, i) => i * BAR_WIDTH)
// la largeur
.attr('width', BAR_WIDTH - MARGIN)
// la position verticale
.attr('y', d => HEIGHT - d)
// la hauteur
.attr('height', d => d)

return container
}
Insert cell
Insert cell
viewof bar2 = {
const WIDTH = width
const HEIGHT = width / 3
const container = DOM.svg(WIDTH, HEIGHT)
const DATA = [4, 6, 2, 8, 1]
const MARGIN = 5
const BAR_WIDTH = WIDTH / DATA.length
const svg = d3.select(container)
// nous allons utiliser une échelle linéaire
const heightScale = d3.scaleLinear()
// les données en entrée
.domain([0, d3.max(DATA)]) // d3.max retourne le maxium d'une liste
// les données en sortie
.range([0, HEIGHT])
// comme 8 est la valeur maximale, heightScale(8) retourne la hauteur HEIGHT
// heightScale(4) retourne la moitié de HEIGHT ...
svg.selectAll('rect')
.data(DATA)
.enter()
.append('rect')
.attr('x', (d, i) => i * BAR_WIDTH)
.attr('width', BAR_WIDTH - MARGIN)
// utiliser heightScale pour y et height
.attr('y', d => HEIGHT - heightScale(d))
.attr('height', heightScale)

return container
}
Insert cell
Insert cell
viewof bar3 = {
const WIDTH = width
const HEIGHT = width / 3
const container = DOM.svg(WIDTH, HEIGHT)
const DATA = [4, 6, 2, 8, 1]
const MARGIN = 5
const BAR_WIDTH = WIDTH / DATA.length
const svg = d3.select(container)
const heightScale = d3.scaleLinear()
.domain([0, d3.max(DATA)])
.range([0, HEIGHT])
// l'échelle de couleur
const fillScale = d3.scaleLinear()
.domain([0, d3.max(DATA)])
.range(['white', 'indianred'])
svg.selectAll('rect')
.data(DATA)
.enter()
.append('rect')
.attr('x', (d, i) => i * BAR_WIDTH)
.attr('width', BAR_WIDTH - MARGIN)
.attr('y', d => HEIGHT - heightScale(d))
.attr('height', heightScale)
// appliquée à l'attribut fill
.attr('fill', fillScale)
return container
}
Insert cell
Insert cell
viewof bar4 = {
const WIDTH = width
const HEIGHT = width / 3
const container = DOM.svg(WIDTH, HEIGHT)
const MARGIN = 5
const svg = d3.select(container)
const DATA = [
{ nom: 'Lausanne', population: 138905 },
{ nom: 'Yverdon-les-Bains', population: 30143 },
{ nom: 'Montreux', population: 26574 },
{ nom: 'Renens', population: 21036 },
{ nom: 'Nyon', population: 20533 },
{ nom: 'Vevey', population: 19827 },
]
const BAR_WIDTH = WIDTH / DATA.length
// maintenant "d" représente un objet
// par exemple: { nom: 'Lausanne', population: 138905 }
const heightScale = d3.scaleLinear()
.domain([0, d3.max(DATA, d => d.population)]) // le maximum de population
.range([0, HEIGHT])
svg.selectAll('rect')
.data(DATA)
.enter()
.append('rect')
.attr('x', (d, i) => i * BAR_WIDTH)
.attr('width', BAR_WIDTH - MARGIN)
// nous devons passer d.population à heightScale
.attr('y', d => HEIGHT - heightScale(d.population))
.attr('height', d => heightScale(d.population))

return container
}
Insert cell
Insert cell
viewof bar5 = {
const WIDTH = width
const HEIGHT = width / 3
const container = DOM.svg(WIDTH, HEIGHT)
const MARGIN = 5
// faisons de la place pour les noms
const MARGIN_BOTTOM = HEIGHT / 10
const GRAPH_HEIGHT = HEIGHT - MARGIN_BOTTOM
const svg = d3.select(container)
const DATA = [
{ nom: 'Lausanne', population: 138905 },
{ nom: 'Yverdon-les-Bains', population: 30143 },
{ nom: 'Montreux', population: 26574 },
{ nom: 'Renens', population: 21036 },
{ nom: 'Nyon', population: 20533 },
{ nom: 'Vevey', population: 19827 },
]
const BAR_WIDTH = WIDTH / DATA.length
const heightScale = d3.scaleLinear()
.domain([0, d3.max(DATA, d => d.population)])
.range([0, GRAPH_HEIGHT]) // la nouvelle hauteur
// un groupe pour pouvoir déplacer les bâtons
const bars = svg.append('g')
.attr('transform', `translate(0,-${MARGIN_BOTTOM})`)
bars.selectAll('rect')
.data(DATA)
.enter()
.append('rect')
.attr('x', (d, i) => i * BAR_WIDTH)
.attr('width', BAR_WIDTH - MARGIN)
.attr('y', d => HEIGHT - heightScale(d.population))
.attr('height', d => heightScale(d.population))
// les éléments <text>
svg.selectAll('text')
.data(DATA)
.enter()
.append('text')
.attr('x', (d, i) => i * BAR_WIDTH + BAR_WIDTH / 2)
.attr('y', HEIGHT - MARGIN_BOTTOM + 20)
.attr('text-anchor', 'middle')
.text(d => d.nom)
return container
}
Insert cell
Insert cell
viewof bar6 = {
const WIDTH = width
const HEIGHT = width / 3
const container = DOM.svg(WIDTH, HEIGHT)
const MARGIN = 5
const MARGIN_BOTTOM = HEIGHT / 10
const GRAPH_HEIGHT = HEIGHT - MARGIN_BOTTOM
// faisons de la place à gauche des bâtons
const MARGIN_LEFT = WIDTH / 20
const GRAPH_WIDTH = WIDTH - MARGIN_LEFT
const svg = d3.select(container)
const DATA = [
{ nom: 'Lausanne', population: 138905 },
{ nom: 'Yverdon-les-Bains', population: 30143 },
{ nom: 'Montreux', population: 26574 },
{ nom: 'Renens', population: 21036 },
{ nom: 'Nyon', population: 20533 },
{ nom: 'Vevey', population: 19827 },
]
const BAR_WIDTH = GRAPH_WIDTH / DATA.length // la nouvelle largeur du graphique
const heightScale = d3.scaleLinear()
.domain([0, d3.max(DATA, d => d.population)])
.range([0, GRAPH_HEIGHT])
const bars = svg.append('g')
// déplacer les bâtons sur la droite
.attr('transform', `translate(${MARGIN_LEFT},-${MARGIN_BOTTOM})`)
bars.selectAll('rect')
.data(DATA)
.enter()
.append('rect')
.attr('x', (d, i) => i * BAR_WIDTH)
.attr('width', BAR_WIDTH - MARGIN)
.attr('y', d => HEIGHT - heightScale(d.population))
.attr('height', d => heightScale(d.population))
// un groupe pour déplacer les noms sur la droite
const cityNames = svg.append('g')
.attr('transform', `translate(${MARGIN_LEFT}, 0)`)
cityNames.selectAll('text')
.data(DATA)
.enter()
.append('text')
.attr('x', (d, i) => i * BAR_WIDTH + BAR_WIDTH / 2)
.attr('y', HEIGHT - MARGIN_BOTTOM + 20)
.attr('text-anchor', 'middle')
.text(d => d.nom)
// créer un axe en fonction de l'échelle
const axisY = d3.axisLeft().scale(heightScale)

// créer un nouveau groupe et y attacher l'axe
svg.append('g')
.attr('transform', `translate(${MARGIN_LEFT - 3})`)
.call(axisY)

return container
}
Insert cell
Insert cell
viewof bar7 = {
const WIDTH = width
const HEIGHT = width / 3
const container = DOM.svg(WIDTH, HEIGHT)
const MARGIN = 5
const MARGIN_BOTTOM = HEIGHT / 10
const GRAPH_HEIGHT = HEIGHT - MARGIN_BOTTOM
const MARGIN_LEFT = WIDTH / 20
const GRAPH_WIDTH = WIDTH - MARGIN_LEFT
const svg = d3.select(container)
const DATA = [
{ nom: 'Lausanne', population: 138905 },
{ nom: 'Yverdon-les-Bains', population: 30143 },
{ nom: 'Montreux', population: 26574 },
{ nom: 'Renens', population: 21036 },
{ nom: 'Nyon', population: 20533 },
{ nom: 'Vevey', population: 19827 },
]
const BAR_WIDTH = GRAPH_WIDTH / DATA.length
// passons d'une échelle de hauteur à une échelle de y
const yScale = d3.scaleLinear()
.domain([0, d3.max(DATA, d => d.population)])
.range([GRAPH_HEIGHT, 0]) // a été inversé
const bars = svg.append('g')
// la valeur y est calculée depuis le haut
// nous n'avons plus besoin de la marge en bas
.attr('transform', `translate(${MARGIN_LEFT}, 0)`)
bars.selectAll('rect')
.data(DATA)
.enter()
.append('rect')
.attr('x', (d, i) => i * BAR_WIDTH)
.attr('width', BAR_WIDTH - MARGIN)
.attr('y', d => yScale(d.population)) // utiliser yScale pour y
.attr('height', d => GRAPH_HEIGHT - yScale(d.population)) // modifier la hauteur
.attr('fill', 'steelblue') // un peu de couleur
const cityNames = svg.append('g')
.attr('transform', `translate(${MARGIN_LEFT}, 0)`)
cityNames.selectAll('text')
.data(DATA)
.enter()
.append('text')
.attr('x', (d, i) => i * BAR_WIDTH + BAR_WIDTH / 2)
.attr('y', HEIGHT - MARGIN_BOTTOM + 20)
.attr('text-anchor', 'middle')
.attr('font-family', 'sans-serif') // pour avoir la même typographie que sur l'axe y
.text(d => d.nom)
// utiliser la nouvelle échelle pour l'axe
const axisY = d3.axisLeft().scale(yScale)
// formatter les valeurs
.tickFormat(d => `${d / 1000}k`)
// le nombre de valeurs présentées
.ticks(5)

svg.append('g')
.attr('transform', `translate(${MARGIN_LEFT - 3})`)
.call(axisY)
return container
}
Insert cell
Insert cell
sales = [
{ name: 'Pommes', value: 23 },
{ name: 'Poires', value: 12 },
{ name: 'Bananes', value: 18 },
]
Insert cell
Insert cell
getPieData = d3.pie().value(d => d.value)
Insert cell
Insert cell
pieData = getPieData(sales)
Insert cell
Insert cell
viewof pie1 = {
const WIDTH = width / 3
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
// une fonction pour transformer les angles en attribut d
const arcCreator = d3.arc()
.innerRadius(0)
.outerRadius(HEIGHT / 2)
svg.selectAll('path')
.data(pieData) // créé plus haut
.enter()
.append('path')
.attr('d', arcCreator)
return container
}
Insert cell
Insert cell
viewof pie2 = {
const WIDTH = width / 3
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
const arcCreator = d3.arc()
.innerRadius(0)
.outerRadius(HEIGHT / 2)
// une fonction pour la couleur
const color = ({ data }) => {
switch (data.name) {
case 'Bananes': return 'gold'
case 'Poires': return 'limegreen'
default: return 'indianred'
}
}
// un groupe pour centrer le camembert
const pie = svg.append('g')
.attr('transform', `translate(${HEIGHT / 2}, ${HEIGHT / 2})`)
pie.selectAll('path')
.data(pieData)
.enter()
.append('path')
.attr('d', arcCreator)
// un peu de couleur
.attr('fill', color)
return container
}
Insert cell
Insert cell
viewof pie3 = {
const WIDTH = width / 3
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
const arcCreator = d3.arc()
.innerRadius(0)
.outerRadius(HEIGHT / 2 - 10) // pour que tout le camembert soit visible
const color = ({ data }) => {
switch (data.name) {
case 'Bananes': return 'gold'
case 'Poires': return 'limegreen'
default: return 'indianred'
}
}
const pie = svg.append('g')
.attr('transform', `translate(${HEIGHT / 2}, ${HEIGHT / 2})`)
pie.selectAll('path')
.data(pieData)
.enter()
.append('path')
.attr('d', arcCreator)
.attr('fill', color)
// un texte pour chaque tranche
pie.selectAll('text')
.data(pieData)
.enter()
.append('text')
// .centroid permet de trouver le centre de la tranche
.attr('transform', d => `translate(${arcCreator.centroid(d)})`)
.attr('text-anchor', 'middle')
.text(d => d.data.name)
// la légende
const legend = svg.append('g')
.attr('transform', `translate(${HEIGHT-10})`)
const RECT_WIDTH = 20
// les carrés de couleur3.line()
legend.selectAll('rect')
.data(pieData)
.enter()
.append('rect')
.attr('y', (d, i) => i * RECT_WIDTH)
.attr('width', RECT_WIDTH)
.attr('height', RECT_WIDTH)
.attr('fill', color)
// les noms de fruits
legend.selectAll('text')
.data(pieData)
.enter()
.append('text')
.attr('x', RECT_WIDTH * 1.5)
.attr('y', (d, i) => i * RECT_WIDTH + RECT_WIDTH * 0.75)
.attr('width', RECT_WIDTH)
.attr('height', RECT_WIDTH)
.text(d => d.data.name)
return container
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof line1 = {
const WIDTH = width
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
// spécifier le format de date
const formatDate = d3.timeParse('%Y-%m-%d')
const DATA = bitcoinPrices.map(d => ({ ...d, date: formatDate(d.date) }))
// échelle de temps pour l'axe X
const scaleX = d3.scaleTime()
.range([0, WIDTH])
.domain(d3.extent(DATA, d => d.date))
// échelle linéaire pour le prix de cloture
const scaleY = d3.scaleLinear()
.range([HEIGHT, 0])
.domain(d3.extent(DATA, d => d.close))

// la fonction d3.line() pour créer l'attribut "d"
const linePathCreator = d3.line()
// quelle échelle, quelle donnée pour l'axe X
.x(d => scaleX(d.date))
// quelle échelle, quelle donnée pour l'axe Y
.y(d => scaleY(d.close))

// ajouter une courbe au SVG
const line = svg.append('path')
// utiliser linePathCreator pour créer l'attribut "d"
.attr('d', linePathCreator(DATA))
.attr('fill', 'none')
.attr('stroke', 'red')

return container
}
Insert cell
Insert cell
viewof line2 = {
const WIDTH = width
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
const formatDate = d3.timeParse('%Y-%m-%d')
const DATA = bitcoinPrices.map(d => ({ ...d, date: formatDate(d.date) }))
const scaleX = d3.scaleTime()
.range([0, WIDTH])
.domain(d3.extent(DATA, d => d.date))
const scaleY = d3.scaleLinear()
.range([HEIGHT, 0])
.domain(d3.extent(DATA, d => d.close))

// créateur de ligne fait maison
const linePathCreator = DATA =>
DATA
// calculer x et y
.map(d => ([scaleX(d.date), scaleY(d.close)]))
// ajouter M au premier, L aux autres
.map(([x, y], index) => `${index === 0 ? 'M' : 'L'} ${x} ${y}` )
// joindre les coordonnés
.join(' ')

const line = svg.append('path')
.attr('d', linePathCreator(DATA))
.attr('fill', 'none')
.attr('stroke', 'red')

return container
}
Insert cell
Insert cell
viewof line3 = {
const WIDTH = width
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
const formatDate = d3.timeParse('%Y-%m-%d')
const DATA = bitcoinPrices.map(d => ({ ...d, date: formatDate(d.date) }))
const scaleX = d3.scaleTime()
.range([0, WIDTH])
.domain(d3.extent(DATA, d => d.date))
const scaleY = d3.scaleLinear()
.range([HEIGHT, 0])
.domain(d3.extent(DATA, d => d.close))

const linePathCreator = d3.line()
.x(d => scaleX(d.date))
.y(d => scaleY(d.close))
.curve(d3.curveBasis) // ici

const line = svg.append('path')
.attr('d', linePathCreator(DATA))
.attr('fill', 'none')
.attr('stroke', 'red')

return container
}
Insert cell
Insert cell
viewof line4 = {
const WIDTH = width
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
const formatDate = d3.timeParse('%Y-%m-%d')
const DATA = bitcoinPrices.map(d => ({ ...d, date: formatDate(d.date) }))
const scaleX = d3.scaleTime()
.range([0, WIDTH])
.domain(d3.extent(DATA, d => d.date))
const scaleY = d3.scaleLinear()
.range([HEIGHT, 0])
.domain(d3.extent(DATA, d => d.close))

const linePathCreator = d3.line()
.x(d => scaleX(d.date))
.y(d => scaleY(d.close))
.curve(d3.curveStep) // ici

const line = svg.append('path')
.attr('d', linePathCreator(DATA))
.attr('fill', 'none')
.attr('stroke', 'red')

return container
}
Insert cell
Insert cell
viewof area = {
const WIDTH = width
const HEIGHT = width / 4
const container = DOM.svg(WIDTH, HEIGHT)
const svg = d3.select(container)
const formatDate = d3.timeParse('%Y-%m-%d')
const DATA = bitcoinPrices.map(d => ({ ...d, date: formatDate(d.date) }))
const scaleX = d3.scaleTime()
.range([0, WIDTH])
.domain(d3.extent(DATA, d => d.date))
const scaleY = d3.scaleLinear()
.range([HEIGHT, 0])
.domain(d3.extent(DATA, d => d.close))

// la fonction d3.area() pour créer l'attribut "d"
const pathCreator = d3.area()
// quelle échelle, quelle donnée pour l'axe X
.x(d => scaleX(d.date))
// pour l'axe vertical nous devons définir de où à où remplir
// ici la ligne de base correspond à la valeur minimum
.y0(scaleY(d3.min(DATA, d => d.close)))
.y1(d => scaleY(d.close))

const line = svg.append('path')
.attr('d', pathCreator(DATA))
.attr('fill', 'steelblue')
return container
}
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more