Use Canvas to make artboards

Use Canvas to make artboards

In this tech blog, we'll use JavaScript and Canvas technology to create a simple canvas application. This artboard will allow the user to draw lines on a canvas, use the eraser to erase the drawn content, change the color and width of the line, and support undo and redo functions.

Preparation

Before we start, we need an HTML file to set up the basic structure of the artboard. Please create a index.htmlfile and copy the following content into it:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas 画板</title>
</head>
<body>
  <canvas id="can"></canvas>
  <input type="color" id="colorInput">
  <input type="range" id="lineWidthRange" min="1" max="20" step="1">
  <span id="lineWidthValue">1</span>
  <button id="clearAllBtn">清空画板</button>
  <button id="eraserBtn">使用橡皮擦</button>
  <input type="range" id="eraserLineWidthRange" min="1" max="20" step="1">
  <span id="eraserLineWidthValue">1</span>
  <div id="eraserCircle"></div>

  <script src="app.js"></script>
</body>
</html>

Next, we need a app.jsfile for writing JavaScript code. Please create a file at the root of your project app.jsand copy the code you provided into it.

Realize function

Now, we'll explain each part of the code in detail and show how they work together to create a complete Artboards app.

Canvas initialization

const oCan = document.getElementById('can');
const ctx = oCan.getContext('2d');

const oColorInput = document.getElementById('colorInput');
const oLineWidthRange = document.getElementById('lineWidthRange');
const oLineWidthValue = document.getElementById('lineWidthValue');
const oClearAllBtn = document.getElementById('clearAllBtn');
const oEraserBtn = document.getElementById('eraserBtn');
const oEraserLineWidthRange = document.getElementById('eraserLineWidthRange');
const oEraserLineWidthValue = document.getElementById('eraserLineWidthValue');
const oEraserCircle = document.getElementById('eraserCircle');

const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;

oCan.width = clientWidth;
oCan.height = clientHeight;

The above code snippet gets the HTML element and the Canvas context object, and sets the width and height of the canvas equal to the width and height of the viewport to ensure that the canvas can fill the entire screen.

State and Constant Definitions

const state = {
  initPos: null,
  eraserStatus: false,
  drewData: [],
  revokedData: []
}

const DATA_FIELD = {
  DREW: 'drewData',
  REVOKED: 'revokedData'
}

const DATA_TYPE = {
  MOVE_TO: 'moveTo',
  LINE_TO: 'lineTo'
}

const CANVAS_VALUES = {
  DEFAULT_COLOR: '#000',
  DEFAULT_LINE_STYLE: 'round',
  DEFAULT_LINE_WIDTH: 1,
  ERASER_COLOR: '#fff'
}

const KEYBOARD = {
  UNDO: 'z',
  REDO: 'b'
}

This part of the code defines the state and constants of the artboard application. stateObjects are used to track user actions and draw data. DATA_FIELDand DATA_TYPEare used to identify the different parts of the plotted data. CANVAS_VALUESDefines some default values ​​for the canvas, including default color, line style, and line width. KEYBOARDDefines keyboard keys for undo and redo.

Initialization and event binding

const init = () => {
  initStyle();
  bindEvent();
}

function initStyle() {
  ctx.setColor(CANVAS_VALUES.DEFAULT_COLOR);
  ctx.setLineStyle(CANVAS_VALUES.DEFAULT_LINE_STYLE);
  ctx.setLineWidth(CANVAS_VALUES.DEFAULT_LINE_WIDTH);
}

function bindEvent() {
  oCan.addEventListener('mousedown', handleCanvasMouseDown, false);
  oColorInput.addEventListener('click', handleColorInput, false);
  oColorInput.addEventListener('input', handleColorInput, false);
  oLineWidthRange.addEventListener('input', handleLineWidthRangeInput, false);
  oClearAllBtn.addEventListener('click', handleClearAllBtnClick, false);
  oEraserBtn.addEventListener('click', handleEraserBtnClick, false);
  oEraserLineWidthRange.addEventListener('input', handleEraserLineWidthRangeInput, false);
  document.addEventListener('keydown', handleKeyDown, false);
}

This part of the code defines initialization and event binding functions. initThe function calls initStyleand bindEventfunctions to initialize the canvas styles and bind event listeners.

initStyleThe function sets the default color, line style and line width of the canvas.

bindEventThe function binds handlers for mouse and keyboard events, including mouse click, color selection, line width selection, clearing the artboard, using the eraser, and undo/redo operations.

draw function

function drawPoint(x, y) {
  ctx.beginPath();
  ctx.arc(x, y, ctx.lineWidth / 2, 0, 2 * Math.PI, false);
  ctx.fill();
}

function drawLine({ x1, y1, x2, y2 }) {
  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.stroke();
}

function drawBatchLine() {
  clearAll();

  state[DATA_FIELD.DREW].forEach(item => {
    ctx.beginPath();
    const { moveTo: [x1, y1], lineTo, info: { color, width } } = item;
    ctx.setColor(color);
    ctx.setLineWidth(width);
    ctx.moveTo(x1, y1);

    lineTo.forEach(line => {
      ctx.lineTo(...line);
    });

    ctx.stroke();
  })
}

function clearAll() {
  ctx.clearRect(0, 0, oCan.offsetWidth, oCan.offsetHeight);
}

The above code snippet contains functions related to drawing. drawPointIt is used to draw a point, drawLineto draw a line, drawBatchLineto draw user's drawing data in batches, and clearAllto clear the canvas.

event handler

// 省略其他事件处理函数...

function handleKeyDown(e) {
  const key = e.key;
  console.log(key);
  if ((e.metaKey || e.ctrlKey) && (Object.values(KEYBOARD).includes(key))) {
    doDrewRecord(key);
    drawBatchLine();
  }

  if (!state[DATA_FIELD.DREW].length || !state[DATA_FIELD.REVOKED].length) {
    ctx.setColor(oColorInput.value);
    ctx.setLineWidth(oLineWidthRange.value);
  }
}

function handleCanvasMouseDown(e) {
  // 省略其他事件处理代码...
}

The above code snippet contains functions to handle keyboard events and mouse events. handleKeyDownThe function is used to handle keyboard events, when the user presses the Ctrl key (or Cmd key on macOS) plus the Z or B key, it will trigger undo and redo operations. handleCanvasMouseDownThe function is used to handle the mouse press event and start the drawing operation.

helper function

// 省略其他辅助函数...

function setDrewRecord(type, data) {
  switch (type) {
    case DATA_TYPE.MOVE_TO:
      state[DATA_FIELD.DREW].push({
        [DATA_TYPE.MOVE_TO]: [...data],
        [DATA_TYPE.LINE_TO]: [],
        info: {
          color: ctx.getColor(),
          width: ctx.getLineWidth()
        }
      })
      break;
    case DATA_TYPE.LINE_TO:
      const drewData = state[DATA_FIELD.DREW];
      drewData[drewData.length - 1][DATA_TYPE.LINE_TO].push([...data]);
      break;
    default:
      break;
  }
}

function doDrewRecord(key) {
  switch (key) {
    case KEYBOARD.UNDO:
      state[DATA_FIELD.DREW].length > 0
      &&
      state[DATA_FIELD.REVOKED].push(state[DATA_FIELD.DREW].pop());
      break;
    case KEYBOARD.REDO:
      state[DATA_FIELD.REVOKED].length > 0
      &&
      state[DATA_FIELD.DREW].push(state[DATA_FIELD.REVOKED].pop());
      break;
    default:
      break;
  }
}
// 定义 EraserCircle 元素的显示与隐藏
oEraserCircle.setVisible = function (visible) {
  this.style.display = visible ? 'block' : 'none';
};

// 定义 EraserCircle 元素的大小设置
oEraserCircle.setSize = function (size) {
  this.style.width = size + 'px';
  this.style.height = size + 'px';
};

// 定义 EraserCircle 元素的位置设置
oEraserCircle.setPosition = function (x, y) {
  this.style.left = x - this.offsetWidth / 2 + 'px';
  this.style.top = y - this.offsetHeight / 2 + 'px';
};

// 设置画笔颜色
ctx.setColor = function (color) {
  this.strokeStyle = color;
  this.fillStyle = color;
};

// 获取画笔颜色
ctx.getColor = function () {
  return this.strokeStyle;
};

// 设置线条样式(线帽和连接点)
ctx.setLineStyle = function (style) {
  this.lineCap = style;
  this.lineJoin = style;
};

// 设置线条宽度
ctx.setLineWidth = function (width) {
  this.lineWidth = width;
};

// 获取线条宽度
ctx.getLineWidth = function () {
  return this.lineWidth;
};

 These functions perfect the parts omitted in the code, and are mainly used to set the display and hide of the eraser element, as well as the setting and acquisition of the brush color, line style and line width. These functions play an important role in the sketchpad application, enabling users to freely choose the brush color, line style and line width, and when using the eraser function, the eraser element can be displayed correctly at the mouse position.

Run the Sketchpad app

Save the above code as app.jsa file and create an HTML file to import it. Then open the HTML file in your browser and you can start drawing on the artboard, erasing, changing color and width, and undoing and redoing.

 

 Learning from station B up - front-end Ono Sensen

Guess you like

Origin blog.csdn.net/weixin_60895836/article/details/132031986