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

求助:在Plotly.js中基于背景图片实现点击绘制直线

实现Plotly.js背景图上点击绘制连续直线

核心思路

  • 维护变量记录上一次点击的坐标,每次点击时:
    1. 获取点击位置对应的图表数据坐标系值
    2. 若已有上一个点,将两个点作为线段端点添加到散点图数据数组
    3. 调用Plotly.update更新图表,渲染新线段
  • 给散点图配置mode: 'lines+markers',同时显示线段与点击标记点,提升交互可视化效果

修改后的完整代码

<script src='https://cdn.plot.ly/plotly-2.16.1.min.js'></script>
<div id='plotlyContainer'></div>

<script>
var d3 = Plotly.d3;
var imgWidth = 0;
var imgHeight = 0;
const imgs = new Image();
let plotCon  = document.getElementById('plotlyContainer');
// 记录上一次点击的坐标
let lastPoint = null;

imgs.onload = function() {
  imgWidth = this.width;
  imgHeight = this.height;  
  loadImgGraph(imgWidth, imgHeight);  
}
imgs.src = 'https://i.sstatic.net/cRbua.png';

function loadImgGraph(imgWidth, imgHeight) {
  var data = [
    {
      x: [],
      y: [],
      type: 'scatter',
      mode: 'lines+markers', // 同时显示线段和点击标记点
      line: {color: '#ff0000', width: 2}, // 自定义直线样式
      marker: {size: 6, color: '#0000ff'} // 自定义标记点样式
    }
  ];
  var layout = {
    autosize: false,
    width: imgWidth,
    height: imgHeight,  
    xaxis: {range: [0, 100], dtick: 25, ticks: 'outside', tickwidth: 2, gridcolor: "black", gridwidth: 2, mirror: true, linecolor: 'black', linewidth: 1},
    yaxis: {range: [140, 0], dtick: 20, ticks: 'outside', tickwidth: 2, gridcolor: "black", gridwidth: 2, mirror: true, linecolor: 'black', linewidth: 1},
    margin: {
      l: 32,
      r: 20,
      b: 30,
      t: 30
    },
    images: [
          {
              source: 'https://i.sstatic.net/cRbua.png',              
              xref: 'paper',
              yref: 'paper',
              x: 0,
              y: 1,
              sizex: 1,
              sizey: 1,
              sizing: 'fill',
              opacity: 1,
              layer: 'below'
          }
        ],
  };
  Plotly.newPlot('plotlyContainer', data, layout).then(clickEv);

  function clickEv() {
    var xaxis = plotCon._fullLayout.xaxis;
    var yaxis = plotCon._fullLayout.yaxis;
    var l = plotCon._fullLayout.margin.l;
    var t = plotCon._fullLayout.margin.t;
    
    plotCon.addEventListener('mousemove', function(evt) {
      var xInDataCoord = xaxis.p2c(evt.x - l);
      var yInDataCoord = yaxis.p2c(evt.y - t);
      
      Plotly.relayout(plotCon, 'title', ['x: ' + xInDataCoord.toFixed(2), 'y : ' + yInDataCoord.toFixed(2)].join('<br>'));
    });
    
    plotCon.on('plotly_click', function(evt){
      // 直接通过Plotly点击事件获取数据坐标系下的坐标
      const clickX = evt.points[0].x;
      const clickY = evt.points[0].y;
      
      if (lastPoint) {
        // 添加线段的起点与终点
        data[0].x.push(lastPoint.x, clickX);
        data[0].y.push(lastPoint.y, clickY);
        
        // 可选:添加null让每条线段独立(非连续折线)
        // data[0].x.push(null);
        // data[0].y.push(null);
        
        // 更新图表数据
        Plotly.update(plotCon, data);
      }
      // 更新上一次点击的坐标记录
      lastPoint = {x: clickX, y: clickY};
    });
  }
}
</script>

关键修改说明

  1. 状态变量新增:lastPoint用于存储上一次点击的坐标,作为下一条线段的起点
  2. 数据系列优化:配置mode: 'lines+markers'并自定义样式,让绘制的线段和点击点更清晰
  3. 点击事件逻辑优化:
    • 利用evt.points[0]直接获取转换后的data坐标系坐标,无需手动计算
    • 存在上一个点时,将两个点坐标添加到数据数组,调用Plotly.update高效更新图表
  4. 可选分段模式:若需要让每条线段独立显示,可在每段线段后给x、y数组添加null分隔

内容的提问来源于stack exchange,提问作者Vinodh Ganesh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 01:01:22