1

是否可以使用带有破折号的交互式 html5 画布?我希望能够像康威的生活游戏一样绘制网格。让它是交互式的,我可以点击一个单元格并激活/停用它。并且能够设置画布大小。谢谢

4

1 回答 1

0

注意:从问题的措辞来看,重点似乎是拥有一个带有可点击单元格的画布网格,这些单元格可以在 Dash 中打开和关闭,所以我将在我的回答中解决这个问题。我不会在这里讨论康威的生命游戏,但如果你想实现它或它的变体,这个答案可以作为一个起点。

dash_html_components模块使我们可以访问Canvas组件。

我们可以做的是创建一个运行一次的客户端回调,通过它我们可以使用 Javascript 访问 DOM,这样我们就可以基于Dash 组件canvas呈现的元素创建网格Canvas并将点击事件侦听器附加到它。

Dash 应用程序可能如下所示:

from dash import Dash
import dash_html_components as html
from dash.dependencies import ClientsideFunction, Output, Input

app = Dash(__name__)
app.layout = html.Canvas(id="canvas")


app.clientside_callback(
    ClientsideFunction(namespace="clientside", function_name="init_grid"),
    Output("canvas", "children"),
    Input("canvas", "children"),
)


if __name__ == "__main__":
    app.run_server()

然后你可以.js在你的目录中添加一个文件,assets里面有这个:

window.dash_clientside = Object.assign({}, window.dash_clientside, {
  clientside: {
    init_grid: function (_) {
      const canvas = document.querySelector("canvas");
      canvas.width = 400;
      canvas.height = 400;
      const ctx = canvas.getContext("2d");
      const cellWidth = 50;
      const cellHeight = 50;
      const numRows = canvas.height / cellHeight;
      const numCols = canvas.width / cellWidth;

      const drawGrid = () => {
        const coordinates = [];
        for (let row = 0; row < numRows; row++) {
          for (let col = 0; col < numCols; col++) {
            ctx.strokeRect(
              col * cellWidth,
              row * cellHeight,
              cellWidth,
              cellHeight
            );
            coordinates.push({ row, col, filledIn: false });
          }
        }
        return coordinates;
      };

      const grid = drawGrid();

      canvas.addEventListener("click", (event) => {
        const clickedRow = Math.floor(event.offsetY / cellHeight);
        const clickedCol = Math.floor(event.offsetX / cellWidth);

        const cell = grid.filter((cell) => {
          return cell.row === clickedRow && cell.col === clickedCol;
        })[0];

        const rectDimensions = [
          clickedCol * cellWidth, // x
          clickedRow * cellHeight, // y
          cellWidth,
          cellHeight,
        ];

        if (!cell.filledIn) {
          ctx.fillStyle = "black";
        } else {
          ctx.fillStyle = "white";
        }

        ctx.fillRect(...rectDimensions);
        ctx.fillStyle = "black";
        ctx.strokeRect(...rectDimensions);

        cell.filledIn = !cell.filledIn;
      });

      return [];
    },
  },
});

有关在 Dash 应用程序中包含 Javascript 的更多信息,请参阅此处的文档。

所以上面代码的想法是使用嵌套循环来创建一个网格。我们根据当前行号、列号以及我们为所有单元格设置的宽度和高度绘制矩形。

除了绘制网格之外,还保存了所有网格坐标,因此我们可以确定用户单击了哪个单元格,是否应该填充或清除该单元格。为了知道是否应该清除或填充单元格filledIn,为所有 xy 坐标对设置了一个属性。


结果:

网格展示

于 2021-07-17T16:54:56.537 回答