跳转到内容

HTML5+JS

效果是使用 HTML5 canvas 元素上的原生 JavaScript 创建的。在本节中,我们将介绍基础知识:绘制形状使用迭代绘制添加运动

在本教程中,我将使用通用的 Lightscript 模板:

<head>
<meta description="Template"/>
<meta publisher="WhirlwindFX" />
</head>
<body style="margin: 0; padding: 0;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
// Get the canvas element from the DOM
var c = document.getElementById("exCanvas");
var ctx = c.getContext("2d");
var width = 320;
var height = 200;
var hue = 0;
function update() {
// Code goes here
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
</script>

无论使用一条还是多条线,过程都保持不变。

  1. ctx.beginPath() 开始您的形状
  2. ctx.moveTo(x, y) 设置第一个点
  3. ctx.lineTo(x, y) 设置下一个点和回到第一个点的线。使用多个 lineTo 绘制更大的形状。
  4. 设置 ctx.strokeStyle 和/或 ctx.fillStyle(如果形状是封闭的)
  5. 使用 ctx.stroke() 绘制线条,使用 ctx.fill() 填充任何封闭形状。需要注意的是,在此之前,一切都是假设的——直到执行这些命令才会绘制任何内容。

以下是使用线条的三个示例:

function update() {
// One Line
ctx.beginPath();
ctx.moveTo(30, 30);
ctx.lineTo(30, 170);
ctx.strokeStyle = "blue";
ctx.stroke();
// L
ctx.beginPath();
ctx.moveTo(80, 30);
ctx.lineTo(80, 100);
ctx.lineTo(120, 100);
ctx.strokeStyle = "blue";
ctx.stroke();
// Filled Triangle
ctx.beginPath();
ctx.moveTo(130, 30);
ctx.lineTo(130, 100);
ctx.lineTo(170, 100);
ctx.lineTo(130, 30);
ctx.fillStyle = "red";
ctx.strokeStyle = "blue";
ctx.fill();
ctx.stroke();
window.requestAnimationFrame(update);
}

需要注意绘制顺序:填充的三角形最后绘制,因此如果它们在同一位置,它会覆盖其他两个形状。此外,三角形中的描边在填充之后绘制。如果我们颠倒这些步骤,描边的内部一半将被红色填充覆盖。

矩形简单且实用,尤其适合 RGB 键盘的平均分辨率。过程类似于绘制线条——只需将 moveTolineTo 命令替换为 rect(x, y, width, height)

function update() {
// Basic Square
ctx.beginPath();
ctx.rect(30, 30, 75, 75);
ctx.strokeStyle = "blue";
ctx.stroke();
// Filled Rectangle
ctx.beginPath();
ctx.rect(125, 30, 125, 100);
ctx.fillStyle = "red";
ctx.strokeStyle = "blue";
ctx.fill();
ctx.stroke();
window.requestAnimationFrame(update);
}

弧线通过围绕中心点旋转外部点来绘制,可用于创建完整的圆形。弧线命令为 ctx.arc(x, y, radius, start angle, end angle),其中两个角度均以弧度表示。与矩形不同,弧线以形状的原点(x, y)在弧线中心绘制。

function update() {
// Half-circle
ctx.beginPath();
ctx.arc(70, 100, 30, 0, Math.PI);
ctx.strokeStyle = "blue";
ctx.stroke();
// 3/4 circle
ctx.beginPath();
ctx.arc(140, 100, 30, 0, Math.PI * (3/2));
ctx.strokeStyle = "blue";
ctx.stroke();
// Full circle
ctx.beginPath();
ctx.arc(210, 100, 30, 0, Math.PI * 2);
ctx.strokeStyle = "blue";
ctx.stroke();
ctx.fillStyle = "red";
ctx.fill();
window.requestAnimationFrame(update);
}

如您所见,弧线默认顺时针绘制。这可以通过小于开始角度的结束角度来反转,或者在 ctx.arc() 方法中添加”true”作为最后一个参数。

手动绘制三个形状可能需要二十行代码,所以如果您需要绘制一百个形状,我们需要更高效的方法。JavaScript 中的循环是重复任务的简单方法,也可以应用于绘图。

以下是使用 for 循环绘制一排棋盘格方块的示例:

function update() {
for(let i = 0; i < 8; i++){
// 1. Create rectangle path and draw the stroke for each.
ctx.beginPath();
ctx.rect(i * 20, 20, 20, 20);
ctx.strokeStyle = "black";
ctx.stroke();
// 2. If 'i' is even, the fillStyle is black. Otherwise it's white.
if(i % 2 == 0){
ctx.fillStyle = "black";
} else {
ctx.fillStyle = "white";
}
// 3. Fill the shape after setting the fillStyle.
ctx.fill();
}
window.requestAnimationFrame(update);
}

以下是使用 while 循环绘制棋盘格的示例。我将在每次迭代中使用模板字面量略微改变黑色方块的亮度和白色方块的色相。

function update() {
var i = 0;
while(i < 16){
// 1. X and Y calculations with 'row' and 'column'
var iRow = Math.floor(i / 4);
var iCol = i % 4;
var ix = iCol * 20;
var iy = iRow * 20;
// 2. Path and stroke
ctx.beginPath();
ctx.rect(ix, iy, 20, 20);
ctx.strokeStyle = "black";
ctx.stroke();
// 3. Fill based on row/column parity
if(iRow % 2 == 0){
if(i % 2 == 0){
ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)`
} else {
ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`;
}
} else {
if(i % 2 == 0){
ctx.fillStyle = `hsl(${10 * i}, 100%, 50%)`;
} else {
ctx.fillStyle = `hsl(1, 0%, ${5 * i}%)`
}
}
ctx.fill();
i++;
}
window.requestAnimationFrame(update);
}

canvas 中的动画仅受您的想象力和数学知识限制。如果您在三角学或几何学方面感到不确定,我建议做一些轻量研究以将您的技能提升到更高水平。在接下来的几个示例中,我将把每个可能的变量附加到简单的振荡运动上。

振荡(规律的来回运动)的黄金标准是取计时器的 sin()余弦任何一种操作都将返回 -1 到 1 之间的值,无论传入任何值,即使它只是在递增。两者之间唯一的区别是它们略微不同步——当 cos 为 0 时,sin 为 -1 或 1,反之亦然。我们可以在此动画中利用这一点——

function update() {
// Find current time in milliseconds, then divide to slow down the animation speed
let time = Date.now() / 100;
// Draw a background so the old shapes don't stick around
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
ctx.beginPath();
//USE ONE EXAMPLE AT A TIME
// 1st example - side to side, y is constant
ctx.arc(15 * Math.cos(time) + 160, 100, 30, 0, Math.PI * 2);
// 2nd example - up and down, x is constant
ctx.arc(160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2);
// 3rd example - circle, x and y are variable
ctx.arc(15 * Math.cos(time) + 160, 15 * Math.sin(time) + 100, 30, 0, Math.PI * 2);
ctx.fillStyle = "black";
ctx.fill();
window.requestAnimationFrame(update);
}

我们还有几个属性可以随时间改变——圆半径、填充和描边。我现在将弧线角度保持不变以使形状简单,因为其余部分会非常炫目。

function update() {
let time = Date.now() / 100;
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 320,200);
ctx.beginPath();
ctx.arc(15 * Math.cos(time) + 160, 15 * Math.sin(time) + 100, 30 + 15 * Math.sin(time), 0, Math.PI * 2);
ctx.fillStyle = `hsl(${180 + 180 * Math.cos(time)}, 100%, 50%)`;
ctx.fill();
ctx.strokeStyle = `hsl(${180 + 180 * Math.sin(time)}, 100%, 50%)`;
ctx.stroke();
window.requestAnimationFrame(update);
}

我们迄今为止创建的每个形状的每个属性都可以随时间改变,我制作的每个动画都是上述技能的组合。有关创建可复用、高效动画的更多详细信息,请查阅我们的回调页面!