状态

应用状态将是一个带有图片,工具和颜色属性的对象。 图片本身就是一个对象,存储图片的宽度,高度和像素内容。 像素逐行存储在一个数组中,方式与第 6 章中的矩阵类相同,按行存储,从上到下。

  1. class Picture {
  2. constructor(width, height, pixels) {
  3. this.width = width;
  4. this.height = height;
  5. this.pixels = pixels;
  6. }
  7. static empty(width, height, color) {
  8. let pixels = new Array(width * height).fill(color);
  9. return new Picture(width, height, pixels);
  10. }
  11. pixel(x, y) {
  12. return this.pixels[x + y * this.width];
  13. }
  14. draw(pixels) {
  15. let copy = this.pixels.slice();
  16. for (let {x, y, color} of pixels) {
  17. copy[x + y * this.width] = color;
  18. }
  19. return new Picture(this.width, this.height, copy);
  20. }
  21. }

我们希望能够将图片当做不变的值,我们将在本章后面回顾其原因。 但是我们有时也需要一次更新大量像素。 为此,该类有draw方法,接受更新后的像素(具有xycolor属性的对象)的数组,并创建一个覆盖这些像素的新图像。 此方法使用不带参数的slice来复制整个像素数组 - 切片的起始位置默认为 0,结束位置为数组的长度。

empty方法使用我们以前没有见过的两个数组功能。 可以使用数字调用Array构造器来创建给定长度的空数组。 然后fill方法可以用于使用给定值填充数组。 这些用于创建一个数组,所有像素具有相同颜色。

颜色存储为字符串,包含传统 CSS 颜色代码 - 一个井号(#),后跟六个十六进制数字,两个用于红色分量,两个用于绿色分量,两个用于蓝色分量。这是一种有点神秘而不方便的颜色编写方法,但它是 HTML 颜色输入字段使用的格式,并且可以在canvas绘图上下文的fillColor属性中使用,所以对于我们在程序中使用颜色的方式,它足够实用。

所有分量都为零的黑色写成"#000000",亮粉色看起来像#ff00ff",其中红色和蓝色分量的最大值为 255,以十六进制数字写为ffaf用作数字 10 到 15)。

我们将允许界面将动作分派为对象,它是属性覆盖先前状态的属性。当用户改变颜色字段时,颜色字段可以分派像{color: field.value}这样的对象,从这个对象可以计算出一个新的状态。

  1. function updateState(state, action) {
  2. return Object.assign({}, state, action);
  3. }

这是相当麻烦的模式,其中Object.assign用于首先将状态属性添加到空对象,然后使用来自动作的属性覆盖其中的一些属性,这在使用不可变对象的 JavaScript 代码中很常见。 一个更方便的表示法处于标准化的最后阶段,也就是在对象表达式中使用三点运算符来包含另一个对象的所有属性。 有了这个补充,你可以写出{...state, ...action}。 在撰写本文时,这还不适用于所有浏览器。