Published
Edited
Aug 25, 2022
Importers
Insert cell
Insert cell
Insert cell
import {slide} from "@mbostock/slide"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
topGenres = ["Action", "Comedy", "Animation", "Drama"]
Insert cell
petalColors = ['#ffc8f0', '#cbf2bd', '#afe9ff', '#ffb09e']
Insert cell
colorObj = {
const colors = _.zipObject(topGenres, petalColors)
colors.Other = '#FFF2B4'
return colors
}
Insert cell
rated = ['G', 'PG', 'PG-13', 'R']
Insert cell
petalPaths = [
'M0 0 C50 50 50 100 0 100 C-50 100 -50 50 0 0',
'M-35 0 C-25 25 25 25 35 0 C50 25 25 75 0 100 C-25 75 -50 25 -35 0',
window.localStorage.petalPath || '"M0,0 C50,40 50,70 20,100 L0,85 L-20,100 C-50,70 -50,40 0,0"',
'M0 0 C50 25 50 75 0 100 C-50 75 -50 25 0 0',
'M0,85 L-20,100 C-55,70 -40,50 -30,40 L-20,60 C-35,20 -10,25 0,0 M0,85 L20,100 C55,70 40,50 30,40 L20,60 C35,20 10,25 0,0'
]
Insert cell
pathObj = _.zipObject(rated, petalPaths)
Insert cell
pathColors = ['#46e276', '#6e94e7', '#b877e9', '#f25d75']
Insert cell
Insert cell
scrollSVG = (svg) => {
return html`<div style='max-height:${width/2}px;overflow-y:scroll;overflow-x:hidden;'>${svg}</div>`
}
Insert cell
pathWidth = 120
Insert cell
perRow = Math.floor(width / pathWidth)
Insert cell
svgHeight = (Math.ceil(movies.length / perRow) + 0.5) * pathWidth
Insert cell
calculateGridPos = (i) => {
return [(i % perRow + 0.5) * pathWidth, (Math.floor(i / perRow) + 0.5) * pathWidth]
}
Insert cell
transformCode = (positions, transform1, transform2) => {
return html`
<code style='font-size: 2.3vw; font-weight: bold;'>
&lt;rect x=${positions.x} y=${positions.y} />
</code>

<pre><code style='font-size: 2.3vw; color: ${pathColors[1]}; font-weight: bold;'>&lt;rect x=${positions.x} y=${positions.y}
transform='${transform1}' /></code></pre>

${transform2 ?
`<pre><code style='font-size: 2.3vw; color: ${pathColors[2]}; font-weight: bold;'>&lt;rect x=${positions.x} y=${positions.y}
transform='${transform2}' /></code></pre>`
: ''
}
`
}
Insert cell
transformSVG = (positions, transform1, transform2) => {
const margin = {left: 30, top: 15}
const el = html`
<svg width=${textWidth + margin.left} height=${textWidth / 2 + margin.top}>
<g transform='translate(${margin.left}, ${margin.top})'>
<g>
<rect width=100 height=100 x=${positions.x} y=${positions.y} />
<g class='x axis' />
<g class='y axis' />
</g>
<g id='transform1' opacity=0.75 transform='${transform1}'>
<rect width=100 height=100 x=${positions.x} y=${positions.y} fill=${pathColors[1]} />
<g class='x axis' />
<g class='y axis' />
</g>

${transform2 ?
`<g id='transform2' opacity=0.75 transform='${transform2}'>
<rect width=100 height=100 x=${positions.x} y=${positions.y} fill=${pathColors[2]} />
<g class='x axis' />
<g class='y axis' />
</g>` : ''
}
</g>
</svg>
`
// helpers
const xAxis = d3.axisTop()
.scale(d3.scaleLinear().domain([0, textWidth]).range([0, textWidth]))
.tickSizeInner(-textWidth / 2)
.tickSizeOuter(0)
.ticks(4)
const yAxis = d3.axisLeft()
.scale(d3.scaleLinear().domain([0, textWidth / 2]).range([0, textWidth / 2]))
.tickSizeInner(-textWidth)
.tickSizeOuter(0)
.ticks(4)

// rendering
const svg = d3.select(el)
svg.selectAll('.x.axis').call(xAxis)
svg.selectAll('.y.axis').call(yAxis)
svg.selectAll('.axis').selectAll('line')
.attr('stroke-dasharray', '5 2')
.attr('opacity', 0.5)
svg.selectAll('.axis').selectAll('text')
.style('font-size', '1.2em')
// translated elements
svg.select('#transform1').selectAll('line, path')
.attr('stroke', pathColors[1]).attr('stroke-width', 2)
svg.select('#transform1').selectAll('text')
.attr('fill', pathColors[1])
.style('font-weight', 'bold')
if (transform2) {
svg.select('#transform2').selectAll('line, path')
.attr('stroke', pathColors[2]).attr('stroke-width', 2)
svg.select('#transform2').selectAll('text')
.attr('fill', pathColors[2])
.style('font-weight', 'bold')
}
return el
}
Insert cell
textWidth = 640
Insert cell
md`
## Appendix
`
Insert cell
movies = FileAttachment("movies (1).json").json()
.then(data => {
return _.chain(data)
.map(d => {
return {
title: d.Title,
released: new Date(d.Released),
genres: d.Genre.split(', '),
rating: +d.imdbRating,
votes: +(d.imdbVotes.replace(/,/g, '')),
rated: d.Rated,
}
}).sortBy(d => -d.released).value()
})
Insert cell
d3 = require('d3')
Insert cell
_ = require('lodash')
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