canvas类库开发基础

这个专栏目标实现一个自给自足的canvas项目,先造轮子,类似于fabric.js,然后用我们自己的轮子封装一个上层应用,类似于实时通信共享的画板excalidraw!! 下面整理了核心的几个canvas基本概念,如果要造canvas相关的轮子,掌握如下知识是必不可少的。

beginPath

我的这篇文章中"理解基于状态的绘图"部分有详解,总结来说就是stroke绘制的图形是基于与上一个beginPath之间的所有路径进行绘制的。

所以ctx.beginPath()调用的最佳实践应该是绘制完成(完成了画线或者填充)一部分之后,开始绘制下一部分之前都调用。

变换

  • ctx.scale:针对坐标轴的刻度间距进行缩放,默认情况下坐标轴方向上刻度间距等于一个canvas像素的大小,如果ctx.scale(2, 2)之后,刻度间距变为原来的两倍,原本处于画布中心的点移动到了画布的右下角。
  • ctx.translate:移动坐标轴,默认情况下canvas的坐标轴原点处于画布的左上角。ctx.translate(150, 150)的含义就是坐标轴移动至(150, 150),基于这个移动,我们画一个正方形并指定正方形的左上角为(0, 0)会发现,正方形的左上角位于(150, 150),因为fillRect等一系列绘制api的位置参数都是基于坐标轴的。
  • ctx.rotate:顺时针旋转坐标轴,默认情况下坐标原点向右为x,向下为y。ctx.rotate(Math.PI)执行之后,坐标原点向左为x方向,向上为y方向。

绘制上下文缓存

ctx.save()方法可以将当前的绘制上下文入栈存储,当调用ctx.restore()时栈顶的绘制上下文将出栈并生效。

绘制上下文包括当前ctx的所有设置,包括上述三种变换,以及fillStyle等属性的设置。

如下:

javascript 复制代码
let canvas = document.getElementById('canvas');
canvas.width = 300 * devicePixelRatio;
canvas.height = 300 * devicePixelRatio;
canvas.style.width = '300px';
canvas.style.height = '300px';
canvas.style.border = '1px solid red';
const ctx = canvas.getContext('2d');
​
ctx.save();
​
ctx.scale(devicePixelRatio, devicePixelRatio);
ctx.translate(150, 150);
ctx.rotate(Math.PI);
ctx.fillStyle = 'red';
​
ctx.restore();
ctx.fillRect(0, 0, 150, 150);

如上ctx创建之后就通过ctx.save保存了上下文(默认的绘制配置入栈),然后开启了一个新的上下文,我们基于这个新的上下文进行后续的操作,然后下面进行的三种变换,以及修改fillStyle都是在最新的上下文中进行的,但是在真正绘制矩形之前调用了ctx.restore(),导致默认上下文出栈取代当前的上下文,所以最后的矩形绘制出来没有任何特效。

创建高清canvas

demo

创建300px * 300px的高清canvas:

javascript 复制代码
let canvas = document.getElementById('canvas');
canvas.width = 300 * devicePixelRatio;
canvas.height = 300 * devicePixelRatio;
canvas.style.width = '300px';
canvas.style.height = '300px';
canvas.style.border = '1px solid red';
const ctx = canvas.getContext('2d');
ctx.scale(devicePixelRatio, devicePixelRatio);
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 300, 300);

解析

让一个物理像素对应一个canvas像素进行绘制,也就是位图高清的原理。

如上,我们 300px * 300px 的区域实际上有 (300 * devicePixelRatio) * (300 * devicePixelRatio)个物理像素来负责这一部分的渲染,所以我们申请canvas的像素(canvas.width / canvas.height)为 300 * devicePixelRatio个,也就是与物理像素一样多。然后通过canvas的样式属性将这些canvas像素限制在 300px * 300px 的区域内,至此,我们就做到了这个 300px * 300px 的区域内部1个物理像素渲染一个canvas像素。

但为了方便开发,我们想做到canvas绘制时相关api的位置信息与css像素一一对应,如上例中,我们希望canvas绘制时 (300, 300)这个点就在canvas画布的右下角,所以我们再通过ctx.scale(devicePixelRatio, devicePixelRatio);修改坐标轴的刻度长度,让刻度变为原来的devicePixelRatio倍。举个例子,devicePixelRatio 等于2时,我们在 300px * 300px的区域上申请了 600 * 600 的canvas像素,所以绘制时 (300, 300)的坐标点在画布的中心而非右下,我们通过ctx.scale(2, 2)将x轴与y轴的坐标长度(刻度间隔)扩大2倍,这样 (300, 300)这个坐标点就是画布的右下角了,这一步变换也就保证了canvas绘制时的坐标与css坐标统一。

相关推荐
Cachel wood1 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端3 分钟前
0基础学前端-----CSS DAY9
前端·css
joan_857 分钟前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
m0_7482361138 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_748248941 小时前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_748235611 小时前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者8 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart