【译】皮特·蒙德里安—Piet Mondrian

@JChehe 2018-09-05 08:13:45发表于 JChehe/blog

原文:Piet Mondrian

用代码复现皮特·蒙德里安的艺术作品并不是件简单的事情。老实说,我认为没有方法能复现他的作品,毕竟它们都是手绘的。但我们可以尝试复现皮特作品的部分工作,这也是本教程要阐述的部分。当然,我们也会进行上色。

老规矩,以下是初始化代码,其中包括设置 canvas 大小和使用 window.devicePixelRatio 缩放 canvas 以适配视网膜屏幕。而页面中仅有一个 <canvas> 元素。

var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');

var size = window.innerWidth;
var dpr = window.devicePixelRatio;
canvas.width = size * dpr;
canvas.height = size * dpr;
context.scale(dpr, dpr);
context.lineWidth = 8;

我采取的方法并不是完美的。首先创建一个大方块(canvas),然后将它进行分割。我会选择一条线(水平或竖直)将其分为多个方块,后续会为分割操作添加随机因子,而不是将所有方块都进行分割,这样应该能呈现出蒙德里安的风格,尽管会有数学上死板的感觉。

创建一组方块。

var squares = [{
  x: 0,
  y: 0,
  width: size,
  height: size
}];

一如既往地创建 “draw” 函数并进行调用。这样就能我们所做的东西。

function draw() {
  for (var i = 0; i < squares.length; i++) {
    context.beginPath();
    context.rect(
      squares[i].x,
      squares[i].y,
      squares[i].width,
      squares[i].height
    );
    context.stroke();
  }
}

draw()

01

这会遍历所有方块(目前仅有一个方块,并绘制在 canvas 上)。

现在,创建一个用于寻找在哪个方块进行分割的函数,该函数会在我们指定的方向上对方块进行分割。

function splitSquaresWith(coordinates) {
  // 遍历找出需要进行分割的方块
}

function splitOnX(square, splitAt) {
  // 基于提供的 x 坐标,创建两个新方块
}

function splitOnY(square, splitAt) {
  // 基于提供的 y 坐标,创建两个方块
}

splitSquaresWith({x: 160})
splitSquaresWith({y: 160})

代码末尾调用了分割方块的函数,分别在 x、y 的中间位置。若代码能正常运行,我们就可以做更多的分裂操作。但就目前而言,更适合试验。

splitSquaresWith 函数:

const { x, y } = coordinates;

for (var i = squares.length - 1; i >= 0; i--) {
  const square = squares[i];
  
  if (x && x > square.x && x < square.x + square.width) {
    squares.splice(i, 1);
    splitOnX(square, x);
  }

  if (y && y > square.y && y < square.y + square.height) {
    squares.splice(i, 1);
    splitOnY(square, y);
  }
}

这里使用了一些小技巧:

  • const { x, y } = coordinates 会提取对象的 xy 变量,如 {x: 160}{y: 160}
  • 使用 (var i = squares.length - 1; i >= 0; i--) 逆序遍历方块,是为了让新元素剔除出循环(分割操作会将一个方块替换为两个)。逆序遍历意味着在遍历下标无需调整的同时,避免新方块不会被再次分割。

当然,现在还是仅有一个方块,这是因为 splitOn 函数还未实现。实质上两者(splitOnXsplitOnY)非常相似。

splitOnX

var squareA = {
  x: square.x,
  y: square.y,
  width: square.width - (square.width - splitAt + square.x),
  height: square.height
};

var squareB = {
  x: splitAt,
  y: square.y,
  width: square.width - splitAt + square.x,
  height: square.height
};

squares.push(squareA);
squares.push(squareB);

splitOnY

var squareA = {
  x: square.x,
  y: square.y,
  width: square.width,
  height: square.height - (square.height - splitAt + square.y)
};

var squareB = {
  x: square.x,
  y: splitAt,
  width: square.width,
  height: square.height - splitAt + square.y
};

squares.push(squareA);
squares.push(squareB);

02

这两个函数均将先前的一个方块分割成两个方块,并将创建的方块添加到 squares 数组中。通过两次居中分割,最终形成了一个窗口。

取消两次硬编码的分割调用,通过 step 变量,遍历多次进行分割。

var step = size / 6;

然后循环遍历。

for (var i = 0; i < size; i += step) {
  splitSquaresWith({ y: i });
  splitSquaresWith({ x: i });
}

03

这就有了多个方块。通过添加随机因子,将原来每次 100% 分割变为 50% 的机会进行分割。

if(Math.random() > 0.5) {
  squares.splice(i, 1);
  splitOnX(square, x); 
}

04

哇喔,看起来不错。y 轴同理。

if(Math.random() > 0.5) {
  squares.splice(i, 1);
  splitOnY(square, y); 
}

05

这就是我们想要的形状和结构!与往常一样,所有教程均可点击编辑器与案例之间的小箭头,让代码重新运行(译者注:原文可体验)。每次点击均可看到不同形状的蒙德里安结构。

现在,让我们为它赋予色彩。首先,定义变量。使用漂亮的红蓝黄色。

var white = '#F2F5F1';
var colors = ['#D40920', '#1356A2', '#F7D842']

我们随机选择三个方块,为它们各自赋予一种颜色。你可能会看到仅有 1 或 2 种颜色,这是因为同一个方块被随机选中了两次以上。

for (var i = 0; i < colors.length; i++) {
  squares[Math.floor(Math.random() * squares.length)].color = colors[i];
}

当然,还需要确保在 draw 函数内进行填充操作。

if(squares[i].color) {
  context.fillStyle = squares[i].color;
} else {
  context.fillStyle = white
}
context.fill()

06

美丽的色彩!

基于网格,你可以轻松增加或减少复杂性。

var step = size / 20;

07

var step = size / 4;

08

var step = size / 7;

09

这就是我们拥有的蒙德里安作品。