使用帆布html&用六角形绘制网格JavaScript

发布于 2025-02-08 21:17:59 字数 1971 浏览 1 评论 0原文

我尝试使用六角形形状创建一个大小x * x的画布,目前我基于此参考,有了一些改编,我的代码到现在为此看起来像:

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

const shapeType = 6;
const angle = 2 * Math.PI / shapeType;
const radius = 20;

function init() {
    drawGrid(5);
}

init();

function drawGrid(size) {
    for (let y = radius, i = 0; i < size; i++) {
        y += radius * Math.sin(angle);
        for (let x = radius, j = 0;
             j < size;
             x += radius * (1 + Math.cos(angle)), y += (-1) ** j++ * radius * Math.sin(angle)) {
            drawHexagon(x, y);
        }
    }
}

function drawHexagon(x, y) {
    context.beginPath();
    for (let i = 0; i < shapeType; i++) {
        let xx = x + radius * Math.cos(angle * i);
        let yy = y + radius * Math.sin(angle * i);
        context.lineTo(xx, yy);
    }
    context.closePath();
    context.stroke();
}
<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>HexGrid</title>
    <style>
        .test {
            display: flex;
            justify-content: space-between;
        }

        .canvas {
            border: 1px solid #000000;
        }
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="500" class="canvas"></canvas>
<script src="main.js"></script>
</body>
</html>

到目前为止,每件事都很好,但是只要使用奇数,但是如果大小为,我就会明白:

“什至grid”

作为JavaScript的初学者,我在此中被阻止了,有人知道我该如何解决?

I try to create a canvas with X * X of size, using hexagon shapes, currently I base on this reference, with some adaptations, my code until now look like this:

const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');

const shapeType = 6;
const angle = 2 * Math.PI / shapeType;
const radius = 20;

function init() {
    drawGrid(5);
}

init();

function drawGrid(size) {
    for (let y = radius, i = 0; i < size; i++) {
        y += radius * Math.sin(angle);
        for (let x = radius, j = 0;
             j < size;
             x += radius * (1 + Math.cos(angle)), y += (-1) ** j++ * radius * Math.sin(angle)) {
            drawHexagon(x, y);
        }
    }
}

function drawHexagon(x, y) {
    context.beginPath();
    for (let i = 0; i < shapeType; i++) {
        let xx = x + radius * Math.cos(angle * i);
        let yy = y + radius * Math.sin(angle * i);
        context.lineTo(xx, yy);
    }
    context.closePath();
    context.stroke();
}
<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>HexGrid</title>
    <style>
        .test {
            display: flex;
            justify-content: space-between;
        }

        .canvas {
            border: 1px solid #000000;
        }
    </style>
</head>
<body>
<canvas id="canvas" width="500" height="500" class="canvas"></canvas>
<script src="main.js"></script>
</body>
</html>

Until now, every thing is good, but just with odd size, but if the size is even, I get this:

even grid

As a beginner in JavaScript, I'm blocked in this, any one have any idea how can I solve this please?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

遇见了你 2025-02-15 21:18:00

常规的六边形网格

一个六角形的网格具有常规的间距,就像一个正方形的网格一样,但是,如果偏移(在这种情况下为下降),则每2列降低了一半的六角形高度。

要获得X和Y网格间距,偏移量是Y网格间距的一半。

    RADIUS = 20;
    EDGE_LEN = Math.sin(Math.PI / 6) * RADIUS * 2;
    GRID_Y_SPACE = Math.cos(Math.PI / 6) * RADIUS * 2;
    GRID_X_SPACE = RADIUS * 2 - EDGE_LEN * 0.5;
    GRID_Y_OFFSET = GRID_Y_SPACE * 0.5;

可以使用六角形的位置(GX,GY为列,行位置)

    function gridToPixel(gx, gy, p = {}) {
        p.x = gx * GRID_X_SPACE;
        p.y = gy * GRID_Y_SPACE + (gx % 2 ? GRID_Y_OFFSET : 0);        
        return p;
    }

示例

示例用六边形网格填充画布。

const ctx = canvas.getContext('2d');

const P2 = (x, y) => ({x,y});
const EDGES = 6;
const RADIUS = 20;
const TAU = 2 * Math.PI;
const EDGE_LEN = Math.sin(Math.PI / EDGES) * RADIUS * 2;
const GRID_Y_SPACE = Math.cos(Math.PI / EDGES) * RADIUS * 2;
const GRID_X_SPACE = RADIUS * 2 - EDGE_LEN * 0.5;
const GRID_Y_OFFSET = GRID_Y_SPACE * 0.5;
const COLS = "=#3c2f18,#01335f,#3f0e77,#204a73,#511d94,#fe1f00,#0060fd,#fe7603,#f0ca1d,#b085e8,#e9cafa".split(",");
const rndItem = arr => arr[Math.random() * arr.length | 0];

drawGrid(1, 1, 15, 13, createPoly(EDGES));
function drawGrid(x, y, w, h, points) {
  const p = P2();
  var gy, gx;
  for (gy = y; gy < y + h; gy++) {
      for (gx = x; gx < x + w; gx++) {
          ctx.fillStyle = rndItem(COLS);
          drawPoly(gridToPixel(gx, gy, p), points);
      }
  }
}
function gridToPixel(gx, gy, p = {}) {
    p.x = gx * GRID_X_SPACE;
    p.y = gy * GRID_Y_SPACE + (gx % 2 ? GRID_Y_OFFSET : 0);       
    return p;
}
function drawPoly(p, points) { // p.x, p.y is center
    ctx.setTransform(1, 0, 0, 1, p.x, p.y);
    var i = 0;
    ctx.beginPath();
    while (i < points.length) {
        const p2 = points[i++];
        ctx.lineTo(p2.x, p2.y);
    }
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
}
function createPoly(sides, points = []) {
    const step = TAU / sides;
    var ang = 0, i = sides;
    while (i--) {
        points.push(P2(RADIUS * Math.cos(ang), RADIUS * Math.sin(ang)));
        ang += step;
    }
    return points;
}
canvas { border: 1px solid #000000; }
<canvas id="canvas" width="500" height="500"></canvas>

Regular Hexagon Grid

A grid of hexagons has a regular spacing just like a grid of squares however every 2nd column if offset (down in this case) by half the hexagons height.

To get the x and y grid spacing, and the offset is half the y grid spacing.

    RADIUS = 20;
    EDGE_LEN = Math.sin(Math.PI / 6) * RADIUS * 2;
    GRID_Y_SPACE = Math.cos(Math.PI / 6) * RADIUS * 2;
    GRID_X_SPACE = RADIUS * 2 - EDGE_LEN * 0.5;
    GRID_Y_OFFSET = GRID_Y_SPACE * 0.5;

The hexagon's position can be calculated using (gx, gy are column, row positions)

    function gridToPixel(gx, gy, p = {}) {
        p.x = gx * GRID_X_SPACE;
        p.y = gy * GRID_Y_SPACE + (gx % 2 ? GRID_Y_OFFSET : 0);        
        return p;
    }

Example

Example fills canvas with hexagon grid.

const ctx = canvas.getContext('2d');

const P2 = (x, y) => ({x,y});
const EDGES = 6;
const RADIUS = 20;
const TAU = 2 * Math.PI;
const EDGE_LEN = Math.sin(Math.PI / EDGES) * RADIUS * 2;
const GRID_Y_SPACE = Math.cos(Math.PI / EDGES) * RADIUS * 2;
const GRID_X_SPACE = RADIUS * 2 - EDGE_LEN * 0.5;
const GRID_Y_OFFSET = GRID_Y_SPACE * 0.5;
const COLS = "=#3c2f18,#01335f,#3f0e77,#204a73,#511d94,#fe1f00,#0060fd,#fe7603,#f0ca1d,#b085e8,#e9cafa".split(",");
const rndItem = arr => arr[Math.random() * arr.length | 0];

drawGrid(1, 1, 15, 13, createPoly(EDGES));
function drawGrid(x, y, w, h, points) {
  const p = P2();
  var gy, gx;
  for (gy = y; gy < y + h; gy++) {
      for (gx = x; gx < x + w; gx++) {
          ctx.fillStyle = rndItem(COLS);
          drawPoly(gridToPixel(gx, gy, p), points);
      }
  }
}
function gridToPixel(gx, gy, p = {}) {
    p.x = gx * GRID_X_SPACE;
    p.y = gy * GRID_Y_SPACE + (gx % 2 ? GRID_Y_OFFSET : 0);       
    return p;
}
function drawPoly(p, points) { // p.x, p.y is center
    ctx.setTransform(1, 0, 0, 1, p.x, p.y);
    var i = 0;
    ctx.beginPath();
    while (i < points.length) {
        const p2 = points[i++];
        ctx.lineTo(p2.x, p2.y);
    }
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
}
function createPoly(sides, points = []) {
    const step = TAU / sides;
    var ang = 0, i = sides;
    while (i--) {
        points.push(P2(RADIUS * Math.cos(ang), RADIUS * Math.sin(ang)));
        ang += step;
    }
    return points;
}
canvas { border: 1px solid #000000; }
<canvas id="canvas" width="500" height="500"></canvas>

怪我太投入 2025-02-15 21:17:59

我转换了 blindman67的响应对可重复使用的功能。

它是高度可配置的。

const TAU = 2 * Math.PI;

const main = () => {
  const ctx = document.querySelector('#drawing').getContext('2d');
  drawGrid(ctx, 1, 1, 15, 13, {
    radius: 20,
    inset: 2,
    randomColors: generateColors(30, 1.0, 0.667)
  });
};

const defaultGridOptions = {
  radius: 10,
  sides: 6,
  inset: 0,
  // Context
  lineWidth: 1,
  fillStyle: '',
  strokeStyle: 'black',
  // Other
  randomColors: null
};

const drawGrid = (ctx, x, y, w, h, options = {}) => {
  const opts = { ...defaultGridOptions, ...options };
  const points = createPoly(opts);
  opts.diameter = opts.radius * 2;
  for (let gy = y; gy < y + h; gy++) {
    for (let gx = x; gx < x + w; gx++) {
      ctx.fillStyle = opts.randomColors ? pickRandom(opts.randomColors) : opts.fillStyle;
      drawPoly(ctx, gridToPixel(gx, gy, opts), points, opts);
    }
  }
};

const gridToPixel = (gridX, gridY, opts) => {
  const m = gridMeasurements(opts);
  return toPoint(
    Math.floor(gridX * m.gridSpaceX),
    Math.floor(gridY * m.gridSpaceY + (gridX % 2 ? m.gridOffsetY : 0))
  );
};

const drawPoly = (ctx, origin, points, opts) => {
  ctx.strokeStyle = opts.strokeStyle;
  ctx.save();
  ctx.translate(origin.x, origin.y);
  polyPath3(ctx, points);
  ctx.restore();
  if (opts.lineWidth) ctx.lineWidth = opts.lineWidth;
  if (opts.fillStyle || opts.randomColors) ctx.fill();
  if (opts.strokeStyle) ctx.stroke();
};

const createPoly = (opts, points = []) => {
  const
    { inset, radius, sides } = opts,
    size = radius - inset,
    step = TAU / sides;
  for (let i = 0; i < sides; i++) {
    points.push(toPolarCoordinate(0, 0, size, step * i));
  }
  return points;
};

const gridMeasurements = (opts) => {
  const
    { diameter, inset, radius, sides } = opts,
    edgeLength = Math.sin(Math.PI / sides) * diameter,
    gridSpaceX = diameter - edgeLength / 2,
    gridSpaceY = Math.cos(Math.PI / sides) * diameter,
    gridOffsetY = gridSpaceY / 2;
  return {
    diameter,
    edgeLength,
    gridSpaceX,
    gridSpaceY,
    gridOffsetY
  };
};

/** @unused */
const polyPath = (ctx, x, y, radius, sides = 3) => {
  ctx.beginPath();
  ctx.moveTo(...fromPoint(toPolarCoordinate2(x, y, radius)));
  for (let i = 1; i <= sides; i += 1) {
    ctx.lineTo(...fromPoint(toPolarCoordinate2(x, y, radius, sides, i)));
  }
  ctx.closePath();
};

/** @unused */
const polyPath2 = (ctx, points = []) => {
  ctx.beginPath();
  ctx.moveTo(points[0], points[1]);
  for (let i = 2; i < points.length - 1; i += 2) {
    ctx.lineTo(points[i], points[i + 1]);
  }
  ctx.closePath();
};

const polyPath3 = (ctx, points = []) => {
  const [{ x: startX, y: startY }] = points;
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  points.forEach(({ x, y }) => { ctx.lineTo(x, y); });
  ctx.closePath();
};

const pickRandom = (arr) => arr[Math.floor(Math.random() * arr.length)];

const toPoint = (x, y) => ({ x, y });

const fromPoint = ({ x, y }) => [ x, y ];

const toPolarCoordinate = (centerX, centerY, radius, angle) => ({
  x: centerX + radius * Math.cos(angle),
  y: centerY + radius * Math.sin(angle)
});

const toPolarCoordinate2 = (centerX, centerY, radius, sides, i) =>
  toPolarCoordinate(centerX, centerY, radius, i === 0 ? 0 : (i * TAU / sides));

const generateColors = (count, saturation = 1.0, lightness = 0.5, alpha = 1.0) =>
  Array.from({ length: count }, (_, i) =>
    `hsla(${[
      Math.floor(i / count * 360),
      `${Math.floor(saturation * 100)}%`,
      `${Math.floor(lightness * 100)}%`,
      alpha
    ].join(', ')})`);

main();
#drawing { border: 1px dashed red; }
<canvas id="drawing" width="500" height="500"></canvas>

I converted Blindman67's response to a reusable function.

It is highly configurable.

const TAU = 2 * Math.PI;

const main = () => {
  const ctx = document.querySelector('#drawing').getContext('2d');
  drawGrid(ctx, 1, 1, 15, 13, {
    radius: 20,
    inset: 2,
    randomColors: generateColors(30, 1.0, 0.667)
  });
};

const defaultGridOptions = {
  radius: 10,
  sides: 6,
  inset: 0,
  // Context
  lineWidth: 1,
  fillStyle: '',
  strokeStyle: 'black',
  // Other
  randomColors: null
};

const drawGrid = (ctx, x, y, w, h, options = {}) => {
  const opts = { ...defaultGridOptions, ...options };
  const points = createPoly(opts);
  opts.diameter = opts.radius * 2;
  for (let gy = y; gy < y + h; gy++) {
    for (let gx = x; gx < x + w; gx++) {
      ctx.fillStyle = opts.randomColors ? pickRandom(opts.randomColors) : opts.fillStyle;
      drawPoly(ctx, gridToPixel(gx, gy, opts), points, opts);
    }
  }
};

const gridToPixel = (gridX, gridY, opts) => {
  const m = gridMeasurements(opts);
  return toPoint(
    Math.floor(gridX * m.gridSpaceX),
    Math.floor(gridY * m.gridSpaceY + (gridX % 2 ? m.gridOffsetY : 0))
  );
};

const drawPoly = (ctx, origin, points, opts) => {
  ctx.strokeStyle = opts.strokeStyle;
  ctx.save();
  ctx.translate(origin.x, origin.y);
  polyPath3(ctx, points);
  ctx.restore();
  if (opts.lineWidth) ctx.lineWidth = opts.lineWidth;
  if (opts.fillStyle || opts.randomColors) ctx.fill();
  if (opts.strokeStyle) ctx.stroke();
};

const createPoly = (opts, points = []) => {
  const
    { inset, radius, sides } = opts,
    size = radius - inset,
    step = TAU / sides;
  for (let i = 0; i < sides; i++) {
    points.push(toPolarCoordinate(0, 0, size, step * i));
  }
  return points;
};

const gridMeasurements = (opts) => {
  const
    { diameter, inset, radius, sides } = opts,
    edgeLength = Math.sin(Math.PI / sides) * diameter,
    gridSpaceX = diameter - edgeLength / 2,
    gridSpaceY = Math.cos(Math.PI / sides) * diameter,
    gridOffsetY = gridSpaceY / 2;
  return {
    diameter,
    edgeLength,
    gridSpaceX,
    gridSpaceY,
    gridOffsetY
  };
};

/** @unused */
const polyPath = (ctx, x, y, radius, sides = 3) => {
  ctx.beginPath();
  ctx.moveTo(...fromPoint(toPolarCoordinate2(x, y, radius)));
  for (let i = 1; i <= sides; i += 1) {
    ctx.lineTo(...fromPoint(toPolarCoordinate2(x, y, radius, sides, i)));
  }
  ctx.closePath();
};

/** @unused */
const polyPath2 = (ctx, points = []) => {
  ctx.beginPath();
  ctx.moveTo(points[0], points[1]);
  for (let i = 2; i < points.length - 1; i += 2) {
    ctx.lineTo(points[i], points[i + 1]);
  }
  ctx.closePath();
};

const polyPath3 = (ctx, points = []) => {
  const [{ x: startX, y: startY }] = points;
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  points.forEach(({ x, y }) => { ctx.lineTo(x, y); });
  ctx.closePath();
};

const pickRandom = (arr) => arr[Math.floor(Math.random() * arr.length)];

const toPoint = (x, y) => ({ x, y });

const fromPoint = ({ x, y }) => [ x, y ];

const toPolarCoordinate = (centerX, centerY, radius, angle) => ({
  x: centerX + radius * Math.cos(angle),
  y: centerY + radius * Math.sin(angle)
});

const toPolarCoordinate2 = (centerX, centerY, radius, sides, i) =>
  toPolarCoordinate(centerX, centerY, radius, i === 0 ? 0 : (i * TAU / sides));

const generateColors = (count, saturation = 1.0, lightness = 0.5, alpha = 1.0) =>
  Array.from({ length: count }, (_, i) =>
    `hsla(${[
      Math.floor(i / count * 360),
      `${Math.floor(saturation * 100)}%`,
      `${Math.floor(lightness * 100)}%`,
      alpha
    ].join(', ')})`);

main();
#drawing { border: 1px dashed red; }
<canvas id="drawing" width="500" height="500"></canvas>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文