如何获取Observable Plot鼠标悬停(移动端触摸)位置的X/Y数据值?
实现交互式时间线鼠标悬停数据同步与标线跟随
无需直接切换到D3,Observable Plot 可结合少量D3工具实现需求
你提到的Crosshair标记是基础,但要实现鼠标位置到时间/温度的映射,核心是利用Observable Plot内置的比例尺(scale),结合D3的二分查找工具来精准匹配数据:
- 第一步:获取图表的x轴比例尺,通过
plot.scales.x访问,调用invert()方法将鼠标的像素坐标转换为对应的时间数据值 - 第二步:用D3的
d3.bisector对时间序列数据做二分查找,找到鼠标位置对应的最接近的数据条目,提取温度值 - 第三步:同步更新顶部显示的时间/温度文本,以及红色标线的x坐标
代码示例
// 假设已创建Observable Plot实例并挂载到容器 const plotContainer = document.getElementById("temp-plot-container"); const tempPlot = Plot.plot({ x: { type: "time", domain: d3.extent(data, d => d.time) }, y: { label: "Temperature (°C)" }, marks: [ Plot.line(data, { x: "time", y: "temp" }), // 初始红色标线 Plot.ruleX([data[0].time], { stroke: "red", strokeWidth: 2, className: "time-marker" }) ] }); plotContainer.appendChild(tempPlot); // 监听鼠标移动事件 plotContainer.addEventListener("mousemove", (e) => { // 计算鼠标相对于图表容器的x像素位置 const relativeX = e.clientX - plotContainer.offsetLeft; // 像素转时间值 const targetTime = tempPlot.scales.x.invert(relativeX); // 二分查找匹配最接近的数据点 const bisectTime = d3.bisector(d => d.time).left; const dataIndex = bisectTime(data, targetTime); const activeData = data[dataIndex] || data[data.length - 1]; // 更新顶部显示 document.getElementById("current-time").textContent = activeData.time.toLocaleString(); document.getElementById("current-temp").textContent = `${activeData.temp.toFixed(1)}°C`; // 更新红色标线位置 const marker = tempPlot.querySelector(".time-marker"); const markerX = tempPlot.scales.x(activeData.time); marker.setAttribute("x1", markerX); marker.setAttribute("x2", markerX); });
若需更精细控制,可直接用D3操作SVG
如果Observable Plot的封装限制了你的需求,直接用D3操作图表SVG元素也是可行的:
- 用
d3.select选中图表的SVG,监听mousemove事件 - 用D3的时间比例尺(
d3.scaleTime)或线性比例尺完成像素与数据的转换 - 手动更新标线元素的位置和显示文本,逻辑和上面一致
内容的提问来源于stack exchange,提问作者Leftium
相关产品推荐
相关产品推荐

