Skip to content

Instantly share code, notes, and snippets.

@seflless
Last active October 13, 2023 16:42
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save seflless/6929dc7bc99f130961d600813a561a61 to your computer and use it in GitHub Desktop.
Save seflless/6929dc7bc99f130961d600813a561a61 to your computer and use it in GitHub Desktop.

ChatGPT Builds A Rudimentary Whiteboarding App

Here's the conversation I had collaborating with ChatGPT to build a basic diagramming app.

Features

  • Canvas based rendering
  • You can create rectangles (click to create a rectangle)
  • You can move rectangles (click on Move Rectangle)
  • You can change the fill color of rectangles (Click on Change Color, then click on a rectangle to randomly assign it a fill color)

Notes

Chat Transcript

Following

Francois Laberge

Create the code of a desktop whiteboarding web app where you can create rectangles, move/translate them, and change their fill color. Also support for pan and zoom using laptop touchpads.

ChatGPT

Here is some sample code for a whiteboarding web app that allows you to create rectangles, move and translate them, and change their fill color:

<!DOCTYPE html>
<html>
<head>
  <title>Whiteboarding App</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      background-color: #f3f3f3;
    }

    #canvas {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
      background-color: #fff;
    }

    #toolbar {
      position: absolute;
      top: 10px;
      left: 10px;
    }

    #toolbar button {
      display: inline-block;
      margin-right: 10px;
      padding: 5px 10px;
      background-color: #ddd;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }

    #toolbar button.selected {
      background-color: #aaa;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <div id="toolbar">
    <button id="create-rectangle-btn">Create Rectangle</button>
    <button id="move-rectangle-btn">Move Rectangle</button>
    <button id="change-color-btn">Change Color</button>
  </div>

  <script>
    // Set up canvas
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    ctx.scale(dpr, dpr);

    // Set up toolbar
    const createRectangleBtn = document.getElementById('create-rectangle-btn');
    const moveRectangleBtn = document.getElementById('move-rectangle-btn');
    const changeColorBtn = document.getElementById('change-color-btn');
    const toolbarBtns = [createRectangleBtn, moveRectangleBtn, changeColorBtn];

    // Set up state
    let mode = 'create';
    let rectangles = [];
    let selectedRectangle = null;
    let startX = 0;
    let startY = 0;
    let isDragging = false;

    // Handle mouse/touch events
    canvas.addEventListener('mousedown', handleMouseDown);
    canvas.addEventListener('mousemove', handleMouseMove);
    canvas.addEventListener('mouseup', handleMouseUp);
    canvas.addEventListener('touchstart', handleTouchStart);
    canvas.addEventListener('touchmove', handleTouchMove);
    canvas.addEventListener('touchend', handleTouchEnd);

    // Handle toolbar button clicks
    createRectangleBtn.addEventListener('click', () => setMode('create'));
    moveRectangleBtn.addEventListener('click',

Francois Laberge

continue

ChatGPT

() => setMode('move'));
changeColorBtn.addEventListener('click', () => setMode('change-color'));

// Set the current mode
function setMode(newMode) {
  mode = newMode;
  toolbarBtns.forEach(btn => btn.classList.remove('selected'));
  switch (mode) {
    case 'create':
      createRectangleBtn.classList.add('selected');
      break;
    case 'move':
      moveRectangleBtn.classList.add('selected');
      break;
    case 'change-color':
      changeColorBtn.classList.add('selected');
      break;
  }
}

// Handle mouse down events
function handleMouseDown(event) {
  const x = event.clientX;
  const y = event.clientY;
  startX = x;
  startY = y;

  switch (mode) {
    case 'create':
      // Create a new rectangle
      rectangles.push({
        x: x,
        y: y,
        width: 0,
        height: 0,
        fill: '#000000'
      });
      break;
    case 'move':
      // Select the rectangle under the cursor
      selectedRectangle = getRectangleAtPoint(x, y);
      if (selectedRectangle) {
        isDragging = true;
      }
      break;
    case 'change-color':
      // Change the color of the rectangle under the cursor
      const rectangle = getRectangleAtPoint(x, y);
      if (rectangle) {
        rectangle.fill = getRandomColor();
      }
      break;
  }

  // Redraw the canvas
  drawCanvas();
}

// Handle mouse move events
function handleMouseMove(event) {
  const x = event.clientX;
  const y = event.clientY;

  switch (mode) {
    case 'create':
      // Update the size of the new rectangle
      const rectangle = rectangles[rectangles.length - 1];
      rectangle.width = x - rectangle.x;
      rectangle.height = y - rectangle.y;
      break;
    case 'move':
      // Move the selected rectangle
      if (selectedRectangle && isDragging) {
        selectedRectangle.x += x - startX;
        selectedRectangle.y += y - startY;
        startX = x;
        startY = y;
      }
      break;
  }

  // Redraw the canvas
  drawCanvas();
}

// Handle mouse up events
function handleMouseUp(event) {
  isDragging = false;
}

// Handle touch start events
function handleTouchStart(event) {
  // Prevent the screen from scrolling
  event.preventDefault();

  // Convert the touch event to a mouse event
  const touch = event.changedTouches[0];
  const mouseEvent = new MouseEvent('mousedown', {
    clientX: touch.clientX,
    clientY: touch.clientY
  });
  canvas.dispatchEvent(mouseEvent);
}

// Handle touch move events
function handleTouchMove(event) {

Francois Laberge

continue

ChatGPT

// Prevent the screen from scrolling
event.preventDefault();

Copy code
  // Convert the touch event to a mouse event
  const touch = event.changedTouches[0];
  const mouseEvent = new MouseEvent('mousemove', {
    clientX: touch.clientX,
    clientY: touch.clientY
  });
  canvas.dispatchEvent(mouseEvent);
}

// Handle touch end events
function handleTouchEnd(event) {
  // Convert the touch event to a mouse event
  const touch = event.changedTouches[0];
  const mouseEvent = new MouseEvent('mouseup', {
    clientX: touch.clientX,
    clientY: touch.clientY
  });
  canvas.dispatchEvent(mouseEvent);
}

// Get the rectangle at a given point
function getRectangleAtPoint(x, y) {
  return rectangles.find(r => (
    x >= r.x && x <= r.x + r.width &&
    y >= r.y && y <= r.y + r.height
  ));
}

// Get a random color
function getRandomColor() {
  return '#' + Math.floor(Math.random() * 16777215).toString(16);
}

// Draw the canvas
function drawCanvas() {
  // Clear the canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Draw all the rectangles
  rectangles.forEach(r => {
    ctx.fillStyle = r.fill;
    ctx.fillRect(r.x, r.y, r.width, r.height);
  });
}
  </script>
</body>
</html>

This code uses the HTML5 canvas element to draw the rectangles and handles mouse and touch events to allow the user to create, move, and change the fill color of the rectangles.

To support pan and zoom using laptop touchpads, you can add some additional code to handle the wheel event and use the scale and translate transformations on the canvas context. Here is an example of how you might do this:

// Set up canvas
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);

// Set up state
let scale = 1.0;
let translateX = 0;
let translateY = 0;

// Handle wheel events
canvas.addEventListener('wheel', handleWheel);

// Handle wheel events
function handleWheel(event) {
  // Calculate the new scale and translation values
  const delta = event.deltaY < 0 ? 1.1 : 0.9;
  const x = event.clientX;
  const y = event.clientY;
  const prevScale = scale;
  scale *= delta;
  translateX = (translateX - x) * delta + x;
  translateY = (translateY - y)

Francois Laberge

Generate the code of a desktop whiteboarding web app where you can create rectangles, move/translate them, and change their fill color. Only use pointer events and mousewheel events for the zooming and rectangle manipulation.

ChatGPT

Here is some sample code for a whiteboarding web app that allows you to create rectangles, move and translate them, and change their fill color using only pointer and mousewheel events:

<!DOCTYPE html>
<html>
<head>
  <title>Whiteboarding App</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      background-color: #f3f3f3;
    }

    #canvas {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
      background-color: #fff;
    }

    #toolbar {
      position: absolute;
      top: 10px;
      left: 10px;
    }

    #toolbar button {
      display: inline-block;
      margin-right: 10px;
      padding: 5px 10px;
      background-color: #ddd;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }

    #toolbar button.selected {
      background-color: #aaa;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <div id="toolbar">
    <button id="create-rectangle-btn">Create Rectangle</button>
    <button id="move-rectangle-btn">Move Rectangle</button>
    <button id="change-color-btn">Change Color</button>
  </div>

  <script>
    // Set up canvas
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    ctx.scale(dpr, dpr);

    // Set up toolbar
    const createRectangleBtn = document.getElementById('create-rectangle-btn');
    const moveRectangleBtn = document.getElementById('move-rectangle-btn');
    const changeColorBtn = document.getElementById('change-color-btn');
    const toolbarBtns = [createRectangleBtn, moveRectangleBtn, changeColorBtn];

    // Set up state
    let mode = 'create';
    let rectangles = [];
    let selectedRectangle = null;
    let isDragging = false;
    let scale = 1.0;
    let translateX = 0;
    let translateY = 0;

    // Handle pointer events
    canvas.addEventListener('pointerdown', handlePointerDown);
    canvas.addEventListener('pointermove', handlePointerMove);
    canvas.addEventListener('pointerup', handlePointerUp);

    // Handle mousewheel events
    canvas.addEventListener('wheel', handleWheel);

    // Handle toolbar button clicks
    createRectangleBtn.addEventListener('click', () => setMode('create'));
    moveRectangleBtn.addEventListener('click', () => setMode('move'));
    change

Francois Laberge

continue

ChatGPT

ColorBtn.addEventListener('click', () => setMode('change-color'));

// Set the current mode
function setMode(newMode) {
  mode = newMode;
  toolbarBtns.forEach(btn => btn.classList.remove('selected'));
  switch (mode) {
    case 'create':
      createRectangleBtn.classList.add('selected');
      break;
    case 'move':
      moveRectangleBtn.classList.add('selected');
      break;
    case 'change-color':
      changeColorBtn.classList.add('selected');
      break;
  }
}

// Handle pointer down events
function handlePointerDown(event) {
  const x = event.clientX;
  const y = event.clientY;

  switch (mode) {
    case 'create':
      // Create a new rectangle
      rectangles.push({
        x: x,
        y: y,
        width: 0,
        height: 0,
        fill: '#000000'
      });
      break;
    case 'move':
      // Select the rectangle under the cursor
      selectedRectangle = getRectangleAtPoint(x, y);
      if (selectedRectangle) {
        isDragging = true;
      }
      break;
    case 'change-color':
      // Change the color of the rectangle under the cursor
      const rectangle = getRectangleAtPoint(x, y);
      if (rectangle) {
        rectangle.fill = getRandomColor();
      }
      break;
  }

  // Redraw the canvas
  drawCanvas();
}

// Handle pointer move events
function handlePointerMove(event) {
  const x = event.clientX;
  const y = event.clientY;

  switch (mode) {
    case 'create':
      // Update the size of the new rectangle
      const rectangle = rectangles[rectangles.length - 1];
      rectangle.width = x - rectangle.x;
      rectangle.height = y - rectangle.y;
      break;
    case 'move':
      // Move the selected rectangle
      if (selectedRectangle && isDragging) {
        selectedRectangle.x = x;
        selectedRectangle.y = y;
      }
      break;
  }

  // Redraw the canvas
  drawCanvas();
}

// Handle pointer up events
function handlePointerUp(event) {
  isDragging = false;
}

// Handle wheel events
function handleWheel(event) {
  // Calculate the new scale and translation values
  const delta = event.deltaY < 0 ? 1.1 : 0.9;
  const x = event.clientX;
  const y = event.clientY;
  const prevScale = scale;
  scale *= delta;
  translateX = (translateX - x) * delta + x;
  translateY = (translateY - y) * delta + y;

  // Redraw the canvas
  drawCanvas();
}

// Get the rectangle at a given point
function getRectangleAtPoint(x, y) {
  return rectangles.find(r => (

Francois Laberge

continue and finish up if you can

ChatGPT

x >= r.x && x <= r.x + r.width &&
y >= r.y && y <= r.y + r.height
));
}

Copy code
// Get a random color
function getRandomColor() {
  return '#' + Math.floor(Math.random() * 16777215).toString(16);
}

// Draw the canvas
function drawCanvas() {
  // Clear the canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Apply the scale and translation transformations
  ctx.setTransform(scale, 0, 0, scale, translateX, translateY);

  // Draw all the rectangles
  rectangles.forEach(r => {
    ctx.fillStyle = r.fill;
    ctx.fillRect(r.x, r.y, r.width, r.height);
  });
}
  </script>
</body>
</html>

This code uses pointer and mousewheel events to allow the user to create, move, and change the fill color of the rectangles, and it uses the scale and translate transformations on the canvas context to support zooming.

Francois Laberge

change the move code so that mouse sticks to the rectangle where it started to drag

ChatGPT

To change the code so that the mouse "sticks" to the rectangle when you start dragging it, you can update the handlePointerMove function to calculate the distance between the initial pointer position and the current pointer position, and then apply that offset to the selected rectangle's position. Here is an example of how you might do this:

// Handle pointer move events
function handlePointerMove(event) {
  const x = event.clientX;
  const y = event.clientY;

  switch (mode) {
    case 'create':
      // Update the size of the new rectangle
      const rectangle = rectangles[rectangles.length - 1];
      rectangle.width = x - rectangle.x;
      rectangle.height = y - rectangle.y;
      break;
    case 'move':
      // Move the selected rectangle
      if (selectedRectangle && isDragging) {
        const offsetX = x - startX;
        const offsetY = y - startY;
        selectedRectangle.x += offsetX;
        selectedRectangle.y += offsetY;
      }
      break;
  }

  // Redraw the canvas
  drawCanvas();
}
In this updated code, the offsetX and offsetY variables represent the distance between the initial pointer position (startX and startY) and the current pointer position, and those offsets are applied to the selected rectangle's position. This will cause the rectangle to "stick" to the cursor as you drag it.

Francois Laberge

startX and startY aren't declared or set anywhere, please fix that.

ChatGPT

You are correct, startX and startY are not declared or set in the code. To fix this, you can add some additional code to handle the pointerdown event and store the initial pointer position in startX and startY. Here is an example of how you might do this:

// Set up state
let mode = 'create';
let rectangles = [];
let selectedRectangle = null;
let isDragging = false;
let startX = 0;
let startY = 0;
let scale = 1.0;
let translateX = 0;
let translateY = 0;

// Handle pointer events
canvas.addEventListener('pointerdown', handlePointerDown);
canvas.addEventListener('pointermove', handlePointerMove);
canvas.addEventListener('pointerup', handlePointerUp);

// Handle pointer down events
function handlePointerDown(event) {
  const x = event.clientX;
  const y = event.clientY;

  switch (mode) {
    case 'create':
      // Create a new rectangle
      rectangles.push({
        x: x,
        y: y,
        width: 0,
        height: 0,
        fill: '#000000'
      });
      break;
    case 'move':
      // Select the rectangle under the cursor
      selectedRectangle = getRectangleAtPoint(x, y);
      if (selectedRectangle) {
        isDragging = true;
        startX = x;
        startY = y;
      }
      break;
    case 'change-color':
      // Change the color of the rectangle under the cursor
      const rectangle = getRectangleAtPoint(x, y);
      if (rectangle) {
        rectangle.fill = getRandomColor();
      }
      break;
  }

  // Redraw the canvas
  drawCanvas();
}

In this updated code, startX and startY are declared and initialized at the top of the script, and they are set to the initial pointer position in the handlePointerDown function when the user starts dragging a rectangle. These values are then used in the handlePointerMove function to calculate the distance between the initial pointer position and the current pointer position, as described in the previous response.

Francois Laberge

This is making the rectangles fly out of the screen when I'm in move rectangle mode. Are you sure about the startX/startY logic?

ChatGPT

Yes, you are correct. There is a mistake in the handlePointerMove function where the startX and startY variables are not being updated as the user moves the pointer. This causes the rectangle to "fly out of the screen" when you start dragging it.

To fix this issue, you can update the handlePointerMove function to update the startX and startY variables as the user moves the pointer. Here is an example of how you might do this:

// Handle pointer move events
function handlePointerMove(event) {
  const x = event.clientX;
  const y = event.clientY;

  switch (mode) {
    case 'create':
      // Update the size of the new rectangle
      const rectangle = rectangles[rectangles.length - 1];
      rectangle.width = x - rectangle.x;
      rectangle.height = y - rectangle.y;
      break;
    case 'move':
      // Move the selected rectangle
      if (selectedRectangle && isDragging) {
        const offsetX = x - startX;
        const offsetY = y - startY;
        startX = x;
        startY = y;
        selectedRectangle.x += offsetX;
        selectedRectangle.y += offsetY;
      }
      break;
  }

  // Redraw the canvas
  drawCanvas();
}

In this updated code, the startX and

Francois Laberge

Can you add keyboard shortcuts for switch modes? r for create rectangle, escape to go back to move rectangle, and f to go into Change Color mode?

ChatGPT

To add keyboard shortcuts for switching modes, you can add some additional code to listen for the keydown event and check the pressed key against the desired shortcuts. Here is an example of how you might do this:

// Set up state
let mode = 'create';
let rectangles = [];
let selectedRectangle = null;
let isDragging = false;
let startX = 0;
let startY = 0;
let scale = 1.0;
let translateX = 0;
let translateY = 0;

// Handle pointer events
canvas.addEventListener('pointerdown', handlePointerDown);
canvas.addEventListener('pointermove', handlePointerMove);
canvas.addEventListener('pointerup', handlePointerUp);

// Handle keyboard events
document.addEventListener('keydown', handleKeyDown);

// Handle pointer down events
function handlePointerDown(event) {
  // ...
}

// Handle pointer move events
function handlePointerMove(event) {
  // ...
}

// Handle key down events
function handleKeyDown(event) {
  switch (event.key) {
    case 'r':
      setMode('create');
      break;
    case 'Escape':
      setMode('move');
      break;
    case 'f':
      setMode('change-color');
      break;
  }
}

In this updated code, the handleKeyDown function listens for the keydown event and checks the pressed key against the desired shortcuts. When a matching key is detected, the function calls the setMode function to change the current mode. This allows the user to switch modes using the keyboard shortcuts r, Escape, and f.

Francois Laberge

Are there emoticons that would be good replacements for the text of each button? For example a square emoticon for "Create Rectangle". Can you change the html please?

ChatGPT

Yes, there are several emoticons that could be used as replacements for the text of the buttons in the whiteboarding app. For example, the "Create Rectangle" button could use the square emoticon (🔲) as a replacement for the text.

Here is an example of how you might update the HTML to use emoticons for the button text:

Copy code
<!DOCTYPE html>
<html>
<head>
  <title>Whiteboarding App</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      background-color: #f3f3f3;
    }

    #canvas {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
      background-color: #fff;
    }

    #toolbar {
      position: absolute;
      top: 10px;
      left: 10px;
    }

    #toolbar button {
      display: inline-block;
      margin-right: 10px;
      padding: 5px 10px;
      background-color: #ddd;
      border: none;

Francois Laberge

What's a good emoticon for Move Rectangle and Change Color?

ChatGPT

There are several emoticons that could be used as replacements for the "Move Rectangle" and "Change Color" buttons in the whiteboarding app.

For the "Move Rectangle" button, one possible emoticon is the hand with the index finger pointing up (☝️), which could represent moving the rectangle with your finger. Another possible emoticon is the open hand (👐), which could represent grabbing the rectangle and moving it around.

For the "Change Color" button, one possible emoticon is the droplet (💧), which could represent changing the color of the rectangle by adding a different color of paint or ink. Another possible emoticon is the artist palette (🎨), which could represent choosing a new color for the rectangle from a palette of available colors.

Here is an example of how you might update the HTML to use these emoticons for the button text:

<!DOCTYPE html>
<html>
<head>
  <title>Whiteboarding App</title>
  <style>
    body {
      margin: 0;
      padding: 0;
      background-color: #f3f3f3;
    }

    #canvas {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      margin: auto;
      background-color: #fff;
    }

    #toolbar {
      position: absolute;
      top: 10px;
      left: 10px;
    }

    #toolbar button {
      display: inline-block;
      margin-right: 10px;
      padding: 5px 10px;
      background-color: #ddd;
      border: none;
      font-size: 16px;
    }

    #toolbar button.selected {
      background-color: #999;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>

  <div id="toolbar">
    <button id="create-rectangle-btn">🔲</button>
    <button id="move-rectangle-btn">☝️</button>
    <button id="change-color-btn">🎨</button>
  </div>

  <script>
    // Get the canvas and context
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    // Set the initial canvas size
    const width = window.innerWidth;
    const height = window.innerHeight;
    canvas.width = width;
    canvas.height = height;

    // Set up state
    let mode = 'create';
    let rectangles = [];
    let selectedRectangle = null;
    let isDragging = false;
    let startX = 0;
    let startY = 0;
    let scale = 1.0;
    let translateX = 0;
    let translateY = 0;

    // Get the toolbar buttons
    const createRectangleBtn = document.getElementById('create-rectangle-btn');
    const moveRectangleBtn = document.getElementById('move-rectangle-btn');
    const changeColorBtn = document

Francois Laberge

Can you change the styling for the tool buttons so it's less subtle which is currently active?

ChatGPT

To make it more obvious which button is currently active, you can update the CSS to add a visual indicator, such as a border or a different background color, for the selected button. Here is an example of how you might do this:

#toolbar button {
  display: inline-block;
  margin-right: 10px;
  padding: 5px 10px;
  background-color: #ddd;
  border: none;
  font-size: 16px;
}

#toolbar button.selected {
  background-color: #999;
  border: 2px solid #333;
}
In this updated CSS, the #toolbar button.selected selector is used to style the selected button with a different background color and a border. This will make it more obvious which button is currently active.

You can also update the JavaScript code to add the selected class to the selected button, which will allow the CSS to apply the appropriate styles. Here is an example of how you might do this:

Copy code
// Set up state
let mode = 'create';
let rectangles = [];
let selectedRectangle = null;
let isDragging = false;
let startX = 0;
let startY = 0;
let scale = 1.0;
let translateX = 0;
let translateY = 0;

// Get the toolbar buttons
const createRectangleBtn = document.getElementById('create-rectangle-btn');
const moveRectangleBtn = document.getElementById('move-rectangle-btn');
const changeColorBtn = document.getElementById('change-color-btn');

// Set up the initial button selection
createRectangleBtn.classList.add('selected');

// Handle pointer events
canvas.addEventListener('pointerdown', handlePointerDown);
canvas.addEventListener('pointermove', handlePointerMove);
canvas.addEventListener('pointerup', handlePointerUp);

// Handle keyboard events
document.addEventListener('keydown', handleKeyDown);

// Set the current mode
function setMode(newMode) {
  // Remove the selected class from the current button
  switch (mode) {
    case 'create':
      createRectangleBtn.classList.remove('selected');
      break;
    case 'move':
      moveRectangleBtn.classList.remove('selected');
      break;
    case 'change-color':
      changeColorBtn.classList.remove('selected');
      break;
  }

  // Update the current mode
  mode = newMode;

  // Add the selected class to the new button
  switch (mode) {
    case 'create':
      createRectangleBtn.classList.add('selected');
      break;
    case 'move':
      moveRectangleBtn.classList.add('selected');
      break;
    case 'change-color':
      changeColorBtn.classList.add('selected');
      break;
  }
}

// Handle pointer down events
function handlePointerDown(event) {
  // ...
}

// Handle pointer move events
function handlePointerMove(event) {

Francois Laberge

Let's add support for pan/zooming the whiteboard content. Only using mousewheel event, no support for mobile devices for now

ChatGPT

To add support for panning and zooming the whiteboard content using the mousewheel, you can add some additional code to handle the wheel event and update the scale and translateX/translateY values accordingly. Here is an example of how you might do this:

// Set up state
let mode = 'create';
let rectangles = [];
let selectedRectangle = null;
let isDragging = false;
let startX = 0;
let startY = 0;
let scale = 1.0;
let translateX = 0;
let translateY = 0;

// Handle pointer events
canvas.addEventListener('pointerdown', handlePointerDown);
canvas.addEventListener('pointermove', handlePointerMove);
canvas.addEventListener('pointerup', handlePointerUp);

// Handle wheel events
canvas.addEventListener('wheel', handleWheel);

// Handle pointer down events
function handlePointerDown(event) {
  // ...
}

// Handle pointer move events
function handlePointerMove(event) {
  // ...
}

// Handle wheel events
function handleWheel(event) {
  // Calculate the new scale and translation values
  const delta = event.deltaY > 0 ? -0.1 : 0.1;
  scale += delta;
  translateX -= event.clientX * delta / scale;
  translateY -= event.clientY * delta / scale;

  // Redraw the canvas
  drawCanvas();
}

In this updated code, the handleWheel function listens for the wheel event and calculates the new scale and translateX/translateY values based on the delta of the wheel event and the current pointer position. These values are then used in the drawCanvas function to apply the panning and zooming transformations to the canvas. Note that this code only supports using the mousewheel for panning and zooming, and it does not support mobile devices. To support mobile devices, you would need to add additional code to handle touch events, such as touchstart, touchmove, and touchend, and use these events to calculate the appropriate panning and zooming transformations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment