JS两点画线_js两点之间画线

JS两点画线_js两点之间画线JavaScript 画两点之间的连线 小白也能轻松上手 用 JavaScript 画两点之间的连线 小白也能轻松上手 先别急着写代码 搞清楚你要用啥武器 上手就是干 Canvas 画线保姆级教程 基础版本 先让线冒出来 进阶版本 让线好看一点 高手版本 画虚线 箭头 渐变色线 SVG 也不是吃素的 线呢 我那么大一根线怎么不见了 排查清单第一条 canvas 有没有大小 排查清单第二条 坐标对不对

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



JavaScript画两点之间的连线,小白也能轻松上手

  • 用JavaScript画两点之间的连线,小白也能轻松上手
  • 先别急着写代码,搞清楚你要用啥武器
  • 上手就是干:Canvas画线保姆级教程
  • 基础版本:先让线冒出来
  • 进阶版本:让线好看一点
  • 高手版本:画虚线、箭头、渐变色线
  • SVG也不是吃素的
  • 线呢?我那么大一根线怎么不见了?
  • 排查清单第一条:canvas有没有大小?
  • 排查清单第二条:坐标对不对?
  • 排查清单第三条:stroke了吗?颜色对了吗?
  • 排查清单第四条:被其他元素挡住了?
  • 排查清单第五条:DPI问题(Retina屏幕糊)
  • 一些让你显得很像大佬的骚操作
  • 封装一个万能画线函数
  • 画曲线?贝塞尔曲线安排上
  • 交互式画线:跟着鼠标走
  • 性能优化:别傻乎乎地重绘所有东西
  • 整点花活,别只会画直线
  • 画个心电图动画
  • 画个连线图(像那些炫酷的关系网)
  • 画个测量尺(带刻度的线)
  • 最后给个完整版:画可拖拽的点连线

用JavaScript画两点之间的连线,小白也能轻松上手

嘿,兄弟姐妹们!今天咱们聊个看似特基础但坑贼多的话题——在网页上画两根点之间的连线。你别笑,这玩意儿看着简单,真写起来你会发现,明明代码都敲对了,屏幕上就是啥也没有,那一刻的绝望感,堪比早起发现手机没电还要赶地铁。

我当年第一次搞这个的时候,满脑子想的都是”不就两个点连起来吗,小学数学就教过两点确定一条直线,这能有啥难度?”结果呢,Canvas坐标系给我上了一课,SVG的viewBox又给我上了一课,最后发现线画出来了但是糊成一坨,原来是没开抗锯齿…说多了都是泪。所以今天这篇文章,我就把这两年踩过的坑、攒下的经验,一股脑儿全倒给你,保证你看完不仅能画出线,还能画出花来。

刚开始接触前端绘图的时候,我也懵啊,一搜JavaScript画图,冒出来Canvas和SVG两个东西,当时我就寻思,这俩啥区别?选错了会不会被同事笑话?后来搞明白才发现,这俩根本就不是竞争关系,而是各有各的舒适区。

Canvas是啥呢?你可以把它理解成一块真实的画布,你拿画笔(也就是JavaScript)在上面涂涂画画,画完了就是一张图。它更适合那种需要频繁重绘、像素级操作的场景,比如你要做个画图板、搞个粒子特效、或者做个数据可视化图表需要疯狂刷新数据的,Canvas就是你的菜。它的工作原理是位图,放大缩小可能会糊,但性能贼好,特别是在处理大量图形的时候。

SVG呢,全称Scalable Vector Graphics,可缩放矢量图形。这玩意儿是用XML描述的,你画一个圆、一条线,其实都是DOM节点。它的好处是矢量,无限放大都不带糊的,而且因为是DOM,你可以直接给每个图形加CSS样式、绑定事件,操作起来更符合前端er的直觉。如果你要画个流程图、组织结构图,或者需要让用户点击某条线弹出个提示框啥的,SVG明显更香。

所以啊,选工具这事儿,没有绝对的好坏,只有合不合适。就像你去买菜,总不能扛着显微镜去吧?接下来我会把这两种方式都给你整明白,到时候你根据项目需求自己挑。

好了,废话不多说,咱们直接开整Canvas。这是最常用的方案,也是坑最多的地方,我尽量说得细一点,代码也给全一点,你复制粘贴改改就能跑。

基础版本:先让线冒出来

首先,你得在HTML里整一个canvas元素,这玩意儿默认是透明的,你如果不给它设宽高,它就像个隐形人一样占着地方但你看不见。很多同学(包括当年的我)就栽在这儿,后面代码写对了但是死活看不见线,就是因为canvas没给尺寸。

 

 
  
    
     
      
      Canvas画线入门 
       
     
    

看好了,我要画线了

看到没,就这么几行。但是这里有几个巨坑我得给你划重点:

  1. getContext(‘2d’) 里的’2d’必须是小写,我当初写成’2D’,调试了半天发现返回null,差点把键盘砸了。
  2. 坐标系问题:Canvas的坐标原点在左上角,x往右是正,y往下是正。这跟咱们高中学的数学坐标系(y向上)是反的!所以如果你要画个从(0,0)到(100,100)的线,它是向右下方倾斜的,不是向右上方,这个坑我踩了不下三次。
  3. 路径问题:beginPath、moveTo、lineTo只是”规划”路径,最后必须调用stroke()才会真正画出来。如果你忘了stroke,或者stroke写在了beginPath前面,那恭喜你,啥也看不见。

进阶版本:让线好看一点

基础的线画出来了,但是黑乎乎一根细线,看着有点寒碜。咱们给它整点样式,让它支棱起来。

// 咱们在上面的基础上加点料 ctx.beginPath(); ctx.moveTo(100, 100); ctx.lineTo(500, 300);

// 设置线条颜色,可以是颜色名、十六进制、rgb、rgba都行 ctx.strokeStyle = ‘#FF6B6B’; // 这种珊瑚红挺好看的

// 设置线条宽度,单位是像素,默认是1,太细了有时候看不清 ctx.lineWidth = 4;

// 设置线帽样式,可以是butt(默认)、round(圆头)、square(方头) // butt是无线帽,round是圆角线帽,square是方形线帽 // 用round看起来会圆润一点,不会那么生硬 ctx.lineCap = ‘round’;

// 设置线条连接处的样式(虽然咱们现在只是单条线,但如果是折线就有用了) // miter(尖角)、round(圆角)、bevel(斜角) ctx.lineJoin = ‘round’;

// 最后画出来 ctx.stroke();

// 还可以加个阴影,显得有立体感 ctx.shadowColor = ‘rgba(0, 0, 0, 0.2)’; // 阴影颜色,带点透明度 ctx.shadowBlur = 10; // 阴影模糊程度 ctx.shadowOffsetX = 2; // 阴影水平偏移 ctx.shadowOffsetY = 2; // 阴影垂直偏移

// 注意:阴影要在stroke之前设置才有效,而且会影响后续所有绘制 // 所以如果只想让这条线有阴影,画完记得重置shadow属性

高手版本:画虚线、箭头、渐变色线

有时候业务需求比较变态,要你画个虚线,或者带箭头的线,甚至是渐变色的线。这些看起来花里胡哨,其实Canvas API都给你准备好了。

// 画虚线 ctx.beginPath(); ctx.moveTo(100, 150); ctx.lineTo(500, 150);

// setLineDash接收一个数组,表示虚线的模式 // [10, 5]表示画10像素实线,空5像素,再画10像素实线… // 你可以写更复杂的比如[10, 5, 2, 5]表示长实线、空隙、短实线、空隙 ctx.setLineDash([10, 5]); ctx.strokeStyle = ‘#4ECDC4’; ctx.lineWidth = 3; ctx.stroke();

// 记得用完虚线要重置回实线,不然后面画的线都是虚的了 ctx.setLineDash([]);

// 画渐变色线 // 创建线性渐变,参数是起点的x,y和终点的x,y // 这里咱们画一条从左到右颜色渐变的线 const gradient = ctx.createLinearGradient(100, 200, 500, 200); gradient.addColorStop(0, ‘#FF6B6B’); // 起点颜色 gradient.addColorStop(0.5, ‘#FFE66D’); // 中间颜色 gradient.addColorStop(1, ‘#4ECDC4’); // 终点颜色

ctx.beginPath(); ctx.moveTo(100, 200); ctx.lineTo(500, 200); ctx.strokeStyle = gradient; // 把渐变对象赋给strokeStyle ctx.lineWidth = 6; ctx.stroke();

// 画带箭头的线(这个稍微复杂点,需要计算角度) function drawArrow(ctx, fromX, fromY, toX, toY, color = ‘#333’) {

const headlen = 10; // 箭头长度 const angle = Math.atan2(toY - fromY, toX - fromX); // 计算线段角度 ctx.beginPath(); ctx.moveTo(fromX, fromY); ctx.lineTo(toX, toY); ctx.strokeStyle = color; ctx.lineWidth = 2; ctx.stroke(); // 画箭头头部 ctx.beginPath(); ctx.moveTo(toX, toY); // 计算箭头两边的点,需要用到三角函数 // angle是线段角度,Math.PI/6是30度,画两个30度偏转的短线 ctx.lineTo(toX - headlen * Math.cos(angle - Math.PI / 6), toY - headlen * Math.sin(angle - Math.PI / 6)); ctx.moveTo(toX, toY); ctx.lineTo(toX - headlen * Math.cos(angle + Math.PI / 6), toY - headlen * Math.sin(angle + Math.PI / 6)); ctx.stroke(); 

}

// 使用咱们的箭头函数 drawArrow(ctx, 100, 250, 500, 250, ‘#9B59B6’);

// 如果你想画个双向箭头,那就调用两次,反向画 drawArrow(ctx, 500, 280, 100, 280, ‘#E74C3C’);

看到没,Canvas虽然API看起来有点古老(确实也是好多年前的技术了),但功能还是很全的。不过我得吐槽一下,画箭头这个居然没有原生API支持,还得自己算三角函数,属实有点离谱。但没办法,谁让它性能好呢,忍了。

Canvas说完了,咱们再来看看SVG怎么搞。SVG画线其实更符合直觉,因为它是声明式的,你直接在HTML里写标签就行,不用像Canvas那样先拿上下文再调API。

 

 
  
    
     
     SVG画线教程 
      
    

SVG画线就是这么简单粗暴

看到没,SVG画线多优雅!特别是那个箭头,用marker定义一次,所有线都能复用,而且不用算三角函数,SVG引擎自动帮你算好了。Canvas看了都流泪。

而且SVG还有个巨大优势:它是响应式的。你在CSS里改个width height,里面的图形会自动缩放适应,不会像Canvas那样要么变形要么得重绘。所以如果你在做那种需要缩放的图表,或者要支持手机端各种屏幕尺寸的,SVG真的香。

但是啊,SVG也不是万能的。如果你要画几千条线,还要每帧都动画,SVG的性能就不太行了,因为它每个图形都是DOM节点,多了会卡。所以总结一下:

  • 选Canvas:需要频繁重绘、图形数量巨大、需要像素级操作(比如图像处理)、要做复杂动画。
  • 选SVG:图形数量不多、需要交互事件(点击悬停)、需要响应式缩放、需要CSS样式控制。

好了,现在你已经会画线了,但是现实往往是残酷的,你信心满满地写好了代码,刷新页面——啥也没有!一片空白!这时候别慌,咱们来系统性地排查一下。

排查清单第一条:canvas有没有大小?

这是新人最常犯的错误。如果你没在canvas标签上写width height,或者只在CSS里写了width height,那问题大了。

// 错误的写法(只在CSS里设大小) // 
   
     
      // 这样canvas的默认大小是300x150,然后被CSS拉伸成600x400,里面的线会变形且模糊

// 正确的写法 //

// 或者用JavaScript动态设置 const canvas = document.getElementById(‘c’); canvas.width = 600; // 注意这里不是字符串’600px’,就是数字600 canvas.height = 400;

记住,canvas有两个尺寸:画布尺寸(width/height属性)和显示尺寸(CSS尺寸)。如果画布尺寸是300x150,显示尺寸是600x400,浏览器会把300x150的画布拉伸到600x400显示,结果就是模糊变形。所以最好保持一致,或者干脆只在属性里设大小。

排查清单第二条:坐标对不对?

很多同学(比如我)刚开始会犯这样的错:

// 错误示范:以为canvas中心是(0,0) ctx.moveTo(0, 0); ctx.lineTo(100, 100);

// 结果发现只看见右下角一点点线,或者根本看不见 // 因为canvas原点(0,0)在左上角,而且如果线宽是1,坐标落在像素边界上会很模糊

正确的做法是,如果你要画清晰的1像素线,坐标要加0.5:

// 画清晰的1像素水平线 ctx.beginPath(); ctx.moveTo(100, 100.5); // y坐标加0.5,让线正好落在一个像素的中间 ctx.lineTo(500, 100.5); ctx.stroke();

这是因为Canvas的坐标系统,整数坐标是像素边界,不是像素中心。如果你从(100,100)画到(500,100),实际上这条线占据了第99和第100个像素行之间,浏览器为了抗锯齿会把线画成半透明的2像素宽,看着就很淡很模糊。加0.5让它正好落在像素中心,就清晰了。

排查清单第三条:stroke了吗?颜色对了吗?

// 错误示范:只fill不stroke ctx.beginPath(); ctx.moveTo(100, 100); ctx.lineTo(500, 300); ctx.fillStyle = ‘red’; ctx.fill(); // fill对开放路径(没闭合的线)是没用的!

// 或者 ctx.strokeStyle = ‘white’; // 白线画在白背景上… ctx.stroke();

// 或者 ctx.strokeStyle = ‘red’; ctx.beginPath(); // …画线… // 忘了调用ctx.stroke(),或者stroke写在了beginPath前面,被清空了

所以看不见线的时候,先检查:

  1. 调用了stroke()吗?
  2. strokeStyle设置的颜色和背景**分度大吗?
  3. lineWidth是不是设成0了(虽然默认是1,但万一呢)?

排查清单第四条:被其他元素挡住了?

如果你是在一个复杂的页面上画线,可能canvas被其他div挡住了。检查一下z-index:

canvas {

position: relative; /* 或者absolute/fixed,根据需要 */ z-index: 10; /* 确保够高 */ border: 1px solid red; /* 加个红边框看看canvas到底在哪儿 */ 

}

加个红边框是调试绝技,能立刻看出来canvas的位置和大小对不对。

排查清单第五条:DPI问题(Retina屏幕糊)

如果你在高分辨率屏幕(比如MacBook的Retina屏)上看自己画的线,发现有点模糊,那是因为物理像素比CSS像素多。需要适配:

function setupHiDPICanvas(canvas)

// 使用 const canvas = document.getElementById(‘myCanvas’); const ctx = setupHiDPICanvas(canvas);

// 现在用(100,100)到(500,300)画线,在Retina屏上也是清晰的

这个技巧很重要,特别是在移动端,devicePixelRatio可能是2、3甚至更高,不适配的话你的线会糊成一坨。

基础功能都会了?来,整点进阶的,让你在代码评审的时候能云淡风轻地说”哦这个啊,我加个贝塞尔曲线优化一**验”。

封装一个万能画线函数

别每次都写beginPath moveTo lineTo stroke,封装起来:

/

  • 万能画线函数,支持各种花里胡哨的配置
  • @param {CanvasRenderingContext2D} ctx - canvas上下文
  • @param {number} x1 - 起点x
  • @param {number} y1 - 起点y
  • @param {number} x2 - 终点x
  • @param {number} y2 - 终点y
  • @param {Object} options - 配置选项 */ function drawLine(ctx, x1, y1, x2, y2, options = {}) } = options;

    ctx.save(); // 保存当前状态,避免污染其他绘制

    ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2);

    ctx.strokeStyle = color; ctx.lineWidth = width; ctx.lineCap = cap; ctx.setLineDash(dash); ctx.globalAlpha = alpha;

    if (shadow)

    ctx.stroke(); ctx.restore(); // 恢复到之前保存的状态 }

// 使用示例,清爽多了 drawLine(ctx, 100, 100, 500, 300, {

color: '#FF6B6B', width: 4, dash: [10, 5], shadow: { color: 'rgba(255,0,0,0.3)', blur: 10 } 

});

看到没,用了save和restore,这样函数内部的样式设置不会影响到外面的其他绘制,这是专业写法。

画曲线?贝塞尔曲线安排上

两点之间不一定非得直线,有时候你需要曲线,比如流程图里的连接线:

// 二次贝塞尔曲线(一个控制点) ctx.beginPath(); ctx.moveTo(100, 300); // 起点 // quadraticCurveTo(cx, cy, x, y) // cx,cy是控制点,x,y是终点 // 控制点决定了曲线的弯曲程度和方向 ctx.quadraticCurveTo(300, 100, 500, 300);
ctx.strokeStyle = ‘#3498DB’; ctx.lineWidth = 3; ctx.stroke();



// 三次贝塞尔曲线(两个控制点,更灵活) ctx.beginPath(); ctx.moveTo(100, 350); // bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) // 两个控制点,可以画出S形曲线 ctx.bezierCurveTo(200, 200, 400, 400, 500, 350); ctx.strokeStyle = ‘#E74C3C’; ctx.stroke();

贝塞尔曲线这玩意儿看着吓人,其实你就记住:控制点像磁铁一样吸引着线往那个方向弯曲。多试几次就能找到感觉。

交互式画线:跟着鼠标走

如果你的需求是让用户自己画线,比如做个画板,那就得监听鼠标事件:

const canvas = document.getElementById(‘canvas’); const ctx = canvas.getContext(‘2d’);

let isDrawing = false; let startX, startY;

canvas.addEventListener(‘mousedown’, (e) => );

canvas.addEventListener(‘mousemove’, (e) => ); });

canvas.addEventListener(‘mouseup’, () => {

isDrawing = false; 

});

// 触摸设备支持(手机平板) canvas.addEventListener(‘touchstart’, (e) => );

canvas.addEventListener(‘touchmove’, (e) => );

这里的关键是getBoundingClientRect,它能帮你把鼠标在屏幕上的坐标转换成在canvas里的坐标。如果不做这个转换,当页面有滚动或者canvas有偏移时,你画线会偏移,那种体验超级诡异,像是有个看不见的力场在扭曲你的鼠标。

性能优化:别傻乎乎地重绘所有东西

如果你在做类似流程图编辑器的东西,有很多线,这时候如果每次只改一条线就全部清屏重绘,性能会很差。优化策略:

// 差劲的做法:每次mousemove都clearRect然后重绘所有元素

// 好一点的做法:分层渲染 // 创建两个canvas叠在一起 // 下面的canvas画静态的、不常变的内容(背景网格、大部分线) // 上面的canvas画动态的、临时的内容(正在拖动的线、选中高亮)

const bgCanvas = document.getElementById(‘bg-layer’); const fgCanvas = document.getElementById(‘fg-layer’);

// 静态内容画在bgCanvas上,只画一次或很少更新 function drawStatic()

// 动态内容画在fgCanvas上,频繁更新 function drawDynamic(currentLine)

// 如果元素数量巨大(几千个),考虑用WebGL或者库(如Fabric.js、PixiJS)

这个分层思想很重要,就像PS里的图层一样。我之前做过一个在线白板工具,一开始所有东西画一个canvas里,拖到第20个元素时就卡成PPT了,分层之后画100个元素都不带喘气的。

基本功练扎实了,咱们来搞点创意。你学会画线可不是为了画个”Hello World”就完事的,得整点能让产品经理眼前一亮的活儿。

画个心电图动画

let offset = 0;

function drawHeartbeat() else if (pos > 200 && pos < 220) {

 y -= 30; // Q波 } else if (pos >= 220 && pos < 240) { y += 50; // R波(高耸) } else if (pos >= 240 && pos < 260) { y -= 20; // S波 } else if (pos > 350 && pos < 400) { y += Math.sin((pos - 350) * 0.1) * 15; // T波 } ctx.lineTo(x, y); } ctx.strokeStyle = '#00FF00'; // 经典的心电图绿色 ctx.lineWidth = 2; ctx.shadowColor = '#00FF00'; ctx.shadowBlur = 10; // 发光效果 ctx.stroke(); offset += 2; // 让波形移动 requestAnimationFrame(drawHeartbeat); 

}

drawHeartbeat();

这个用在医疗相关的项目或者科技感的loading动画里,逼格直接拉满。

画个连线图(像那些炫酷的关系网)

const nodes = [

{ x: 300, y: 100, label: '我' }, { x: 150, y: 250, label: '死党A' }, { x: 450, y: 250, label: '死党B' }, { x: 300, y: 350, label: '饭搭子' } 

];

// 画节点之间的连线 ctx.strokeStyle = ‘rgba(100, 100, 100, 0.3)’; ctx.lineWidth = 1;

// 画所有连接线 for (let i = 0; i < nodes.length; i++) {

for (let j = i + 1; j < nodes.length; j++) { ctx.beginPath(); ctx.moveTo(nodes[i].x, nodes[i].y); ctx.lineTo(nodes[j].x, nodes[j].y); ctx.stroke(); } 

}

// 画节点 nodes.forEach(node => {

// 画圆 ctx.beginPath(); ctx.arc(node.x, node.y, 20, 0, Math.PI * 2); ctx.fillStyle = '#3498DB'; ctx.fill(); // 画文字 ctx.fillStyle = 'white'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.font = '14px Arial'; ctx.fillText(node.label, node.x, node.y); 

});

这种效果配上力导向算法(节点之间有力的作用,可以拖动)就是那些炫酷的关系图谱了。不过力导向算法代码比较长,这里就不展开了,你可以搜搜D3.js或者Cytoscape.js这类库。

画个测量尺(带刻度的线)

function drawRuler(x1, y1, x2, y2) {

// 画主线 drawLine(ctx, x1, y1, x2, y2, { color: '#333', width: 2 }); // 计算角度和长度 const dx = x2 - x1; const dy = y2 - y1; const angle = Math.atan2(dy, dx); const length = Math.sqrt(dx * dx + dy * dy); // 画刻度 const tickInterval = 10; // 每10像素一个刻度 const tickLength = 8; for (let i = 0; i <= length; i += tickInterval) } 

}

drawRuler(100, 200, 500, 200);

这种可以用在图片标注工具、CAD简易版或者各种测量工具里。

最后给个完整版:画可拖拽的点连线

这个是我实际项目里用过的代码,可以直接拿去用:

 

 
  
    
     
      
      可拖拽连线演示 
       
     
    

 
  
    
    
拖拽圆点改变连线,双击空白处添加新点

这个例子综合了上面说的所有技巧:坐标转换、事件处理、动态绘制、甚至还有个双击删除的功能。你把这段代码保存成html文件打开,就能拖着绿点跑,看连线跟着动,双击空白处加新点,双击点删除,是不是很骚?

行了,关于JavaScript画两点连线这事儿,我基本上把家底都掏给你了。从最简单的Canvas画线,到SVG方案,再到各种踩坑排查,最后还有进阶技巧和创意应用,代码也给了不少,你拿着这些应该能应付大部分需求了。

记住啊,编程这玩意儿,光看不动手等于白看。建议你现在就打开VS Code,把我上面的代码敲一遍,改改参数看看效果,踩几个坑印象更深。特别是那个坐标系和devicePixelRatio的问题,不亲身经历一次很难记牢。

去吧,去画出属于你的那几根线,说不定下一个网红可视化工具就是你整出来的!

小讯
上一篇 2026-04-19 18:29
下一篇 2026-04-19 18:27

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/271718.html