DEV Community

loading...
Cover image for Visualizing the Breaking Bad Timeline  – Using React (Hooks) with D3

Visualizing the Breaking Bad Timeline – Using React (Hooks) with D3

muratkemaldar profile image Murat Kemaldar Updated on ・1 min read

Hi, this is part 8 of "Using React (Hooks) with D3, and in this one, we visualize the Breaking Bad timeline, by working with time scales.

Code:
https://github.com/muratkemaldar/using-react-hooks-with-d3/tree/08-breaking-bad

Timeline component:

import React, { useRef, useEffect } from "react";
import { select, min, max, scaleTime, scaleLinear, axisBottom } from "d3";
import useResizeObserver from "./useResizeObserver";

// helper to transform date strings to date objects
const getDate = dateString => {
  const date = dateString.split("-");
  return new Date(date[2], date[0] - 1, date[1]);
};

function BBTimeline({ data, highlight }) {
  const svgRef = useRef();
  const wrapperRef = useRef();
  const dimensions = useResizeObserver(wrapperRef);

  // will be called initially and on every data change
  useEffect(() => {
    const svg = select(svgRef.current);
    if (!dimensions) return;

    const minDate = min(data, episode => getDate(episode.air_date));
    const maxDate = max(data, episode => getDate(episode.air_date));

    // maps dates to x-values
    const xScale = scaleTime()
      .domain([minDate, maxDate])
      .range([0, dimensions.width]);

    // maps numbers (character count) to y-values
    const yScale = scaleLinear()
      .domain([max(data, episode => episode.characters.length), 0])
      .range([0, dimensions.height]);

    // renders line for each episode
    svg
      .selectAll(".episode")
      .data(data)
      .join("line")
      .attr("class", "episode")
      .attr("stroke", episode =>
        episode.characters.includes(highlight) ? "blue" : "black"
      )
      .attr("x1", episode => xScale(getDate(episode.air_date)))
      .attr("y1", dimensions.height)
      .attr("x2", episode => xScale(getDate(episode.air_date)))
      .attr("y2", episode => yScale(episode.characters.length));

    // x-axis
    const xAxis = axisBottom(xScale);
    svg
      .select(".x-axis")
      .style("transform", `translateY(${dimensions.height}px)`)
      .call(xAxis);

    // draw the gauge
  }, [data, dimensions, highlight]);

  return (
    <div ref={wrapperRef} style={{ marginBottom: "2rem" }}>
      <svg ref={svgRef}>
        <g className="x-axis" />
      </svg>
    </div>
  );
}

export default BBTimeline;

Enter fullscreen mode Exit fullscreen mode

Hope you like it!

Discussion (0)

Forem Open with the Forem app