Emil
Emil

Reputation: 107

How do I save a drawn path on a canvas as a function in JavaScript?

I am making a drawing tool for my photo editor application. I managed to make the drawing tool. However, since I have multiple tools like filters and text, the canvas has to be cleared and redrawn. I had to do this when I added text earlier. That was pretty easy, because the text was always stored in a function. Therefore I could just call that function after clearing the canvas to redraw the things that was already there. However, that is not as easy with a drawn path, as the path is never saved in a variable or a function. I have noe idea how I can manage to do that.

I basically want the drawn paths to be stored in some way, so that I can call them whenever I want to. Then they should appear on the canvas in the exact same way. Because right now they disappear whenever I use another tool, since I have to clear the canvas.

I am using Vanilla JS.

Here are the necessary parts of the HTML and JavaScript code:

var imageLoader = document.getElementById('imageLoader');
imageLoader.addEventListener('change', handleImage, false);
var canvas = document.getElementById('imageCanvas');
var ctx = canvas.getContext('2d');

var img;

function handleImage(e){
  var reader = new FileReader();
  reader.onload = function(event){
    img = new Image();
    img.onload = function(){
      var ratio = this.height / this.width;
      canvas.height = canvas.width * ratio;
      ctx.drawImage(img,0,0,canvas.width, canvas.height);
    }
    img.src = event.target.result;
  }
  reader.readAsDataURL(e.target.files[0]);
}

var pos = { x: 0, y: 0 };

document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);

function setPosition(e) {
  var rect = canvas.getBoundingClientRect();
  pos.x = e.clientX - rect.left
  pos.y = e.clientY - rect.top
}

function draw(e) {
    if (e.buttons !== 1) return;
    console.log(pos.x)
    ctx.beginPath();

    ctx.lineWidth = 5;
    ctx.lineCap = 'round';
    ctx.strokeStyle = '#c0392b';

    ctx.moveTo(pos.x, pos.y);
    setPosition(e);
    ctx.lineTo(pos.x, pos.y);

    ctx.stroke();
}
<input type="file" id="imageLoader"/>
<div class="container" id="container">
  <canvas id="imageCanvas"></canvas>
</div>

Upvotes: 1

Views: 1101

Answers (1)

Emil S. J&#248;rgensen
Emil S. J&#248;rgensen

Reputation: 6366

One of the simplest solutions would be to keep a secondary canvas to keep your pixel manipulations on.

Something like this might work for you:

let canvas = document.getElementById("imageCanvas");
let canvasContext = canvas.getContext("2d");
let canvasShadow = canvas.cloneNode();
let canvasContextShadow = canvasShadow.getContext("2d");

function SaveCanvas() {
  canvasContextShadow.clearRect(0, 0, canvasShadow.width, canvasShadow.height);
  canvasContextShadow.drawImage(canvas, 0, 0);
}

function ResetCanvas() {
  canvasContext.clearRect(0, 0, canvas.width, canvas.height);
  canvasContext.drawImage(canvasShadow, 0, 0);
}
//Events
var pos = {
  x: 0,
  y: 0
};
document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);
document.getElementById("reset").addEventListener('click', ResetCanvas);
document.getElementById("save").addEventListener('click', SaveCanvas);

function setPosition(e) {
  var rect = canvas.getBoundingClientRect();
  pos.x = e.clientX - rect.left;
  pos.y = e.clientY - rect.top;
}

function draw(e) {
  if (e.buttons !== 1)
    return;
  canvasContext.beginPath();
  canvasContext.lineWidth = 5;
  canvasContext.lineCap = 'round';
  canvasContext.strokeStyle = '#c0392b';
  canvasContext.moveTo(pos.x, pos.y);
  setPosition(e);
  canvasContext.lineTo(pos.x, pos.y);
  canvasContext.stroke();
}
canvas {
  background: #eee
}
<canvas id="imageCanvas" width="100" height="100"></canvas>
<button id="reset">Reset</button>
<button id="save">Save</button>

Upvotes: 1

Related Questions