D3 scaleTime getting really strange dates

  Kiến thức lập trình

I am working on a timeline in d3. My expectations are that it shows the day of today, and then the user can zoom in/out to see more times and dates. However to start with it gets me a date in 2034 and then after zooming, it gets me something closer but still pretty wrong.

This is the code (wrapped in a react component):

import { useRef, useEffect } from 'react';
import * as d3 from 'd3';

const D3Timeline = () => {
  const svgRef = useRef<SVGSVGElement>(null);
  const initializedRef = useRef(false); // Flag to track initialization
  const height = 100;
  const width = 600;

  useEffect(() => {
    if (initializedRef.current) return; // Ensure initialization only happens once
    initializedRef.current = true;

    const svg = d3
      .select(svgRef.current)
      .attr('width', width)
      .attr('height', height);

    const now = new Date();
    const oneYearAgo = new Date(
      now.getFullYear(),
      now.getMonth() - 1,
      now.getDate()
    );
    const oneYearLater = new Date(
      now.getFullYear(),
      now.getMonth() + 1,
      now.getDate()
    );

    const initialStartDate = new Date(now.getTime() - 6 * 60 * 60 * 1000); // 6 hours before now
    const initialEndDate = new Date(now.getTime() + 6 * 60 * 60 * 1000); // 6 hours after now

    const x = d3
      .scaleTime()
      .domain([oneYearAgo, oneYearLater])
      .range([0, width]);

    const xAxisGroup = svg
      .append('g')
      .attr('transform', `translate(0,${height - 20})`);

    const centerLine = svg
      .append('line')
      .attr('x1', width / 2)
      .attr('y1', 0)
      .attr('x2', width / 2)
      .attr('y2', height)
      .attr('stroke', 'red')
      .attr('stroke-width', 2);

    const zoom = d3
      .zoom()
      .scaleExtent([1, 365]) // Allow zooming from 1 day to 3 years
      .translateExtent([
        [0, 0],
        [width, height],
      ])
      .extent([
        [0, 0],
        [width, height],
      ])
      .on('zoom', zoomed);

    // Initial axis rendering
    xAxisGroup.call(
      d3
        .axisBottom(x)
        .ticks(width / 80)
        .tickSizeOuter(0)
    );

    // Calculate the initial scale factor to fit 12 hours in the viewport
    const initialScale = width / (x(initialEndDate) - x(initialStartDate));
    const initialTranslate = -x(now) * initialScale + width / 2;

    const initialZoom = d3.zoomIdentity
      .scale(initialScale)
      .translate(initialTranslate, 0);

    svg.call(zoom).call(zoom.transform, initialZoom);

    function zoomed(event) {
      const transform = event.transform;
      const xz = transform.rescaleX(x);
      xAxisGroup.call(
        d3
          .axisBottom(xz)
          .ticks(width / 80)
          .tickSizeOuter(0)
      );

      // Get the center date
      const centerDate = xz.invert(width / 2);
      console.log(centerDate);
    }

    // Handle zoom using the mouse wheel
    svg.on('wheel', (event) => {
      event.preventDefault();
      const direction = event.deltaY > 0 ? 1.1 : 0.9;
      const zoomLevel = d3.zoomTransform(svg.node()).k * direction;
      const zoomTransform = d3.zoomIdentity.scale(zoomLevel);
      svg.transition().duration(500).call(zoom.transform, zoomTransform);
    });
  }, []);

  return <svg ref={svgRef}></svg>;
};

You can see it working in here:
https://stackblitz.com/edit/vitejs-vite-xgiskr?file=src%2Fcomponents%2FD3Timeline.tsx,src%2Fmain.tsx&terminal=dev

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT