You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:42:17