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
相关产品推荐
相关产品推荐

