题目描述
一个 100 * 100 Canvas 占用内存多大,它的大小的决定因素是什么?这里我们只考虑存储这么多像素的内存,不考虑运算过程中使用的内存。
感性认识
我们知道 Canvas 给了我们前端像素级别控制的能力,我们可以精确到像素,因此多了 更多的灵活性和复杂度。可以说 Canvas 的模型和传统的 DOM 模型有很大的不同。
那么要回答这个问题,我们其实只要知道 1 * 1 像素占用多大内存就好了。
那么 1 * 1 像素的 Canvas 占用多大像素呢?我们来感性思考一下,我们现在前端在写颜色的时候, 很多都是用RGBA或者#(六位十六进制)的形式
, 对于 RGBA 我们可以怎么存储?A 的话,我们平时的取值范围 0 - 1 的小数,步长为 0.01,因此 100 个数字就够了,7Bit 就可以搞定。
那么 RGB 呢?如果你用过 RGB 或者 RGBA 的话,应该知道我们通常使用的范围是 0-255 的范围, 因此 RGB 的存储空间应该是 8*3 = 24 Bit,也就是 3Byte。
如果使用十六进制表示呢?一个十六进制需要 4 个 bit,那么 6 个十六进制就是 24 个 bit,也就是 3Byte,和 RGBA 是一样的。
因此一个像素的 Canvas 占据空间理论上是 31Bit。那么事实上是这样么?
什么是 ImageData
回答这个问题之前,我先来介绍一个 Canvas 的 API getImageData
, 这个 方法会返回一个 ImageData,它用来描述"canvas"元素的一个隐含像素数据的区域。使用 ImageData() 构造函数创建或者使用和 canvas 在一起的 CanvasRenderingContext2D 对象的创建方法:createImageData() 和 getImageData()。我们这次就使用getImageData()
.
我们来实际测试一下
先说结果,结果是一个像素的 Canvas 占内存是4Byte
。
我做了一个实验,具体是怎么做的呢?我们一起来看一下。
下面是我写的一个HTML结构,里面放了一个Canvas和一个button,点击button会打印出来一个像素的信息。
这里是代码:
代码语言:javascript复制<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="150">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "rgba(200,2,2,0.8)";
ctx.fillRect(10, 10, 50, 50);
function getImageData() {
var imgData = ctx.getImageData(10, 10, 1, 1);
console.log(imgData);
return imgData;
}
</script>
<button onclick="getImageData()">获取1像素信息</button>
</body>
</html>
这里是渲染结果:
点击之后,控制台的显示结果:
204 就是 255 * 0.8 算出来的
可以看出其实像素信息使用 Uint8 来存储的,数组长度为 4, Uint8 占用内存为 1 个字节, 因此一共是 4 个字节,所以答案就是一个像素的 Canvas 占内存是4Byte
。这个和我们的猜测31Bit
相差了1Bit,其实多这1bit也是为了方便操作。
这道题还没有完~
RGB,RGBA 等等表示法有什么本质区别
我们刚才的推测以及实验都是基于特定的颜色表示方法,比如 RGBA 或者#(六位十六进制)的形式, 如果我们采用别的方式呢?比如之前的 24 位,即 RGB 表示法。那结果就是100 * 100 *3
, 如果一个颜色不用一个字节,而是更多或者更少的字节呢?这些都会影响到结果。
因此上面我们的猜测以及“结论”都不确切,更确切地说占用多少内存完全取决于你如何对像素进行编码和解码,只有掌握这个根本点,才能无往而不利。
总结
我们先从感性认识来“猜测”了一下问题的答案,然后通过 Canvas 的内置 API 实际地检测了一下我们的猜想, 但是这个猜想似乎并不是很严谨,换句话说我们的猜想以及验证结果是有一定的前提条件的。
其实答案不重要,重要的是你看到这种问题的思维过程,这种题目很容易看出一个人的知识广度和深度, 甚至思维能力。本文完~