Всплывающая подсказка с плавающим курсором запутывается, когда я пытаюсь сделать свой график d3 отзывчивым - PullRequest
0 голосов
/ 23 апреля 2020

Я добавил это в мой html контейнер:

<canvas width="16" height="9" style="width: 100%"></canvas>

и это в мой сценарий d3:

.attr("preserveAspectRatio", "xMinYMin meet")
    .attr("viewBox", "0 0 980 550")

Вы можете сообщить мне, если есть лучший способ сделай это. Я не могу использовать window.Innerwidth из-за ограничений страницы, на которой я должен добавить это.

HTML FILE:

<div id="wrapper" class="wrapper">
            <canvas width="16" height="9" style="width: 100%"></canvas>

            <div id="tooltip" class="tooltip">
                <div class="tooltip-date">
                    <span id="date"></span>
                </div>
                <div class="tooltip-confirmed">
                    Confirmed Cases: <span id="confirmed"></span>
                </div>
            </div>
        </div>

CSS:

.wrapper {
    position: relative;
}

.line {
    fill: none;
    stroke: #FF9933;
    stroke-width: 2;
}

.y-axis-label {
    fill: black;
    font-size: 1.4em;
    text-anchor: middle;
    transform: rotate(-90deg);
}

.listening-rect {
    fill: transparent;
}

body {
    display: flex;
    justify-content: center;
    padding: 5em 1em;
    font-family: sans-serif;
}

.tooltip {
    opacity: 0;
    position: absolute;
    top: -14px;
    left: 0;
    padding: 0.6em 1em;
    background: #fff;
    text-align: center;
    line-height: 1.4em;
    font-size: 0.9em;
    border: 1px solid #ddd;
    z-index: 10;
    transition: all 0.1s ease-out;
    pointer-events: none;
}

.tooltip:before {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    width: 12px;
    height: 12px;
    background: white;
    border: 1px solid #ddd;
    border-top-color: transparent;
    border-left-color: transparent;
    transform: translate(-50%, 50%) rotate(45deg);
    transform-origin: center center;
    z-index: 10;
}

.tooltip-date {
    margin-bottom: 0.2em;
    font-weight: 600;
    font-size: 1.1em;
    line-height: 1.4em;
}

Сценарий D3:

async function drawLineChart() {
  let dataset = await d3.json('./history.json')

  yAccessor = d => d.summary.total
  const dateParser = d3.timeParse("%Y-%m-%d")
  format = d3.timeFormat("%b-%d")
  const xAccessor = d => dateParser(d.day)
  console.log(format(xAccessor(dataset[0])))
  dataset = dataset.sort((a,b) => xAccessor(a) - xAccessor(b)).slice(0, 100)

  let dimensions = {
    width: 980,
    height: 550,
    margin: {
      top: 20,
      right: 30,
      bottom: 30,
      left: 70,
    },
  }
  dimensions.boundedWidth = dimensions.width - dimensions.margin.left - dimensions.margin.right
  dimensions.boundedHeight = dimensions.height - dimensions.margin.top - dimensions.margin.bottom

  // 3. Draw canvas

  const wrapper = d3.select("#wrapper")
    .append("svg")
    .attr("preserveAspectRatio", "xMinYMin meet")
    .attr("viewBox", "0 0 980 550")
     // .attr("width", dimensions.width)
     // .attr("height", dimensions.height)

  wrapper.append("rect")
    .attr("class", "background")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("fill", "#faebd7")

  const bounds = wrapper.append("g")
      .attr("transform", `translate(${
        dimensions.margin.left
      }, ${
        dimensions.margin.top
      })`)

  bounds.append("text")
      .attr("class", "watermark")
      .attr("x", dimensions.width - 180)
      .attr("y", dimensions.height - 60)
      .attr("fill", "grey")
      .style("opacity", 0.5)
      .html("@Alepthoughts")

  bounds.append("defs").append("clipPath")
      .attr("id", "bounds-clip-path")
    .append("rect")
      .attr("width", dimensions.boundedWidth)
      .attr("height", dimensions.boundedHeight)

  const clip = bounds.append("g")
    .attr("clip-path", "url(#bounds-clip-path)")

  // 4. Create scales

  const yScale = d3.scaleLinear()
    .domain(d3.extent(dataset, yAccessor))
    .range([dimensions.boundedHeight, 0])
    .nice()



  const xScale = d3.scaleTime()
    .domain(d3.extent(dataset, xAccessor))
    .range([0, dimensions.boundedWidth])

  const lineGenerator = d3.line()
    .x(d => xScale(xAccessor(d)))
    .y(d => yScale(yAccessor(d)))

  const line = clip.append("path")
      .attr("class", "line")
      .attr("d", lineGenerator(dataset))

  const yAxisGenerator = d3.axisLeft()
      .scale(yScale)


  const yAxis = bounds.append("g")
      .attr("class", "y-axis")
      .call(yAxisGenerator)

  const yAxisLabel = yAxis.append("text")
      .attr("class", "y-axis-label")
      .attr("x", -dimensions.boundedHeight / 2)
      .attr("y", -dimensions.margin.left + 20)
      .html("Confirmed Cases")



  const listeningRect = bounds.append("rect")
      .attr("class", "listening-rect")
      .attr("width", dimensions.boundedWidth)
      .attr("height", dimensions.boundedHeight)
      .on("mousemove", onMouseMove)
      .on("mouseleave", onMouseLeave)

  const tooltip = d3.select("#tooltip")
  const tooltipCircle = bounds.append("circle")
          .attr("class", "tooltip-circle")
          .attr("r", 4)
          .attr("stroke", "#FF9933")
          .attr("fill", "white")
          .attr("stroke-width", 2)
          .style("opacity", 0)

  function onMouseMove() {
    const mousePosition = d3.mouse(this)
    const hoveredDate = xScale.invert(mousePosition[0])

    const getDistanceFromHoveredDate = d => Math.abs(xAccessor(d) - hoveredDate)
    const closestIndex = d3.scan(dataset, (a, b) => (
    getDistanceFromHoveredDate(a) - getDistanceFromHoveredDate(b)
    ))
    const closestDataPoint = dataset[closestIndex]

    const closestXValue = xAccessor(closestDataPoint)
    const closestYValue = yAccessor(closestDataPoint)

    const formatDate = d3.timeFormat("%B %A %-d, %Y")
    tooltip.select("#date")
    .text(formatDate(closestXValue))

    //const formatTemperature = d => `${d3.format(".1f")(d)}°F`
    tooltip.select("#confirmed")
        .html(closestYValue)

    const x = xScale(closestXValue)
      + dimensions.margin.left
    const y = yScale(closestYValue)
      + dimensions.margin.top

    tooltip.style("transform", `translate(`
      + `calc(-50% + ${x}px),`
      + `calc(-100% + ${y}px)`
      + `)`)

    tooltip.style("opacity", 1)

    tooltipCircle
        .attr("cx", xScale(closestXValue))
        .attr("cy", yScale(closestYValue))
        .style("opacity", 1)
  }
  function onMouseLeave() {
    tooltip.style("opacity", 0)

    tooltipCircle.style("opacity", 0)
  }

}

Пример формата данных:

[
    {
      "day": "2019-03-10",
      "summary": {
        "total": 47,
      },
{
      "day": "2019-03-11",
      "summary": {
        "total": 60,
      }
]
...