Published
Edited
Nov 4, 2019
Insert cell
md`# typography with d3.force`
Insert cell
width = 500
Insert cell
height = 80
Insert cell
datanodes = 'sungryeol park'.split('').map((d,i) => ({ text: d, index: i }))
Insert cell
md`## mouseover animation 1`
Insert cell
{
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height)
const onMouseEnter = () => {
const ev = d3.event;
datanodes.forEach((d, i) => {
datanodes[i].x = Math.random() * width / 2
datanodes[i].y = Math.random() * height / 2
})
sim.force('x', d3.forceX().x(d => d.index * 15))
.force('y', d3.forceY().y(d => height / 2))
.alpha(1)
.restart();
}
const onMouseLeave = () => {
const ev = d3.event
sim.force('x', d3.forceX().x(() => Math.random() * width / 2))
.force('y', d3.forceY().y(() => Math.random() * height / 2))
.alpha(1)
.restart();
}
svg.on('mouseenter', onMouseEnter);
svg.on('mouseleave', onMouseLeave);

const ticked = () => {
texts.attr('transform', d => `translate(${d.x},${d.y})`)
}
const sim = d3.forceSimulation(datanodes) // force is not recalculated.
.force('x', d3.forceX().x(d => d.index * 20)) // force('x', arg => ) argument is not a full data. only X pos.
.force('y', d3.forceY().y(d => height / 2))
.force('charge', d3.forceManyBody().strength(2))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('collision', d3.forceCollide().radius(5))
.on('tick', ticked);
const gEnter = enter => enter.append('text')
.classed('mytext', true)
.text(d => d.text)
const gUpdate = null;
const gExit = null;
const texts = svg.selectAll('.mytext')
.data(datanodes)
.join(gEnter, gUpdate, gExit);
// invalidation.then(() => sim.stop());
return svg.node();
}
Insert cell
md`## mouseover animation 2`
Insert cell
anim2 = ({
width: 500,
height: 500
})
Insert cell
{
const svg = d3.create('svg')
.attr('width', anim2.width)
.attr('height', anim2.height)
const onMouseEnter = () => {
const ev = d3.event;
console.log('ticked');
// const _root = datanodes[0];
// _root.x = ev.offsetX;
// _root.y = ev.offsetY;
datanodes.forEach((d, i) => {
datanodes[i].x = Math.random() * width / 2
datanodes[i].y = Math.random() * height / 2
})
sim.force('x', d3.forceX().x(d => d.index * 20))
.force('y', d3.forceY().y(d => height / 2))
.alpha(1)
.restart();
}
const onMouseLeave = () => {
const ev = d3.event
sim.force('x', d3.forceX().x(() => Math.random() * width / 2))
.force('y', d3.forceY().y(() => Math.random() * height / 2))
.alpha(1)
.restart();
}
svg.on('mouseenter', onMouseEnter);
svg.on('mouseleave', onMouseLeave);

const ticked = () => {
texts.attr('transform', d => `translate(${d.x},${d.y})`)
}
// const root = { text: '.', root: true };
// if (!datanodes[0].root) datanodes.unshift(root);
const sim = d3.forceSimulation(datanodes) // force is not recalculated.
.force('x', d3.forceX().x(d => d.index * 20)) // force('x', arg => ) argument is not a full data. only X pos.
.force('y', d3.forceY().y(d => height / 2))
.force('charge', d3.forceManyBody().strength(2))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('collision', d3.forceCollide().radius(5))
.on('tick', ticked);
const gEnter = enter => enter.append('text')
.classed('mytext', true)
.text(d => d.text)
const gUpdate = null;
const gExit = null;
const texts = svg.selectAll('.mytext')
.data(datanodes)
.join(gEnter, gUpdate, gExit);
// invalidation.then(() => sim.stop());
return svg.node();
}
Insert cell
d3 = require('d3')
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