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

Plotly选择模式下实现触控设备单点点击坐标获取

在Plotly select模式下实现触控设备单点点击获取坐标数据的解决方案

问题描述

使用Plotly.js绘制热力图并设置dragmode: 'select'后,出现跨设备行为不一致的问题:

  • 非触控设备:矩形拖拽选择、单点点击均能正常触发plotly_selected和plotly_click事件,可正常获取坐标数据
  • 触控设备(含Chrome开发者工具模拟):矩形拖拽选择功能正常,能触发plotly_selected事件;但单点点击仅触发无坐标数据的plotly_selected事件,plotly_click事件完全不触发

已尝试设置clickmode为"select"、"event"或"event+select",问题未得到解决。

解决方案

方案1:通过plotly_selected事件兼容触控单点点击

利用触控设备单点点击时plotly_selected事件无有效选择数据的特征,手动提取触控位置并转换为图表坐标数据:

const z = Array.from({length: 50}, () => Array.from({length: 50}, () => Math.floor(Math.random() * 255)));
const data = [{type: 'heatmap', z: z}];
const layout = {'yaxis': {'scaleanchor': 'x'}, 'dragmode': 'select'}
const config = { modeBarButtons: [["select2d"]] };
const plot = document.querySelector("#plot");
Plotly.newPlot(plot, data, layout, config);

// 处理非触控设备的plotly_click事件
plot.on("plotly_click", (e) => { 
    console.log("plotly_click", e); 
});

// 处理plotly_selected事件,兼容触控设备单点点击
plot.on("plotly_selected", (e) => { 
    console.log("plotly_selected", e);
    // 判断是否为触控单点点击:无选中数据且无选择区域
    if (!e.points?.length && !e.range) {
        const touchEvent = e.event?.touches?.[0] || e.event?.changedTouches?.[0];
        if (touchEvent) {
            // 将屏幕坐标转换为图表内部相对坐标
            const plotRect = plot.getBoundingClientRect();
            const relX = touchEvent.clientX - plotRect.left;
            const relY = touchEvent.clientY - plotRect.top;
            
            // 获取该位置对应的热力图数据点
            Plotly.Fx.hover(plot, {xval: relX, yval: relY}, false).then((hoverData) => {
                if (hoverData.points?.length) {
                    console.log("触控单点点击坐标数据", hoverData.points[0]);
                    // 可选:模拟plotly_click事件,复用原有点击逻辑
                    const clickEvent = new CustomEvent('plotly_click', {detail: hoverData});
                    plot.dispatchEvent(clickEvent);
                }
            });
        }
    }
});

方案2:直接监听触控原生事件

通过监听touchstart和touchend原生事件,判断是否为单点点击(移动距离小于阈值),再获取对应坐标数据:

const z = Array.from({length: 50}, () => Array.from({length: 50}, () => Math.floor(Math.random() * 255)));
const data = [{type: 'heatmap', z: z}];
const layout = {'yaxis': {'scaleanchor': 'x'}, 'dragmode': 'select'}
const config = { modeBarButtons: [["select2d"]] };
const plot = document.querySelector("#plot");
Plotly.newPlot(plot, data, layout, config);

// 处理plotly_click事件
plot.on("plotly_click", (e) => { 
    console.log("plotly_click", e); 
});

// 记录触控开始位置
let touchStartPos = null;
plot.addEventListener('touchstart', (e) => {
    if (e.touches.length === 1) {
        touchStartPos = {x: e.touches[0].clientX, y: e.touches[0].clientY};
    }
});

// 处理触控结束事件
plot.addEventListener('touchend', (e) => {
    if (touchStartPos && e.changedTouches.length === 1) {
        const endPos = {x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY};
        // 判断是否为单点点击(移动距离小于10px)
        const moveDistance = Math.sqrt(
            Math.pow(endPos.x - touchStartPos.x, 2) + 
            Math.pow(endPos.y - touchStartPos.y, 2)
        );
        
        if (moveDistance < 10) {
            const plotRect = plot.getBoundingClientRect();
            const relX = endPos.x - plotRect.left;
            const relY = endPos.y - plotRect.top;
            
            // 获取对应数据点
            Plotly.Fx.hover(plot, {xval: relX, yval: relY}, false).then((hoverData) => {
                if (hoverData.points?.length) {
                    console.log("触控单点点击坐标数据", hoverData.points[0]);
                    // 可选:模拟plotly_click事件
                    const clickEvent = new CustomEvent('plotly_click', {detail: hoverData});
                    plot.dispatchEvent(clickEvent);
                }
            });
        }
        touchStartPos = null;
    }
});

代码解释

  • 两种方案核心都是将触控屏幕坐标转换为图表内部相对坐标,再通过Plotly.Fx.hover方法获取对应的数据点
  • 方案1复用Plotly自身的plotly_selected事件触发时机,代码更简洁;方案2通过原生触控事件判断,兼容性更强
  • 可选模拟plotly_click事件,让原有针对该事件的逻辑无需修改即可适配触控设备

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 06:07:14