richardmyu / blog

个人(issues)博客
https://github.com/richardmyu/blog/issues
MIT License
0 stars 0 forks source link

canvas 绘制矩形 #21

Open richardmyu opened 3 years ago

richardmyu commented 3 years ago

今天看 cnavas 时,偶然瞥见 fillStyle 示例,就延展了一下,没想到如下奇图:

canvas-fillrect

canvas-fillrect

按照一般顺序,分图 1、2、3、4 来说明。

图 1 是在 MDN 的样例放大一倍的结果;图 2、3、4 分别在前者的基础上放大一倍形成。

1.“3维”数据转换“2维”数据

在完成第一次放大后,会有如下代码:

for (var i = 0; i < 12; i++) {
      for (var j = 0; j < 12; j++) {
        ctx_two.fillStyle = 'rgb(' + (255 - 21.25 * i) + ',' + (255 - 21.25 * j) + ',0)';
        ctx_two.fillRect(j * 50, i * 50, 50, 50);
      }
}

注意到,生成色中,只有两个变量,第三个参数被固定了。于是就想把第三个参数也弄成变量,这样颜色是不是更丰富一些。

增加了变量以后,就引发了一个问题:第三个变量是无关第一第二的变量,还是设置为相关的好?

先尝试使用无关的第三方变量,随之而来的第一个问题是:再增加一次循环,会得到循环次数(n)的三次方个数据,而表格只有 n^2 个小格,即增加的颜色会因为格数不够而无法展示。那只有把表格扩大了。

扩大的表格,意味着,要将“3维”数据降维为“2维”数据。换算成简单的数学问题:n^3 < m ^2,用计算器可以解决。

2.自然数排列

第二个问题:三轮循环中获取的颜色值,只能变成一维数组储存;而在填充小矩形时,需要将其转换为二维数组。换算成数学问题:如何将一个小于 n 的连续自然数,按横轴、纵轴完全排列。

这个问题听起来很简单,实际处理毫无头绪,好在大学读过几本闲书,刚好知道。大概记得是一个俄罗斯的数学家(名字忘了,叫啥排列也忘了...:scream:)发现的一个自然数排列。

横轴:1,3,5,7... 以奇数依次排列;纵轴:2^1,2^2,2^3,2^4.... 以 2 的次幂依次排列,构成一个二维表,可以排列所有自然数。

0 1    3      5      7      9      11         ...
  2^1  2^1*3  2^1*5  2^1*7  2^1*9  2^1*11
  2^2  2^2*3  2^2*5  2^2*7  2^2*9  2^2*11
  2^3  2^3*3  2^3*5  2^3*7  2^3*9  2^3*11
  2^4  2^4*3  2^4*5  2^4*7  2^4*9  2^4*11
  2^5  2^5*3  2^5*5  2^5*7  2^5*9  2^5*11
  ...

按照这个规律,写了脚本测试:

function create_natural_arrange(n) {
  const ary = [];
  let result = 0;
  for (let i = 1; i < n; i++) {
    for (let j = 1; j < n; j++) {
      result = (2 ** (j - 1)) * (2 * i - 1);
      if (result <= n) {
        if (!ary[j]) {
          ary[j] = [];
        }
        ary[j][i] = (2 ** (j - 1)) * (2 * i - 1);
      }
    }
  }
  console.log(ary);
}

测试部分数据如下(过大的数据,排列不整齐,就没放上来):

// create_natural_arrange(24);
[
  <1 empty item>,
  [ <1 empty item>, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 ],
  [ <1 empty item>, 2, 6, 10, 14, 18, 22 ],
  [ <1 empty item>, 4, 12, 20 ],
  [ <1 empty item>, 8, 24 ],
  [ <1 empty item>, 16 ]
]

// create_natural_arrange(35);
[
  <1 empty item>,
  [ <1 empty item>, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35 ],
  [ <1 empty item>, 2, 6, 10, 14, 18, 22, 26, 30, 34 ],
  [ <1 empty item>, 4, 12, 20, 28 ],
  [ <1 empty item>, 8, 24 ],
  [ <1 empty item>, 16 ],
  [ <1 empty item>, 32 ]
]

虽然弄出来了,但实际上没用这个,而是用了简单的 (i+1)*(j+1),然后画风就突变到了图 2:rofl:。看到这么明显的锯齿,忍不住继续放大看看,于是就有了 图 3、4,却是越来越平滑了。

richardmyu commented 3 years ago

3.第三相关变量

在上述中有提到 rgb 第三个变量的问题,这里为第三变量为相关变量做一个拓展。

canvas-fillrect

图 1 是没有放大,只改变第三变量的结果 (z = (x + y) / 2)。 图 2 是图 1 放大 5 倍的结果。

richardmyu commented 3 years ago

4.修改颜色提取规则

前面说过,在使用第三方无关变量时,取色规则没有使用复杂的方法,而只用了简单的 (i+1)*(j+1)。现在我们试着修改这部分。

4.1.直接顺序读取

ctx_first.fillStyle = FIRST_COLORS.shift() || "#ffffff";

结果(如图 1):

canvas-fillrect

后面的白色色块(以及图二的白色色块),明显是因为生成的色块少于方格数(24^3 < 120^2)。

4.2.“排列”读取

将储存颜色的数组通过以下函数排序:

function create_natural_arrange(n) {
      const ary = [];
      let result = 0;
      for (let i = 1; i < n; i++) {
        for (let j = 1; j < n; j++) {
          result = (2 ** (j - 1)) * (2 * i - 1);
          if (result <= n) {
            ary.push((2 ** (j - 1)) * (2 * i - 1));
          }
        }
      }
      return ary;
}

// 生成排序列表
const TWO_list = create_natural_arrange(24 ** 3);

// 取色
ctx_two.fillStyle = TWO_COLORS[TWO_list.shift()] || "#ffffff";

见上图图 2。


如果改变颜色生成规则或者取色规则,可能会出现各种各样的图吧。但变化太多,不想一一尝试了。:smiling_imp: