pixel-art.html 3.6 KB
<html>
  <head>
    <style>
      #grid-canvas {
        border-collapse: collapse;
      }
      #grid-canvas td {
        width: 20px;
        height: 20px;
        padding: 0;
        border: 1px solid black;
      }
    </style>
    <script>
      const GRID_WIDTH = 20
      const GRID_HEIGHT = 20

      // The <input type='color'> used to select the drawing color
      let colorInput
      // The <table> used to display the pixel art
      let grid
      // The WebSocket
      let socket
      // Rectangles are drawn by clicking and dragging.
      // These store the row and column of the cell that was clicked.
      let startRow
      let startCol

      // Sends a `set-color` message with the current color.
      // Called when the page loads and whenever `colorInput` changes.
      function sendColor() {
        const message = {type: 'set-color', color: colorInput.value}
        socket.send(JSON.stringify(message))
      }

      // Sends a `draw` message to create a rectangle
      // from `startRow` and `startCol` to the given row and column
      function sendRectangle(row, col) {
        const message = {
          type: 'draw',
          // Using Math.min() and Math.abs()
          // since the rectangle could have been drawn in any direction
          row: Math.min(startRow, row),
          col: Math.min(startCol, col),
          rows: Math.abs(startRow - row) + 1,
          cols: Math.abs(startCol - col) + 1
        }
        socket.send(JSON.stringify(message))
      }

      // Adds the table cell elements to `grid` and attaches
      // `mousedown` and `mouseup` event handlers to them
      function makeGrid() {
        // Build a grid of <tr> and <td> elements, like in Minesweeper
        for (let row = 0; row < GRID_HEIGHT; row++) {
          const gridRow = document.createElement('tr')
          for (let col = 0; col < GRID_WIDTH; col++) {
            const cell = document.createElement('td')
            // Handles the click of a click-and-drag
            cell.onmousedown = () => {
              startRow = row
              startCol = col
            }
            // Handles the release of a click-and-drag
            cell.onmouseup = () => sendRectangle(row, col)
            gridRow.append(cell)
          }
          grid.append(gridRow)
        }
      }

      // Handles a `draw` message from the server.
      // `{color, ..., cols}` unpacks the message's fields.
      function draw({color, row, col, rows, cols}) {
        // For each cell in the rectangle, update its background color
        for (let i = 0; i < rows; i++) {
          const gridRow = grid.children[row + i]
          for (let j = 0; j < cols; j++) {
            const gridCell = gridRow.children[col + j]
            gridCell.style.background = color
          }
        }
      }

      // We need to run code that accesses HTML elements after
      // the rest of the HTML is parsed (signaled by the `load` event)
      window.onload = () => {
        // Retrieve HTML elements
        colorInput = document.getElementById('color')
        grid = document.getElementById('grid-canvas')

        colorInput.onchange = sendColor
        makeGrid()

        // Create a WebSocket connection
        socket = new WebSocket('ws://localhost')
        // As soon as socket opens, send the initial color (black)
        socket.onopen = sendColor
        // Handles `draw` messages from the server.
        // `event.data` is the JSON string received.
        socket.onmessage = event => draw(JSON.parse(event.data))
      }
    </script>
  </head>
  <body>
    <input type='color' id='color' />
    <table id='grid-canvas'></table>
  </body>
</html>