JChehe / blog

🌈 原创&翻译 🌈
719 stars 111 forks source link

【译】三角网格—Triangular mesh #24

Open JChehe opened 6 years ago

JChehe commented 6 years ago

原文:Triangular mesh

本文由才华横溢的 maxwellito 撰写。如果你对编写文章感兴趣,可以像他一样提交一份 提案

这种三角网格效果常出现库和 SVG 中。今天我们将用 canvas 实现它!同时,这也是一个说明坐标系和替换细节而得到漂亮效果的案例。

老规矩,首先是初始化得到一个方形 canvas。

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

var size = window.innerWidth;

canvas.width = size;
canvas.height = size;

创建点网格的常规方式是通过行和列。这些点将会被绘制在 canvas 上,并被存储在一个数组中,以便后续使用。

点是由一个含有 xy 属性的对象表示。

行列之间的间距通过 gap 变量表示。我们将点绘制成圆后,便看到网格在 canvas 上的样子。

var line,
    lines = [],
    gap = size / 7;

for (var y = gap / 2; y <= size; y+= gap) {
  line = []
  for (var x = gap / 2; x <= size; x+= gap) {
    line.push({x: x, y: y})
    context.beginPath();
    context.arc(x, y, 1, 0, 2 * Math.PI, true);
    context.fill();
  }
  lines.push(line)
}

gap 间距的点

然后替换隔行的 x 坐标。这里我们通过 odd 变量交替赋值 true 或 false 实现。

一个正三角网格将在新坐标下形成。

var line, dot,
    odd = false, 
    lines = [],
    gap = size / 8;

for (var y = gap / 2; y <= size; y+= gap) {
  odd = !odd
  line = []
  for (var x = gap / 4; x <= size; x+= gap) {
    dot = {x: x + (odd ? gap/2 : 0), y: y}
    line.push(dot)
    context.beginPath();
    context.arc(dot.x, dot.y, 1, 0, 2 * Math.PI, true);
    context.fill();
  }
  lines.push(line)
}

正三角形网格的点

下一步是使用点绘制三角形。

创建一个接收三角形三个坐标,并连接绘制它们的函数。

function drawTriangle(pointA, pointB, pointC) {
  context.beginPath();
  context.moveTo(pointA.x, pointA.y);
  context.lineTo(pointB.x, pointB.y);
  context.lineTo(pointC.x, pointC.y);
  context.lineTo(pointA.x, pointA.y);
  context.closePath();
  context.stroke();
}

现在,结合 drawTriangle 函数和点数组绘制所有三角形。

这部分也许会有点难以理解。脚本会遍历所有线,并组合相邻线的点以形成三角形。为了便于理解,我们将相邻的两条线分别称为 ab。然后将两线符合要求的点合并到一个数组中,使其看起来像“之”字型:a1b1a2b2a3 以此类推。

这将为我们提供了一个含有三角形所有坐标的数组。如:[a1, b1, a2]、[b1, a2, b2], [a2, b2, a3] 等。

var dotLine;
odd = true;

for (var y = 0; y < lines.length - 1; y++) {
  odd = !odd
  dotLine = []
  for (var i = 0; i < lines[y].length; i++) {
    dotLine.push(odd ? lines[y][i]   : lines[y+1][i])
    dotLine.push(odd ? lines[y+1][i] : lines[y][i])
  }
  for (var i = 0; i < dotLine.length - 2; i++) {
    drawTriangle(dotLine[i], dotLine[i+1], dotLine[i+2])
  }
}

正三角形网格

至此,我们得到一个正三角网格。接着,我们为一个细节赋予魔法。

现在每个点与相邻点之间的间距相同。其实,我们可以将点在该区域内进行位移,而避免与其它点发生重叠(译者注:每个点的安全位移区域是点间距的一半)。利用 Math.random() 对点进行位移。

line.push({
  x: x + (Math.random()*.8 - .4) * gap  + (odd ? gap/2 : 0),
  y: y + (Math.random()*.8 - .4) * gap,
})

随机位移

另外,还可以增加一些生成艺术的乐趣。比如填充只有 16 种色调的灰色!

var gray = Math.floor(Math.random()*16).toString(16);
context.fillStyle = '#' + gray + gray + gray; 
context.fill();

最终效果——三角网格

如果想探索该效果的更多实现细节,可看看我的库:triangulr