【译】皮特·蒙德里安—Piet Mondrian
JChehe opened this issue · comments
用代码复现皮特·蒙德里安的艺术作品并不是件简单的事情。老实说,我认为没有方法能复现他的作品,毕竟它们都是手绘的。但我们可以尝试复现皮特作品的部分工作,这也是本教程要阐述的部分。当然,我们也会进行上色。
老规矩,以下是初始化代码,其中包括设置 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()
这会遍历所有方块(目前仅有一个方块,并绘制在 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
会提取对象的x
和y
变量,如{x: 160}
或{y: 160}
。- 使用
(var i = squares.length - 1; i >= 0; i--)
逆序遍历方块,是为了让新元素剔除出循环(分割操作会将一个方块替换为两个)。逆序遍历意味着在遍历下标无需调整的同时,避免新方块不会被再次分割。
当然,现在还是仅有一个方块,这是因为 splitOn
函数还未实现。实质上两者(splitOnX
和 splitOnY
)非常相似。
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);
这两个函数均将先前的一个方块分割成两个方块,并将创建的方块添加到 squares
数组中。通过两次居中分割,最终形成了一个窗口。
取消两次硬编码的分割调用,通过 step
变量,遍历多次进行分割。
var step = size / 6;
然后循环遍历。
for (var i = 0; i < size; i += step) {
splitSquaresWith({ y: i });
splitSquaresWith({ x: i });
}
这就有了多个方块。通过添加随机因子,将原来每次 100% 分割变为 50% 的机会进行分割。
if(Math.random() > 0.5) {
squares.splice(i, 1);
splitOnX(square, x);
}
哇喔,看起来不错。y
轴同理。
if(Math.random() > 0.5) {
squares.splice(i, 1);
splitOnY(square, y);
}
这就是我们想要的形状和结构!与往常一样,所有教程均可点击编辑器与案例之间的小箭头,让代码重新运行(译者注:原文可体验)。每次点击均可看到不同形状的蒙德里安结构。
现在,让我们为它赋予色彩。首先,定义变量。使用漂亮的红蓝黄色。
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()
美丽的色彩!
基于网格,你可以轻松增加或减少复杂性。
var step = size / 20;
var step = size / 4;
var step = size / 7;
这就是我们拥有的蒙德里安作品。