使用 canvas 构建球金字塔
Build a pyramid of balls using the canvas
我在 canvas 上复制下面的金字塔时遇到困难。
关于如何在每条新线上绘制一个新球的数学部分,我正在苦苦挣扎。到目前为止,这是我的代码。
<canvas id="testCanvas" width="300" height="300" style="border:1px solid #d3d3d3;"></canvas>
<script>
// Access canvas element and its context
const canvas = document.getElementById('testCanvas');
const context = canvas.getContext("2d");
const x = canvas.width;
const y = canvas.height;
const radius = 10;
const diamater = radius * 2;
const numOfRows = canvas.width / diamater;
function ball(x, y) {
context.arc(x, y, radius, 0, 2 * Math.PI, true);
context.fillStyle = "#FF0000"; // red
context.fill();
}
function draw() {
for (let i = 0; i < numOfRows; i++) {
for (let j = 0; j < i + 1; j++) {
ball(
//Pos X
(x / 2),
//Pos Y
diamater * (i + 1)
);
}
}
ball(x / 2, y);
context.restore();
}
draw();
</script>
我一直被这个问题困扰了一段时间。感谢您提供的任何帮助。
谢谢。
解决这个问题的方法是一次将其分解为一个步骤。
- 在第 (1) 行画 1 个圆
- 在第 (2) 行画 2 个圆圈
- 在第 (3) 行画 3 个圆圈
等等...
然后你必须弄清楚每个圆圈的绘制位置。那你也可以分解成步骤。
第 1 行第 1 个圆在中心(宽度)
第2行第1圆圆心减去直径
第2行第2圆在圆心加上直径
等等。
这样做你会找到一个模式转换成 2 个 for 循环。
像这样:
//1st row 1st circle
ball(w/2,radius * 1, red);
//2nd row 1st circle
ball(w/2 - radius,radius * 3, blue);
//2nd row 2nd circle
ball(w/2 + radius,radius * 3, blue);
下面的代码显示了如何绘制每个球的每个步骤。我也做了一些更正来处理 numberOfRows。
const canvas = document.getElementById('testCanvas');
const context = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const radius = 10;
const diamater = radius * 2;
const numOfRows = Math.min(h / diamater, w / diamater);
const red = "#FF0000";
const blue = "#0000FF";
var k = 1;
function ball(x, y, color) {
setTimeout(function() {
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, true);
context.fillStyle = color;
context.fill();
}, (k++) * 250);
}
for (var i = 1; i <= numOfRows; i++) {
for (var j = 1; j <= i; j++) {
var y = (i * radius * 2) - radius;
var x = (w / 2) - ((i * radius) + radius) + (j * diamater);
ball(x, y, i % 2 ? red : blue);
}
}
<canvas id="testCanvas"
width="300" height="180"
style="border:1px solid #d3d3d3;"></canvas>
我注意到圆圈没有接触。我不确定您是否需要或希望他们这样做,但由于这是一个有趣的问题,所以我创建了这个答案。
堆叠圆之间的距离。
可以使用直角三角形计算行间距,如下图所示
其中 R 是圆的半径,D 是行间距。
D = ((R + R) ** 2 - R ** 2) ** 0.5;
这样我们就可以得到给定半径可以容纳的行数为
S = (H - R * 2) / D;
其中 H
是 canvas 的高度,S
是行数。
例子
给定的半径使尽可能多的行适合给定的 canvas 高度。
const ctx = canvas.getContext("2d");
const W = canvas.width, H = canvas.height, CENTER = W / 2;
const cols = ["#E80", "#0B0"];
draw();
function fillPath(path, x, y, color) {
ctx.fillStyle = color;
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.fill(path);
}
function draw() {
const R = 10;
const D = ((R * 2) ** 2 - R ** 2) ** 0.5;
const S = (H - R * 2) / D | 0;
const TOP = R + (H - (R * 2 + D * S)) / 2; // center horizontal
const circle = new Path2D();
circle.arc(0, 0, R, 0, Math.PI * 2);
var y = 0, x;
while (y <= S) {
x = 0;
const LEFT = CENTER - (y * R);
while (x <= y) {
fillPath(circle, LEFT + (x++) * R * 2, TOP + y * D, cols[y % 2]);
}
y ++;
}
}
canvas {
border:1px solid #ddd;
}
<canvas id="canvas" width="300" height="180"></canvas>
适合 n 行堆叠圆的半径
或者如果您有要适合的高度 H
和行数 S
。如下图所示。
我们想在给定 H
和 S
的情况下找到 R
我们重新排列 H
并用
求解结果二次方程
ss = S * S - 2 * S + 1;
a = 4 / ss;
b = -4 * H / ss;
c = H * H / ss;
R = (-b-(b*b - 4 * a * c) ** 0.5) / (2 * a); // the radius
例子
给定行数(数字输入)计算适合该行数的半径
const ctx = canvas.getContext("2d");
const W = canvas.width, H = canvas.height, CENTER = W / 2;
rowsIn.addEventListener("input", draw)
const cols = ["#DD0", "#0A0"];
draw();
function fillPath(path, x, y, color) {
ctx.fillStyle = color;
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.fill(path);
}
function draw() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0,0,W,H);
const S = Number(rowsIn.value);
const ss = S * S - 2 * S + 1;
const a = 4 / ss - 3, b = -4 * H / ss, c = H * H / ss;
const R = (- b - ((b * b - 4 * a * c) ** 0.5)) / (2 * a); // the radius
const TOP = R;
const D = ((R * 2) ** 2 - R ** 2) ** 0.5;
//const S = (H - R * 2) / D;
const circle = new Path2D();
circle.arc(0, 0, R, 0, Math.PI * 2);
var y = 0, x;
while (y < S) {
x = 0;
const LEFT = CENTER - (y * R);
while (x <= y) {
fillPath(circle, LEFT + (x++) * R * 2, TOP + y * D, cols[y % 2]);
}
y ++;
}
}
canvas {
border:1px solid #ddd;
}
<canvas id="canvas" width="300" height="180"></canvas>
<input type="number" id="rowsIn" min="3" max="12" value="3">Rows
我在 canvas 上复制下面的金字塔时遇到困难。
关于如何在每条新线上绘制一个新球的数学部分,我正在苦苦挣扎。到目前为止,这是我的代码。
<canvas id="testCanvas" width="300" height="300" style="border:1px solid #d3d3d3;"></canvas>
<script>
// Access canvas element and its context
const canvas = document.getElementById('testCanvas');
const context = canvas.getContext("2d");
const x = canvas.width;
const y = canvas.height;
const radius = 10;
const diamater = radius * 2;
const numOfRows = canvas.width / diamater;
function ball(x, y) {
context.arc(x, y, radius, 0, 2 * Math.PI, true);
context.fillStyle = "#FF0000"; // red
context.fill();
}
function draw() {
for (let i = 0; i < numOfRows; i++) {
for (let j = 0; j < i + 1; j++) {
ball(
//Pos X
(x / 2),
//Pos Y
diamater * (i + 1)
);
}
}
ball(x / 2, y);
context.restore();
}
draw();
</script>
我一直被这个问题困扰了一段时间。感谢您提供的任何帮助。 谢谢。
解决这个问题的方法是一次将其分解为一个步骤。
- 在第 (1) 行画 1 个圆
- 在第 (2) 行画 2 个圆圈
- 在第 (3) 行画 3 个圆圈
等等...
然后你必须弄清楚每个圆圈的绘制位置。那你也可以分解成步骤。
第 1 行第 1 个圆在中心(宽度)
第2行第1圆圆心减去直径
第2行第2圆在圆心加上直径
等等。
这样做你会找到一个模式转换成 2 个 for 循环。
像这样:
//1st row 1st circle
ball(w/2,radius * 1, red);
//2nd row 1st circle
ball(w/2 - radius,radius * 3, blue);
//2nd row 2nd circle
ball(w/2 + radius,radius * 3, blue);
下面的代码显示了如何绘制每个球的每个步骤。我也做了一些更正来处理 numberOfRows。
const canvas = document.getElementById('testCanvas');
const context = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const radius = 10;
const diamater = radius * 2;
const numOfRows = Math.min(h / diamater, w / diamater);
const red = "#FF0000";
const blue = "#0000FF";
var k = 1;
function ball(x, y, color) {
setTimeout(function() {
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, true);
context.fillStyle = color;
context.fill();
}, (k++) * 250);
}
for (var i = 1; i <= numOfRows; i++) {
for (var j = 1; j <= i; j++) {
var y = (i * radius * 2) - radius;
var x = (w / 2) - ((i * radius) + radius) + (j * diamater);
ball(x, y, i % 2 ? red : blue);
}
}
<canvas id="testCanvas"
width="300" height="180"
style="border:1px solid #d3d3d3;"></canvas>
我注意到圆圈没有接触。我不确定您是否需要或希望他们这样做,但由于这是一个有趣的问题,所以我创建了这个答案。
堆叠圆之间的距离。
可以使用直角三角形计算行间距,如下图所示
其中 R 是圆的半径,D 是行间距。
D = ((R + R) ** 2 - R ** 2) ** 0.5;
这样我们就可以得到给定半径可以容纳的行数为
S = (H - R * 2) / D;
其中 H
是 canvas 的高度,S
是行数。
例子
给定的半径使尽可能多的行适合给定的 canvas 高度。
const ctx = canvas.getContext("2d");
const W = canvas.width, H = canvas.height, CENTER = W / 2;
const cols = ["#E80", "#0B0"];
draw();
function fillPath(path, x, y, color) {
ctx.fillStyle = color;
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.fill(path);
}
function draw() {
const R = 10;
const D = ((R * 2) ** 2 - R ** 2) ** 0.5;
const S = (H - R * 2) / D | 0;
const TOP = R + (H - (R * 2 + D * S)) / 2; // center horizontal
const circle = new Path2D();
circle.arc(0, 0, R, 0, Math.PI * 2);
var y = 0, x;
while (y <= S) {
x = 0;
const LEFT = CENTER - (y * R);
while (x <= y) {
fillPath(circle, LEFT + (x++) * R * 2, TOP + y * D, cols[y % 2]);
}
y ++;
}
}
canvas {
border:1px solid #ddd;
}
<canvas id="canvas" width="300" height="180"></canvas>
适合 n 行堆叠圆的半径
或者如果您有要适合的高度 H
和行数 S
。如下图所示。
我们想在给定 H
和 S
的情况下找到 R
我们重新排列 H
并用
ss = S * S - 2 * S + 1;
a = 4 / ss;
b = -4 * H / ss;
c = H * H / ss;
R = (-b-(b*b - 4 * a * c) ** 0.5) / (2 * a); // the radius
例子
给定行数(数字输入)计算适合该行数的半径
const ctx = canvas.getContext("2d");
const W = canvas.width, H = canvas.height, CENTER = W / 2;
rowsIn.addEventListener("input", draw)
const cols = ["#DD0", "#0A0"];
draw();
function fillPath(path, x, y, color) {
ctx.fillStyle = color;
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.fill(path);
}
function draw() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0,0,W,H);
const S = Number(rowsIn.value);
const ss = S * S - 2 * S + 1;
const a = 4 / ss - 3, b = -4 * H / ss, c = H * H / ss;
const R = (- b - ((b * b - 4 * a * c) ** 0.5)) / (2 * a); // the radius
const TOP = R;
const D = ((R * 2) ** 2 - R ** 2) ** 0.5;
//const S = (H - R * 2) / D;
const circle = new Path2D();
circle.arc(0, 0, R, 0, Math.PI * 2);
var y = 0, x;
while (y < S) {
x = 0;
const LEFT = CENTER - (y * R);
while (x <= y) {
fillPath(circle, LEFT + (x++) * R * 2, TOP + y * D, cols[y % 2]);
}
y ++;
}
}
canvas {
border:1px solid #ddd;
}
<canvas id="canvas" width="300" height="180"></canvas>
<input type="number" id="rowsIn" min="3" max="12" value="3">Rows