React D3 Emoji
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js"></script>
<script src="https://npmcdn.com/react@latest/dist/react-with-addons.js"></script>
<script src="https://npmcdn.com/react-dom@latest/dist/react-dom.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div class="container">
<div id="app"></div>
<div id="renders"></div>
</div>
</div>
SCSS
$emoji: #62a6ff;
$reactions: #ff6363;
.container {
background: #eee;
}
.area {
fill-opacity: 0.8;
}
.area.emoji {
fill: $emoji;
stroke: $emoji;
}
.area.reactions {
fill: $reactions;
stroke: $reactions;
}
.tick > line,
.domain {
stroke: #ccc;
fill: none;
}
.tick > text {
fill: #888;
}
.mouse-line {
stroke: #333;
}
.legend {
display: flex;
justify-content: space-around;
padding: 12px;
width: 100%;
}
.key {
display: flex;
align-items: center;
width: 25%;
}
.key span {
display: inline-block;
margin-right: 12px;
height: 24px;
width: 24px;
}
.emoji > span {
background: transparentize($emoji, 0.2);
}
.reactions > span {
background: transparentize($reactions, 0.2);
}
JavaScript 1.7
const dataUrl = "https://s3.amazonaws.com/thoughtbot-images/react-rendering-misconception/emoji-vs-reactions.json";
const height = 400;
const width = 800;
const padding = 40;
let renders = 0;
function trackRender() {
renders += 1;
document.getElementById("renders").innerText = `renders: ${renders}`;
}
function calculateScales(data) {
const xScale = d3.scaleTime().
domain([d3.min(data, d => d.x), d3.max(data, d => d.x)]).
range([padding, width - padding]);
const yScale = d3.scaleLinear().
domain([d3.min(data, d => d.y), d3.max(data, d => d.y)]).
range([height - padding, padding]);
return { x: xScale, y: yScale };
}
function closestPoint(mouseLocation, data) {
const interpolatedX = data.scales.x.invert(mouseLocation[0]);
const index = d3.bisector(d => d.x).left(data.values, interpolatedX);
const points = [
{
data: data.values[index],
distance: data.values[index].x - interpolatedX,
},
];
if (data.values[index - 1]) {
points.push({
data: data.values[index - 1],
distance: interpolatedX - data.values[index - 1].x,
});
}
return _.sortBy(points, "distance")[0].data;
}
function fetchData() {
return fetch(dataUrl)
.then(r => r.json())
.then(json => {
const parsed = json.map(j => ({ ...j, date: new Date(j["date"])}));
const sorted = _.sortBy(parsed, j => j["date"]);
const data = {
emoji: { values: [] },
reactions: { values: [] },
};
sorted.forEach(entry => {
const date = new Date(entry["date"]);
data["emoji"]["values"].push({ x: date, y: entry["emoji"] });
data["reactions"]["values"].push({ x: date, y: entry["reactions"] });
});
for (let key in data) {
const scales = calculateScales(data[key].values);
data[key]["scales"] = {
x: scales.x.clamp(true),
y: scales.y.clamp(true),
};
}
return data;
});
}
const MouseLine = ({...