SOURCE

console 命令行工具 X clear

                    
>
console
//
//    Copyright (c) 2023 Lanter
//
//按钮事件
var didc_sketchpad;$(document).ready(function () {didc_sketchpad =new didc_sketchpad({element:'#didc_sketchpad',width:400,height:400
});$('#didc_color-picker').change(color);$('#didc_color-picker').val('#000');$('#didc_size-picker').change(size);$('#didc_size-picker').val(1);});function didc_undo() {didc_sketchpad.didc_undo();}
function didc_redo() {didc_sketchpad.didc_redo();}
function color(event) {didc_sketchpad.color =$(event.target).val();}
function size(event) {didc_sketchpad.penSize =$(event.target).val();}
function animatesketchpad() {didc_sketchpad.animate(10);}
function didc_save() {didc_sketchpad.didc_save();}
function ofsoclear() {didc_sketchpad.ofsoclear();}
function eraser() {didc_sketchpad.eraser();}
//按钮事件
function didc_sketchpad(config) {
  // Enforces the context for all functions
  for (var key in this.constructor.prototype) {
    this[key] = this[key].bind(this);
  }

  // Warn the user if no DOM element was selected
  if (!config.hasOwnProperty('element')) {
    console.error('didc_sketchpad ERROR: No element selected');
    return;
  }

  if (typeof(config.element) === 'string') {
    this.element = $(config.element);
  }
  else {
    this.element = config.element;
  }

  // Width can be defined on the HTML or programatically
  this._width = config.width || this.element.attr('data-width') || 0;
  this._height = config.height || this.element.attr('data-height') || 0;

  // Pen attributes
  this.color = config.color || this.element.attr('data-color') || '#000000';
  this.penSize = config.penSize || this.element.attr('data-penSize') || 5;

  // ReadOnly didc_sketchpads may not be modified
  this.readOnly = config.readOnly ||
                  this.element.attr('data-readOnly') ||
                  false;
  if (!this.readOnly) {
      this.element.css({cursor: 'crosshair'});
  }

  // Stroke control variables
  this.strokes = config.strokes || [];
  this._currentStroke = {
    color: null,
    size: null,
    lines: [],
  };

  // Undo History
  this.undoHistory = config.undoHistory || [];

  // Animation function calls
  this.animateIds = [];

  // Set sketching state
  this._sketching = false;

  // Setup canvas sketching listeners
  this.reset();
}

//
// Private API
//

didc_sketchpad.prototype._cursorPosition = function(event) {
  return {
    x: event.pageX - $(this.canvas).offset().left,
    y: event.pageY - $(this.canvas).offset().top,
  };
};

didc_sketchpad.prototype._draw = function(start, end, color, size) {
  this._stroke(start, end, color, size,type);
};
//画笔和橡皮切换
var tmp="" 
var type="source-over"
didc_sketchpad.prototype.eraser= function() {
document.getElementById("eraser").innerHTML="画笔"
if(tmp==""){type="destination-out";tmp="2"}else if(tmp=="2"){
document.getElementById("eraser").innerHTML="橡皮"    
type="source-over";tmp=""}
};
//画笔和橡皮切换
didc_sketchpad.prototype._stroke = function(start, end, color, size, compositeOperation) {
  this.context.save();
  this.context.lineJoin = 'round';
  this.context.lineCap = 'round';
  this.context.strokeStyle = color;
  this.context.lineWidth = size;
  this.context.globalCompositeOperation = type;
  this.context.beginPath();
  this.context.moveTo(start.x, start.y);
  this.context.lineTo(end.x, end.y);
  this.context.closePath();
  this.context.stroke();

  this.context.restore();
};

//
// Callback Handlers
//

didc_sketchpad.prototype._mouseDown = function(event) {
  this._lastPosition = this._cursorPosition(event);
  this._currentStroke.color = this.color;
  this._currentStroke.size = this.penSize;
  this._currentStroke.lines = [];
  this._currentStroke.globalCompositeOperation=type;
  this._sketching = true;
  this.canvas.addEventListener('mousemove', this._mouseMove);
};

didc_sketchpad.prototype._mouseUp = function(event) {
  if (this._sketching) {
    this.strokes.push($.extend(true, {}, this._currentStroke));
    this._sketching = false;
  }
  this.canvas.removeEventListener('mousemove', this._mouseMove);
};

didc_sketchpad.prototype._mouseMove = function(event) {
  var currentPosition = this._cursorPosition(event);

  this._draw(this._lastPosition, currentPosition, this.color, this.penSize);
  this._currentStroke.lines.push({
    start: $.extend(true, {}, this._lastPosition),
    end: $.extend(true, {}, currentPosition),
  });

  this._lastPosition = currentPosition;
};

didc_sketchpad.prototype._touchStart = function(event) {
  event.preventDefault();
  if (this._sketching) {
    return;
  }
  this._lastPosition = this._cursorPosition(event.changedTouches[0]);
  this._currentStroke.color = this.color;
  this._currentStroke.size = this.penSize;
  this._currentStroke.lines = [];
  this._sketching = true;
  this.canvas.addEventListener('touchmove', this._touchMove, false);
};

didc_sketchpad.prototype._touchEnd = function(event) {
  event.preventDefault();
  if (this._sketching) {
    this.strokes.push($.extend(true, {}, this._currentStroke));
    this._sketching = false;
  }
  this.canvas.removeEventListener('touchmove', this._touchMove);
};

didc_sketchpad.prototype._touchCancel = function(event) {
  event.preventDefault();
  if (this._sketching) {
    this.strokes.push($.extend(true, {}, this._currentStroke));
    this._sketching = false;
  }
  this.canvas.removeEventListener('touchmove', this._touchMove);
};

didc_sketchpad.prototype._touchLeave = function(event) {
  event.preventDefault();
  if (this._sketching) {
    this.strokes.push($.extend(true, {}, this._currentStroke));
    this._sketching = false;
  }
  this.canvas.removeEventListener('touchmove', this._touchMove);
};

didc_sketchpad.prototype._touchMove = function(event) {
  event.preventDefault();
  var currentPosition = this._cursorPosition(event.changedTouches[0]);

  this._draw(this._lastPosition, currentPosition, this.color, this.penSize);
  this._currentStroke.lines.push({
    start: $.extend(true, {}, this._lastPosition),
    end: $.extend(true, {}, currentPosition),
  });

  this._lastPosition = currentPosition;
};

//
// Public API
//
didc_sketchpad.prototype.reset = function() {
  // Set attributes
  this.canvas = this.element[0];
  this.canvas.width = this._width;
  this.canvas.height = this._height;
  this.context = this.canvas.getContext('2d');

  // Setup event listeners
  this.redraw(this.strokes);

  if (this.readOnly) {
    return;
  }

  // Mouse
  this.canvas.addEventListener('mousedown', this._mouseDown);
  this.canvas.addEventListener('mouseout', this._mouseUp);
  this.canvas.addEventListener('mouseup', this._mouseUp);

  // Touch
  this.canvas.addEventListener('touchstart', this._touchStart);
  this.canvas.addEventListener('touchend', this._touchEnd);
  this.canvas.addEventListener('touchcancel', this._touchCancel);
  this.canvas.addEventListener('touchleave', this._touchLeave);
};

didc_sketchpad.prototype.drawStroke = function(stroke) {
  for (var j = 0; j < stroke.lines.length; j++) {
    var line = stroke.lines[j];
    this._draw(line.start, line.end, stroke.color, stroke.size);
  }
};

didc_sketchpad.prototype.redraw = function(strokes) {
  for (var i = 0; i < strokes.length; i++) {
//给逐个重绘添加叠加样式选择器
        type=this.strokes[i].globalCompositeOperation;
        if (type=="source-over"){
        this.context.globalCompositeOperation="source-over"
        this.drawStroke(strokes[i]);
        }else{
        this.context.globalCompositeOperation=type;
            this.drawStroke(strokes[i]);
        }
//

  }
};

didc_sketchpad.prototype.toObject = function() {
  return {
    width: this.canvas.width,
    height: this.canvas.height,
    strokes: this.strokes,
    undoHistory: this.undoHistory,
  };
};

didc_sketchpad.prototype.toJSON = function() {
  return JSON.stringify(this.toObject());
};

didc_sketchpad.prototype.animate = function(ms, loop, loopDelay) {
  this.clear();
  var delay = ms;
  var callback = null;
  for (var i = 0; i < this.strokes.length; i++) {
    var stroke = this.strokes[i];
    for (var j = 0; j < stroke.lines.length; j++) {
      var line = stroke.lines[j];
      callback = this._draw.bind(this, line.start, line.end,
                                 stroke.color, stroke.size);
      this.animateIds.push(setTimeout(callback, delay));
      delay += ms;
    }
  }

  if (loop) {
    loopDelay = loopDelay || 0;
    callback = this.animate.bind(this, ms, loop, loopDelay);
    this.animateIds.push(setTimeout(callback, delay + loopDelay));
  }
};

didc_sketchpad.prototype.cancelAnimation = function() {
  for (var i = 0; i < this.animateIds.length; i++) {
    clearTimeout(this.animateIds[i]);
  }
};
//tmp=2是和destination-out并存的,处于擦除,tmp=""是和source-over并存,处于画笔
/*撤销过程:去掉并返回数组this.strokes最后一位,然后把返回的最后这一个加到历史储存,剩下的给画布。
问题:这时候剩下的最后一位,在因为叠加模式需要是source-over,当橡皮模式(橡皮)时destination-out,
(一步擦除以上时)之前所有的橡皮都会全部显示出来,这种现象“在画笔模式”重做也存在问题,要在无论什么模式下
重做就要根据元数据叠加方式进行,①注释掉判断,解决了。
-----------------------
?要怎么办呢
-----------------------
分析:撤销的过程是向后的,去掉不要的,把剩下的显示出来,但问题是现在202302231624剩下的这部分不再能
区分叠加模式,因为是整块的出现,所以模式还必须是source-over。
解决撤销的问题:
方案1:将保留的部分进行遍历,获取叠加模式,然后重绘,可以参照笔记回放,然后在撤销里将剩下的
为同样方法处理。(实际上就是将剩下的进行从0到最后的重做过程),重绘过程在redraw,给它加上和重做一样的叠加判断。

*/
didc_sketchpad.prototype.clear = function() {
  this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
};

didc_sketchpad.prototype.didc_undo = function() {
  this.clear();
  var stroke = this.strokes.pop();
  if (stroke) {
type="source-over"//保持
    this.undoHistory.push(stroke);//去除的最后一位加到历史储存
    this.redraw(this.strokes);//去除最后一位剩下的
  }
};
/*
重做过程:去掉并返回历史记录的最后一位,将返回的这一位加到现有画布储存,返回的最后一位再给画布。
问题:写了一段根据画布储存叠加方式的判断,最后这一位之前是什么叠加方式,现在就还是什么叠加方式。
分析:重做是向前的,也就是在手册中的源图像 = 您打算放置到画布上的绘图=取回来的这一位
目标图像 = 您已经放置在画布上的绘图=已经存在的。只要判断出要添加的这一位是什么模式,
再给现在画板设置成这个模式,就没有问题了。
 */
didc_sketchpad.prototype.didc_redo = function() {
  var stroke = this.undoHistory.pop();
  if (stroke) {
      /*重做叠加方式判断,stroke是历史的最后一位,也就是要重做回来的,获取他的显示模式,
      并赋予现在显示模式。*/
// ①           if(tmp=="2"){
        type=stroke.globalCompositeOperation;
        console.log(stroke.globalCompositeOperation)
//①      }else{type="source-over"}
              console.log(tmp)
              this.context.globalCompositeOperation=type
          console.log(this.context.globalCompositeOperation)
          //
    this.strokes.push(stroke);
    this.drawStroke(stroke);
  }
};
//保存提交
didc_sketchpad.prototype.didc_save = function() {
var canvas = document.getElementById("didc_sketchpad");
    if (window.navigator.msSaveOrOpenBlob) {//ie浏览器
      var imgData = canvas.msToBlob();
      var blobObj = new Blob([imgData]);
    } else {//谷歌火狐浏览器
document.getElementById("back").style.background="url("+canvas.toDataURL('image/png')+")"
document.getElementById("back").style.backgroundPosition='center';
document.getElementById("back").style.backgroundRepeat= 'no-repeat';
document.getElementById("back").style.backgroundSize='contain';
   //console.log(canvas.toDataURL("image/png"))
ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);//清空画布
            ctx.beginPath();
    }
}
//保存提交
//清屏
didc_sketchpad.prototype.ofsoclear=function(){
var canvas = document.getElementById("didc_sketchpad");
ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);//清空画布
            ctx.beginPath();
}
//清屏
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> canvas涂鸦画板 </title>
<link rel="stylesheet" href="css/sketchpad.css">
<style>.didc_sketchpad{background:#FFF;width:400px;height:400px;border-radius:2px;-webkit-box-shadow:2px 2px 5px 0px rgba(50,50,50,0.75);-moz-box-shadow:2px 2px 5px 0px rgba(50,50,50,0.75);box-shadow:2px 2px 5px 0px rgba(50,50,50,0.75);}</style>
</head>
<body>
<div id="main_content_wrap" class="outer">
<section id="didc_main_content" class="didc_inner">
<div style="text-align: center">
<div id="back"style="width:100px;height:100px;background:url()"></div>
<canvas class="didc_sketchpad" id="didc_sketchpad"><canvas>
</div>
<div style="text-align: center">
<button id="eraser"onclick="eraser()">橡皮</button>
<button onclick="ofsoclear()">清屏</button>
<button onclick="didc_undo()">撤销</button>
<button onclick="didc_redo()">重做</button>
<input id="didc_color-picker" type="color">
<input id="didc_size-picker" type="range" min="1" max="50">
<button onclick="animatesketchpad()">笔迹回放</button>
<button id="didc_save" onclick="didc_save()">保存</button>
</div>
</section>
</div>
<script src="js/jquery.js" type="text/javascript"></script>
<script src="js/sketchpad.js"></script>
</body>
</html>

本项目引用的自定义外部资源