Получить значения данных наблюдаемого графика x, y из положения мыши (наведение курсора мыши)

Вопрос: Как я могу получить значения данных x/y из наблюдаемого графика для точки, над которой наведен курсор мыши? (Или коснуться на мобильном телефоне.)

Предыстория: я пытаюсь создать интерактивную ленту, как на https://merrysky.net. Поэтому я хотел бы преобразовать положение X мыши в значение времени/температуры из построенных данных.

Текущий прогресс:


  • В настоящее время красное правило на сюжете автоматически обновляется (попробуйте воспроизвести или перетащить временную шкалу радара).
  • Следующая задача — реактивное обновление в другую сторону: данные времени и температуры, отображаемые в самом верху, изменяются при наведении курсора мыши на график температуры. (Красное правило должно следовать за указателем мыши, и соответствующие данные должны быть обновлены.)

Я изучил документацию Observable Plot . Кажется, есть API для обновления определенных меток, когда указатель находится рядом с ними, но я не увидел ничего для извлечения данных из графика на основе положения мыши. Знак Перекрестие кажется наиболее близким к тому, что я ищу.

Возможно, необходимо использовать базовый API D3?

Можете ли вы попытаться добавить событие мыши в SVG, а затем использовать эти координаты для преобразования их в координаты данных? проверьте jsfiddle.net/Lum5fxg1

ashutosh rathi 03.06.2024 12:55

Для прямого д3 вот очень похожий эффект я писала, блин, 8 лет назад...

Mark 03.06.2024 15:15
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
137
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ух ты, Observable Plot — это очень круто. Похоже, вы можете полностью воспроизвести свою цель (и то, что я сделал 8 лет назад), просто:

const plot = Plot.plot({
  marks: [
    // draw the line graph
    Plot.line(data, { x: 'x', y: 'y' }),
    // add vertical interaction line
    Plot.ruleX(data, Plot.pointerX({ x: 'x', py: 'y', stroke: 'red' })),
    // add interaction dot
    Plot.dot(data, Plot.pointerX({ x: 'x', y: 'y', stroke: 'red' })),      
    // add interaction text
    Plot.text(
      data,
      Plot.pointerX({
        x: 'x',
        y: 'y',
        dx: 20,
        text: 'y',
      })
    ),
  ],
});

Пример выполнения:

<!DOCTYPE html>
<div id = "myplot"></div>
<script src = "https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src = "https://cdn.jsdelivr.net/npm/@observablehq/[email protected]"></script>
<script type = "module">
  const data = [];
  for (let i = 0; i < 20; i++) {
    data.push({
      x: i,
      y: Math.random() * 100,
    });
  }

  const plot = Plot.plot({
    marks: [
      Plot.line(data, { x: 'x', y: 'y' }),
      Plot.ruleX(data, Plot.pointerX({ x: 'x', py: 'y', stroke: 'red' })),
      Plot.dot(data, Plot.pointerX({ x: 'x', y: 'y', stroke: 'red' })),
      Plot.text(
        data,
        Plot.pointerX({
          x: 'x',
          y: 'y',
          dx: 20,
          text: 'y',
        })
      ),
    ],
  });

  const div = document.querySelector('#myplot');
  div.append(plot);
</script>

-- Изменения на основе комментариев --

Как насчет чего-то вроде этого. Он обеспечивает точку входа в сюжет, поэтому вы можете снести и воссоздать только то, что вам нужно:

<!DOCTYPE html>
<div id = "myplot"></div>
<script src = "https://cdn.jsdelivr.net/npm/d3@7"></script>
<script src = "https://cdn.jsdelivr.net/npm/@observablehq/[email protected]"></script>
<script type = "module">
  const div = document.querySelector('#myplot');

  const data1 = [];
  for (let i = 0; i < 20; i++) {
    data1.push({
      x: i,
      y: Math.random() * 100,
    });
  }

  const plot1 = Plot.ruleX([0], {
    render: (i, s, v, d, c, next) => {
      const g = next(i, s, v, d, c);
      c.ownerSVGElement.updateRuleX = (value) => {
        const pg = d3.select('g');
        pg.select('.custom-rule').remove();

        const ig = pg.append('g')
          .attr('class','custom-rule');
        
        ig.append('line')
          .attr('x1', s.x(value))
          .attr('x2', s.x(value))
          .attr('y1', s.y.range()[0])
          .attr('y2', s.y.range()[1])
          .attr('stroke', 'red');

        ig.append('circle')
          .attr('r', 5)
          .attr('stroke', 'red')
          .attr('cx', s.x(value))
          .attr('cy', s.y(data1[value].y));
      };
    },
  }).plot({
    marks: [
      Plot.line(data1, {x: 'x', y: 'y'}),
      Plot.ruleX(data1, Plot.pointerX({ x: 'x', py: 'y', stroke: 'red' })),
      Plot.dot(data1, Plot.pointerX({ x: 'x', y: 'y', stroke: 'red' })),
      Plot.text(
        data1,
        Plot.pointerX({
          x: 'x',
          y: 'y',
          dx: 20,
          text: 'y',
        })
      ),
    ],
  });
  div.append(plot1);

  const updateRuleExternal = () => {
    const v = Math.floor(Math.random() * (20 - 1 + 1) + 1);
    plot1.updateRuleX(v)
  };
  setInterval(updateRuleExternal, 500);

</script>

Да! Я нашел Plot.pointerX после того, как опубликовал свой вопрос. В нем даже есть plot.addEventListener(), поэтому элементы вне сюжета можно обновлять. Однако в вашем решении по-прежнему отсутствует одна часть: обновление красного правила на основе внешних событий. (Обратите внимание, что на сайте merrysky.net есть несколько красных правил на нескольких графиках, и все они связаны между собой и отображают одно и то же время). Мне удалось сделать мое красное правило таким же реактивным, но оно требует неоднократного разрушения и создания новых графиков: github.com/Leftium/weather-sense/blob/…

Leftium 05.06.2024 00:45

Возможно, я смогу использовать комбинацию вашего решения и моего решения: постоянный график, подобный вашему, плюс еще один график, наложенный сверху только с красным правилом, когда мышь находится за пределами графика. Тогда нужно будет уничтожить и создать заново только более простой сюжет, когда нужно будет обновить красное правило. Есть идеи получше? В идеале знак Plot.ruleX можно добавлять и удалять динамически.

Leftium 05.06.2024 00:59

@Leftium, см. редактирование выше. На основе этого примера есть способ зацепить внутренности и обновить сюжет. Хотя, честно говоря, с таким большим количеством настроек (возможно, хрупких, когда библиотека работает), мне любопытно, что на самом деле обеспечивает Observable.plot. Вы могли бы просто сделать все это в прямом d3.

Mark 05.06.2024 15:53

Ух ты, спасибо~ Ваш метод редактирования намного чище, чем я надеялся. (Ответ принят!) Моя окончательная временная шкала погоды будет более сложной, чем пример в этом вопросе, но в основном статической. Я думаю, что красное правило — это единственное, что нужно сделать через d3. Возможно, стоит сделать все это в d3, но я не знаком с d3. Observable.plot показался более доступным и помог мне на 90%.

Leftium 05.06.2024 18:08

Я адаптировал этот ответ для своей конкретной проблемы с некоторыми различными исправлениями/улучшениями: stackoverflow.com/a/78595834/117030 Еще раз спасибо!

Leftium 08.06.2024 15:38

Эй, я добился некоторого прогресса и открыл похожий вопрос: stackoverflow.com/q/78597528/117030

Leftium 09.06.2024 05:56

Я только что понял, что если шкалы можно генерировать вне сюжета, то updateRuleX() также можно определять вне сюжета, верно?

Leftium 30.06.2024 01:21

Я адаптировал Ответ Марка для своего приложения:

Временная шкала и сюжет радара теперь взаимно реагируют:

  • Воспроизведение/перетаскивание временной шкалы радара обновляет сюжет.
  • При наведении курсора на график обновляется временная шкала радара.
  • (Дата/время вверху также обновляется)

Заметные изменения/улучшения в ответе Марка:

  • Сделайте селектор более конкретным: pg = d3.select(div).select('g'). По какой-то причине d3.select('g') не работал в моем приложении (и селектор был слишком широким, выбирая другой SVG на странице).
  • Не добавляйте (рендерингуйте) красное правило X, если value находится за пределами домена графика.
  • Верните значение из render(). (Не требуется, но исправляет ошибку типа.)

Другие важные дополнения:

Другие вопросы по теме