返回

用canvas实现画板-2.0

前端

用canvas实现画板-2.0

在上一篇文章中,我们已经实现了一个简单的画板。在这个版本中,我们将对画板进行升级,加入更多实用的功能,如图形工具和前进撤回功能。

图形工具

在2.0版本中,我们加入了图形工具,包括矩形、圆形和直线。用户可以选择要绘制的图形,然后在画布上拖动鼠标即可绘制图形。

前进撤回功能

前进撤回功能是画板中非常重要的一个功能。它允许用户撤销或重做上一次的操作。在2.0版本中,我们加入了前进撤回功能。用户可以通过点击工具栏中的前进或撤回按钮来撤销或重做上一次的操作。

实现细节

在实现这些功能时,我们遇到了不少问题。其中最难解决的问题是图形的撤回。由于图形的绘制涉及到多个步骤,因此很难实现图形的撤回。我们最终通过将图形的绘制过程记录下来,然后在撤回时反向执行这些步骤来实现图形的撤回。

总结

2.0版本中加入了许多实用的功能,使其成为一个更加强大和易用的画板。希望大家能够喜欢这个版本。

示例代码

// 创建画布
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// 设置画笔颜色
ctx.strokeStyle = "#000000";

// 设置画笔宽度
ctx.lineWidth = 1;

// 设置图形类型
var shapeType = "rect";

// 设置前进撤回栈
var undoStack = [];
var redoStack = [];

// 监听鼠标按下事件
canvas.addEventListener("mousedown", function(e) {
  // 获取鼠标点击位置
  var x = e.clientX - canvas.offsetLeft;
  var y = e.clientY - canvas.offsetTop;

  // 开始绘制图形
  ctx.beginPath();
  ctx.moveTo(x, y);

  // 将鼠标点击位置压入撤回栈
  undoStack.push({
    type: "mousedown",
    x: x,
    y: y
  });
});

// 监听鼠标移动事件
canvas.addEventListener("mousemove", function(e) {
  // 获取鼠标移动位置
  var x = e.clientX - canvas.offsetLeft;
  var y = e.clientY - canvas.offsetTop;

  // 根据图形类型绘制图形
  switch (shapeType) {
    case "rect":
      ctx.fillRect(x, y, 100, 100);
      break;
    case "circle":
      ctx.arc(x, y, 50, 0, 2 * Math.PI);
      ctx.fill();
      break;
    case "line":
      ctx.lineTo(x, y);
      ctx.stroke();
      break;
  }

  // 将鼠标移动位置压入撤回栈
  undoStack.push({
    type: "mousemove",
    x: x,
    y: y
  });
});

// 监听鼠标松开事件
canvas.addEventListener("mouseup", function(e) {
  // 获取鼠标松开位置
  var x = e.clientX - canvas.offsetLeft;
  var y = e.clientY - canvas.offsetTop;

  // 结束绘制图形
  ctx.closePath();

  // 将鼠标松开位置压入撤回栈
  undoStack.push({
    type: "mouseup",
    x: x,
    y: y
  });
});

// 监听前进按钮点击事件
document.getElementById("btnUndo").addEventListener("click", function() {
  // 获取撤回栈顶元素
  var undoItem = undoStack.pop();

  // 根据撤回栈顶元素类型撤销操作
  switch (undoItem.type) {
    case "mousedown":
      // 撤销鼠标点击操作
      ctx.clearRect(undoItem.x - 1, undoItem.y - 1, 2, 2);
      break;
    case "mousemove":
      // 撤销鼠标移动操作
      ctx.lineTo(undoItem.x, undoItem.y);
      ctx.stroke();
      break;
    case "mouseup":
      // 撤销鼠标松开操作
      ctx.closePath();
      break;
  }

  // 将撤销操作压入重做栈
  redoStack.push(undoItem);
});

// 监听撤回按钮点击事件
document.getElementById("btnRedo").addEventListener("click", function() {
  // 获取重做栈顶元素
  var redoItem = redoStack.pop();

  // 根据重做栈顶元素类型重做操作
  switch (redoItem.type) {
    case "mousedown":
      // 重做鼠标点击操作
      ctx.fillRect(redoItem.x, redoItem.y, 1, 1);
      break;
    case "mousemove":
      // 重做鼠标移动操作
      ctx.lineTo(redoItem.x, redoItem.y);
      ctx.stroke();
      break;
    case "mouseup":
      // 重做鼠标松开操作
      ctx.closePath();
      break;
  }

  // 将重做操作压入撤回栈
  undoStack.push(redoItem);
});