Вопрос: Как я могу получить значения данных x/y из наблюдаемого графика для точки, над которой наведен курсор мыши? (Или коснуться на мобильном телефоне.)
Предыстория: я пытаюсь создать интерактивную ленту, как на https://merrysky.net. Поэтому я хотел бы преобразовать положение X мыши в значение времени/температуры из построенных данных.
Текущий прогресс:
Я изучил документацию Observable Plot . Кажется, есть API для обновления определенных меток, когда указатель находится рядом с ними, но я не увидел ничего для извлечения данных из графика на основе положения мыши. Знак Перекрестие кажется наиболее близким к тому, что я ищу.
Возможно, необходимо использовать базовый API D3?
Для прямого д3 вот очень похожий эффект я писала, блин, 8 лет назад...



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Ух ты, 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/…
Возможно, я смогу использовать комбинацию вашего решения и моего решения: постоянный график, подобный вашему, плюс еще один график, наложенный сверху только с красным правилом, когда мышь находится за пределами графика. Тогда нужно будет уничтожить и создать заново только более простой сюжет, когда нужно будет обновить красное правило. Есть идеи получше? В идеале знак Plot.ruleX можно добавлять и удалять динамически.
@Leftium, см. редактирование выше. На основе этого примера есть способ зацепить внутренности и обновить сюжет. Хотя, честно говоря, с таким большим количеством настроек (возможно, хрупких, когда библиотека работает), мне любопытно, что на самом деле обеспечивает Observable.plot. Вы могли бы просто сделать все это в прямом d3.
Ух ты, спасибо~ Ваш метод редактирования намного чище, чем я надеялся. (Ответ принят!) Моя окончательная временная шкала погоды будет более сложной, чем пример в этом вопросе, но в основном статической. Я думаю, что красное правило — это единственное, что нужно сделать через d3. Возможно, стоит сделать все это в d3, но я не знаком с d3. Observable.plot показался более доступным и помог мне на 90%.
Я адаптировал этот ответ для своей конкретной проблемы с некоторыми различными исправлениями/улучшениями: stackoverflow.com/a/78595834/117030 Еще раз спасибо!
Эй, я добился некоторого прогресса и открыл похожий вопрос: stackoverflow.com/q/78597528/117030
Я только что понял, что если шкалы можно генерировать вне сюжета, то updateRuleX() также можно определять вне сюжета, верно?
Действительно, мне удалось переместить updateRuleX() наружу и удалить лишний график . Посмотрите, чего мне удалось достичь с вашей помощью: Weather-sense.leftium.com
Я адаптировал Ответ Марка для своего приложения:
Временная шкала и сюжет радара теперь взаимно реагируют:
Заметные изменения/улучшения в ответе Марка:
pg = d3.select(div).select('g'). По какой-то причине d3.select('g') не работал в моем приложении (и селектор был слишком широким, выбирая другой SVG на странице).value находится за пределами домена графика.render(). (Не требуется, но исправляет ошибку типа.)Другие важные дополнения:
Можете ли вы попытаться добавить событие мыши в SVG, а затем использовать эти координаты для преобразования их в координаты данных? проверьте jsfiddle.net/Lum5fxg1