Canvas之绘制图形后续

Canvas之绘制图形后续

Canvas 提供了一些线条 API ,它可以设置 ctx.lineCap 线条的端点,ctx.lineJoin 线条的折线连接处,ctx.miterLimit 控制线条折线的尖角长短。

js 复制代码
ctx.beginPath();
ctx.lineWidth = 10;
ctx.strokeStyle = 'red';
ctx.lineCap ="butt"; //平的
ctx.moveTo(50, 50)
ctx.lineTo(300, 50);
ctx.stroke();

ctx.beginPath();
ctx.lineCap="round" //突出圆
ctx.moveTo(50, 100)
ctx.lineTo(300, 100);
ctx.stroke();

ctx.beginPath();
ctx.lineCap="square" //突出平
ctx.moveTo(50, 150)
ctx.lineTo(300, 150);
ctx.stroke();
js 复制代码
ctx.beginPath();
ctx.lineWidth = 10;
ctx.strokeStyle = 'red';
ctx.lineJoin = "miter";//尖的
ctx.moveTo(50, 50);
ctx.lineTo(150, 150);
ctx.lineTo(250, 50);
ctx.stroke();

ctx.beginPath();
ctx.lineJoin = "round";//圆的
ctx.moveTo(50, 100);
ctx.lineTo(150, 200);
ctx.lineTo(250, 100);
ctx.stroke();

ctx.beginPath();
ctx.lineJoin = "bevel";//平的
ctx.moveTo(50, 150);
ctx.lineTo(150, 250);
ctx.lineTo(250, 150);
ctx.stroke();
js 复制代码
ctx.beginPath();
ctx.lineWidth = 20;
ctx.strokeStyle = 'red';
ctx.lineJoin = "miter";
ctx.miterLimit = 2;
ctx.moveTo(50, 50);
ctx.lineTo(70, 100);
ctx.lineTo(100, 50);
ctx.stroke();

ctx.beginPath();
ctx.lineWidth = 40;
ctx.miterLimit = 2;
ctx.moveTo(150, 50);
ctx.lineTo(170, 100);
ctx.lineTo(200, 50);
ctx.stroke();

ctx.miterLimit 会根据 ctx.lineWidth 来设置相应的比例进行长短变化。不同宽度设置同一个 ctx.miterLimit 长短不一致。

js 复制代码
ctx.beginPath();
ctx.moveTo(50, 100);
ctx.lineTo(200, 100);
ctx.setLineDash([200])
ctx.lineDashOffset = 200;
// ctx.lineDashOffset = -50;
ctx.lineWidth = 10;
ctx.strokeStyle = 'blue';
ctx.stroke();

function move() {
    ctx.lineDashOffset -= 1;
    console.log(ctx.lineDashOffset);
    ctx.stroke();
    if (ctx.lineDashOffset > -200) {
        requestAnimationFrame(move);
    }
}
requestAnimationFrame(move);

同样可以设置虚线,由于需要知道虚线实线长和空白长,需要一个数组,前面表示实线长,后面表示空白长。如果只设置一个数据,表示实线和空白长度相同。如果设置更多数据,表示重复循环设置。最好设置成偶数个数,这样就是一组。

同时,也可以设置偏移量。负数向右,正数向左偏移。

js 复制代码
ctx.beginPath();
ctx.lineWidth=10;
ctx.moveTo(0, 50);
ctx.lineTo(400, 50);
ctx.stroke();

ctx.clearRect(0, 0, canvas.width, canvas.height);

// ctx.beginPath();
ctx.lineWidth=10;
ctx.moveTo(50, 0);
ctx.lineTo(50, 400);
ctx.stroke();

在之前的虚线移动动画中,并不是真的移动,而是我们在不断进行重新描边,所以之前的线条依然被保留。只是因为线条在不断加长,所以没有看到问题。当让线条继续向右移动时,本该出现的空白就会被原来的线条挡住。

可以通过设置清除画布的区域来进行处理。如果设置的区域是整个画布,那么整个画布会被清除。注意,要使用 ctx.beginPath() ,如果不使用,清除画布只是设置了透明度,会保留原来的路径,在绘制时会全部画出。

js 复制代码
ctx.setLineDash([200])
ctx.lineDashOffset = 200;
ctx.lineWidth = 10;
ctx.strokeStyle = 'blue';

function move() {
    ctx.clearRect(50, 95, 200, 10);
    ctx.beginPath();
    ctx.moveTo(50, 100);
    ctx.lineTo(200, 100);
    ctx.lineDashOffset -= 1;
    ctx.stroke();
    if (ctx.lineDashOffset === -200) {
        ctx.lineDashOffset = 200;
    }
    requestAnimationFrame(move);
}
requestAnimationFrame(move);

我们可以实现简单的动画。在每一次动画前清除动画,设置 ctx.beginPath() ,然后不断递归,每次使得偏移量减一。当偏移量达到最低时,重新设置偏移量。

js 复制代码
ctx.beginPath();
ctx.lineWidth = 10;
ctx.moveTo(50, 50);
ctx.lineTo(50, 200);
ctx.lineTo(200, 200)
// ctx.lineTo(50, 50)
ctx.closePath();
ctx.stroke(); 
ctx.fill()

线条形成区域设置填充效果会将相关区域填充。但这时会出现最后端点闭合的样式问题,因为最后的端点并没有相连。所以想要实现自动闭合 ctx.closePath() 会解决这个问题。

而且不管是一条线段还是已经闭合的区域,自动闭合都会有效果,只是效果不太明显。

js 复制代码
ctx.beginPath();
ctx.arc(100,100,50,0,Math.PI * 2) ;
ctx.stroke();

ctx.beginPath();
ctx.arc(300,100,50,0,Math.PI,true);
ctx.stroke();

ctx.beginPath();
ctx.arc(100,300,50,Math.PI/2,Math.PI,true);
ctx.stroke();
js 复制代码
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(100,300);
ctx.lineTo(300,300);
ctx.stroke();

ctx.beginPath();
ctx.arc(200,200,100,0,Math.PI * 2);
ctx.stroke();

ctx.beginPath();
ctx.lineWidth = 4 ;
ctx.strokeStyle = '#00f';
ctx.moveTo(100,200);
ctx.arcTo(100,300,200,300,100);
ctx.stroke();

可以使用 ctx.arc( x , y , r , startAngle , endAngle , dir ),需要知道圆点,半径,起始点,结束点,绘制方向可选。起始点都是顺时针方向的,从x轴右侧作为起始点。false 默认为顺时针,true 为逆时针。

还可以使用 ctx.arcTo( x1 , y1 , x2 , y2 , r )实际由三个点相切,第一个点为 moveTo 的点或者是上一次绘制的结果,剩下的点为自己设置。会形成一个有夹角的线条,保证一段和线条相切的圆弧。

js 复制代码
ctx.beginPath();
ctx.ellipse(100, 100, 100, 50, 0, 0, Math.PI * 2);
ctx.stroke();

ctx.beginPath();
ctx.ellipse(300, 100, 100, 50, 0, 0, Math.PI / 2);
ctx.stroke();

ctx.beginPath();
ctx.ellipse(100, 300, 100, 50, Math.PI / 4, 0, Math.PI * 2);
ctx.stroke();

除了可以设置圆弧,还可以设置椭圆。ctx.ellipse( x , y , rx , ry , rotate , startAngle , endAngle , dir ) 需要增加 x 轴,y 轴,x 轴旋转角度,旋转角度默认为顺时针。其他和之前设置圆弧的方式相同。

js 复制代码
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.arc(100, 300, 5, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(200, 100, 5, 0, Math.PI * 2)
ctx.fill();
ctx.beginPath()
ctx.arc(300, 300, 5, 0, Math.PI * 2)
ctx.fill();

ctx.beginPath();
ctx.moveTo(100, 300);
ctx.quadraticCurveTo(200, 100, 300, 300)
ctx.stroke();
js 复制代码
ctx.beginPath();
ctx.moveTo(100, 300);
ctx.bezierCurveTo(200, 100, 250, 350, 300, 300)
ctx.stroke();

为了更好地设置曲线,可以采用贝塞尔曲线来进行绘制。除了起始点和结束点,还需要设置一些控制点。一个控制点叫做二次贝塞尔曲线,两个控制点叫做三次贝塞尔曲线。

设置一个参数 t ,数值为从0到1,不断找符合参数比例的点连线,直到找到最后一个点,然后这个点就是曲线的切点,这个点所在的直线叫做切线。这样随着参数 t 不断变化,就会得到一条最终的曲线。

前面是二次贝塞尔曲线,ctx.quadraticCurveTo( cx1 , cy1 , ex , ey),参数为控制点和结束点坐标,起始点坐标为前面给出的坐标。后面是三次贝塞尔曲线,bezierCurveTo(cx1,cy1 , cx2 , cy2 , ex , ey) 多了一个控制点坐标。

可以看到,贝塞尔曲线最多只能达到中间的位置,不会离控制点太过靠近。

js 复制代码
ctx.beginPath();
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = 'bold italic 50px sans-serif'
ctx.fillText('你好', 100, 100)

ctx.beginPath();
ctx.font = 'bold italic 50px sans-serif'
ctx.strokeText('你好', 100, 200)

ctx.beginPath();
ctx.fillStyle = '#f00'
ctx.arc(100, 100, 5, 0, Math.PI * 2)
ctx.fill();
ctx.beginPath();
ctx.fillStyle = '#f00'
ctx.arc(100, 200, 5, 0, Math.PI * 2)
ctx.fill();

同样字体也可以被绘制出来,使用填充或者描边的形式绘制。ctx.fillText(textStr , x , y , maxWidth)分别是绘制的文本,坐标和最大的宽度。如果超过了最大宽度,就会整体压缩到最大宽度。

还需要在绘制之前设置字体样式,记住一定要在最后设置字体,否则样式无法显示。

使用 ctx.textAlign 和 ctx.textBaseline 分别设置水平位置和垂直位置。center 表示水平居中,middle 表示垂直居中。

相关推荐
卸任2 小时前
Vibe coding工具使用心得
前端·ai编程
brien93 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance3 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M3 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写3 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李3 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易4 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding4 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马4 小时前
前沿视界
前端
kyriewen4 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员