当前位置: 首页 > news >正文

Canvas 绘制曲线并实现鼠标点击高亮效果

使用 Canvas 绘制的曲线也可以实现鼠标点击高亮显示效果。由于 Canvas 是基于像素的绘制方式(不像 SVG 是基于矢量的),我们需要手动检测鼠标点击位置是否在曲线上,并重新绘制高亮效果。

实现方案

基本思路

  1. 存储所有曲线的路径数据
  2. 监听鼠标点击事件
  3. 检测点击位置是否在某条曲线上(使用点是否在路径上的检测算法)
  4. 重新绘制所有曲线,将选中的曲线高亮显示

完整代码示例

<!DOCTYPEhtml><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Canvas曲线点击高亮示例</title><style>body{font-family:Arial,sans-serif;display:flex;flex-direction:column;align-items:center;padding:20px;}h1{color:#333;}.container{margin-top:20px;border:1px solid #ddd;padding:10px;border-radius:5px;background-color:#f9f9f9;}canvas{border:1px solid #ccc;background-color:white;}.info{margin-top:15px;font-style:italic;color:#666;}</style></head><body><h1>Canvas曲线点击高亮示例</h1><p>点击任意一条曲线可以高亮显示它</p><divclass="container"><canvasid="curveCanvas"width="600"height="400"></canvas></div><divclass="info"id="curveInfo">点击一条曲线查看详细信息</div><script>document.addEventListener('DOMContentLoaded',function(){constcanvas=document.getElementById('curveCanvas');constctx=canvas.getContext('2d');constcurveInfo=document.getElementById('curveInfo');// 存储所有曲线的信息constcurves=[{name:"曲线1: 蓝色正弦波",color:"#3498db",highlightColor:"#ff9900",path:createSineWavePath(ctx,50,200,500,150,2)},{name:"曲线2: 绿色方波",color:"#2ecc71",highlightColor:"#27ae60",path:createSquareWavePath(ctx,50,100,500,200)},{name:"曲线3: 红色三角波",color:"#e74c3c",highlightColor:"#c0392b",path:createTriangleWavePath(ctx,50,300,500,200)}];letselectedCurveIndex=-1;// 当前选中的曲线索引// 绘制所有曲线functiondrawCurves(){// 清空画布ctx.clearRect(0,0,canvas.width,canvas.height);// 绘制背景网格drawGrid();// 绘制所有曲线curves.forEach((curve,index)=>{if(index===selectedCurveIndex){// 高亮显示选中的曲线ctx.strokeStyle=curve.highlightColor;ctx.lineWidth=4;}else{// 普通显示其他曲线ctx.strokeStyle=curve.color;ctx.lineWidth=2;}ctx.beginPath();ctx.moveTo(curve.path[0].x,curve.path[0].y);for(leti=1;i<curve.path.length;i++){ctx.lineTo(curve.path[i].x,curve.path[i].y);}ctx.stroke();});}// 绘制背景网格functiondrawGrid(){ctx.strokeStyle="#eee";ctx.lineWidth=1;// 垂直线for(letx=50;x<=550;x+=100){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,canvas.height);ctx.stroke();}// 水平线for(lety=50;y<=350;y+=100){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(canvas.width,y);ctx.stroke();}}// 创建正弦波路径functioncreateSineWavePath(ctx,startX,startY,width,height,cycles){constpath=[];constpoints=100;for(leti=0;i<=points;i++){constx=startX+(width/points)*i;consty=startY+Math.sin((i/points)*cycles*Math.PI*2)*height/2;path.push({x,y});}returnpath;}// 创建方波路径functioncreateSquareWavePath(ctx,startX,startY,width,height){constpath=[];constsegments=10;constsegmentWidth=width/segments;for(leti=0;i<=segments;i++){constx=startX+i*segmentWidth;lety;if(i%2===0){y=startY-height/2;}else{y=startY+height/2;}path.push({x,y});}returnpath;}// 创建三角波路径functioncreateTriangleWavePath(ctx,startX,startY,width,height){constpath=[];constsegments=10;constsegmentWidth=width/segments;for(leti=0;i<=segments;i++){constx=startX+i*segmentWidth;constprogress=i/segments;lety;if(progress<0.5){y=startY+(progress*2-0.5)*height;}else{y=startY+(1.5-progress*2)*height;}path.push({x,y});}returnpath;}// 检测点是否在路径上(简化版,适用于连续曲线)functionisPointOnPath(point,path,tolerance=5){for(leti=0;i<path.length-1;i++){constp1=path[i];constp2=path[i+1];// 计算点到线段的距离constdistance=pointToSegmentDistance(point,p1,p2);if(distance<=tolerance){returntrue;}}returnfalse;}// 计算点到线段的距离functionpointToSegmentDistance(point,p1,p2){constA=point.x-p1.x;constB=point.y-p1.y;constC=p2.x-p1.x;constD=p2.y-p1.y;constdot=A*C+B*D;constlen_sq=C*C+D*D;letparam=-1;if(len_sq!==0){param=dot/len_sq;}letxx,yy;if(param<0){xx=p1.x;yy=p1.y;}elseif(param>1){xx=p2.x;yy=p2.y;}else{xx=p1.x+param*C;yy=p1.y+param*D;}constdx=point.x-xx;constdy=point.y-yy;returnMath.sqrt(dx*dx+dy*dy);}// 初始化绘制drawCurves();// 鼠标点击事件处理canvas.addEventListener('click',function(e){constrect=canvas.getBoundingClientRect();constmouseX=e.clientX-rect.left;constmouseY=e.clientY-rect.top;letfound=false;// 检查是否点击了某条曲线for(leti=0;i<curves.length;i++){constpoint={x:mouseX,y:mouseY};if(isPointOnPath(point,curves[i].path)){selectedCurveIndex=i;curveInfo.textContent=`已选择:${curves[i].name}`;found=true;break;}}// 如果没有点击任何曲线,取消选择if(!found){selectedCurveIndex=-1;curveInfo.textContent="点击一条曲线查看详细信息";}// 重新绘制drawCurves();});});</script></body></html>

代码说明

  1. 曲线存储:

    • 使用数组存储所有曲线信息,包括名称、颜色、高亮颜色和路径点
    • 每种曲线类型有对应的路径生成函数
  2. 绘制函数:

    • drawCurves()负责绘制所有曲线,根据选中状态应用不同样式
    • drawGrid()绘制背景网格作为参考
  3. 路径检测:

    • isPointOnPath()检测点是否在路径上(简化版,适用于连续曲线)
    • pointToSegmentDistance()计算点到线段的最短距离
  4. 交互处理:

    • 监听 canvas 的点击事件
    • 检测点击位置是否在某条曲线上
    • 更新选中状态并重新绘制

优化建议

  1. 性能优化:

    • 对于复杂曲线,可以减少路径点数量或使用更高效的检测算法
    • 可以使用requestAnimationFrame优化重绘
  2. 功能扩展:

    • 添加鼠标悬停效果
    • 支持多选曲线
    • 添加图例说明
    • 支持曲线动态修改
  3. 精确检测:

    • 对于更精确的检测,可以考虑使用 Canvas 的isPointInPath()方法(但需要为每条曲线重新创建路径对象)
    • 或者使用第三方库如paper.jsfabric.js提供更高级的交互功能

这个实现展示了 Canvas 实现曲线点击高亮的基本方法,你可以根据实际需求进一步优化和完善。

http://www.jsqmd.com/news/765002/

相关文章:

  • Windows 11安卓子系统WSA:3步免费安装,大屏畅玩手机应用
  • 【DeerFlow 2.0】代码详解(二):Lead Agent 与 Prompt 工程
  • 「权威评测」2026年国内品酒培训厂家实力推荐,谁才是靠谱之选? - 深度智识库
  • SLAM3R (1)运行 - MKT
  • OpenClaw从入门到应用——工具(Tools)
  • 任天堂Switch屏幕色彩优化完整指南:快速提升游戏视觉体验
  • 2026年江西菜连锁品牌排名TOP3怎么选?多维度深度解析江西菜连锁品牌 - 速递信息
  • 简单高效的视频下载神器:yt-dlp-gui 完整使用指南
  • 亨得利维修保养的30个魔鬼细节曝光:从百达翡丽到浪琴,专业与业余的差距只在毫厘之间(附全国七店地址+400-901-0695) - 时光修表匠
  • 保姆级教程:用rsync和dd命令备份你的RK3588 Ubuntu系统(附完整命令清单)
  • HiClaw 上线 Worker 模板市场,提供稳定可共享的 Agent 生产力
  • 别再只用Log了!用Android Studio Layout Inspector实时调试UI的3个高级技巧
  • 中小型创业团队如何利用Taotoken统一管理多个AI模型的接入
  • 借助 Taotoken 统一接口快速迁移原有基于 OpenAI 的应用
  • 保姆级教程:用GEE和Landsat 8数据,5分钟搞定城市热岛区域自动识别与面积计算
  • 通过用量看板观测 API 调用成本与 Token 消耗明细
  • 用claude-hud提升开发效率:快马平台定制智能编码工作流
  • 抖音下载器完整指南:如何免费批量下载无水印抖音视频
  • 2026年企业级安全合规OpenClaw平替厂商,国产替代优选 - 品牌2026
  • 企业展示型小程序,找制作公司还是自己搭?3个判断标准 - 维双云小凡
  • 告别混乱!用Cadence Capture高效管理你的原理图器件库(附自定义库创建教程)
  • 2026年重庆环保装配式墙板全攻略:从甲醛危机到即装即住的绿色家装革命 - 优质企业观察收录
  • 程序员转行AI大模型:高薪风口!行业前景、薪资待遇、学习路线全解析!
  • 【SCI复现】三电平NPC变流器中点电位平衡下零序电压的分析与计算研究(Simulink仿真实现)
  • 广州金烨再生资源回收:盐田废铜回收厂家 - LYL仔仔
  • 从CDD文件到ISO 15765-2:深入CANoe诊断控制台,看多帧传输如何被‘隐藏’
  • 程序员如何接受工作内容毫无意义?
  • 从原酒之乡到人才摇篮:2026年品酒师培训标杆之选——川池华沃酿酒研究院深度解读 - 深度智识库
  • Windows更新故障终极解决方案:Reset Windows Update Tool完整使用指南
  • 互联网大厂Java求职面试:核心技术与业务场景探讨