0

I'm trying to create a drawing board with two different colour brushes. You can select the brush you want by clicking on the appropriate button.

Note: This only needs to work on iOS Safari.

The two brushes are:

  1. A Yellow Highlighter
  2. A solid brush colour

The issue I am facing is that selecting a different brush, alters the colour of the existing brush strokes on the canvas.

How can I have each brush not affect the other?

Code:

var el = document.getElementById('c');
var ctx = el.getContext('2d');
var isDrawing;
var _highlight = false;


function marker(){
  ctx.lineWidth = 10;
 ctx.strokeStyle = 'rgba(0,0,0,1)';
}

function highlight(){
  ctx.lineWidth = 15;
  ctx.strokeStyle = 'rgba(255,255,0,0.4)';
  ctx.globalCompositeOperation = 'destination-atop';
}

document.getElementById("marker").addEventListener("click", function(){
  _highlight = false;
});

document.getElementById("clear").addEventListener("click", function(){
  ctx.clearRect(0, 0, el.width, el.height);
  ctx.restore();
  ctx.beginPath();
});


document.getElementById("highlight").addEventListener("click", function(){
  _highlight = true;
});

el.onmousedown = function(e) {
  isDrawing = true;
  if(_highlight){
  highlight();
   ctx.lineJoin = ctx.lineCap = 'round';
   ctx.moveTo(e.clientX, e.clientY);  
  }else{
    marker();
    ctx.lineJoin = ctx.lineCap = 'round';
   ctx.moveTo(e.clientX, e.clientY);
  }
};
el.onmousemove = function(e) {
  if (isDrawing) {
    if(_highlight){
      highlight();
     ctx.lineTo(e.clientX, e.clientY);
     ctx.stroke();  
    }else{
      marker();
      ctx.lineTo(e.clientX, e.clientY);
     ctx.stroke();
    }
    
  }
};
el.onmouseup = function() {
  isDrawing = false;
};
canvas#c { 
  border: 1px solid #ccc;
  background:url(http://i.imgur.com/yf6d9SX.jpg);
  position: relative;
  left: 0;
  top: 0;
  z-index: 2;
}
<canvas id="c" width="930" height="500"></canvas>

<br>
<button id="marker">Marker</button>
<button id="highlight">Highlight</button>
<button id="clear">Clear</button>
Mike
  • 1,884
  • 2
  • 24
  • 42

1 Answers1

1

You are accumulating all lines to the same path, so every time you stroke the current stroke color will be used for all of them, including the previous lines.

Try adding beginPath() at your mouse down event as well as in your pen change.

There are several other issues though in this code not addressed here, including:

  • Composite mode when pen changes
  • Mouse positions must be corrected to relative position of canvas

(For marker effect you can also use the new blending mode "multiply" instead of "destination-atop", does not work in IE though).

var el = document.getElementById('c');
var ctx = el.getContext('2d');
var isDrawing;
var _highlight = false;

function marker() {
  ctx.lineWidth = 10;
  ctx.strokeStyle = 'rgba(0,0,0,1)';
  ctx.globalCompositeOperation = 'source-over';
}

function highlight() {
  ctx.lineWidth = 15;
  ctx.strokeStyle = 'rgba(255,255,0,0.4)';
  ctx.globalCompositeOperation = "multiply";
  if (ctx.globalCompositeOperation !== "multiply")     // use multiply if available
    ctx.globalCompositeOperation = 'destination-over'; // fallback mode
}

document.getElementById("marker").addEventListener("click", function() {
  _highlight = false;
});

document.getElementById("clear").addEventListener("click", function() {
  ctx.clearRect(0, 0, el.width, el.height);
  ctx.beginPath();
});


document.getElementById("highlight").addEventListener("click", function() {
  _highlight = true;
});

el.onmousedown = function(e) {
  var pos = getMouse(e);
  isDrawing = true;
  ctx.beginPath();
  _highlight ? highlight() : marker();
  ctx.lineJoin = ctx.lineCap = 'round';
  ctx.moveTo(pos.x, pos.y);
};

el.onmousemove = function(e) {
  if (isDrawing) {
    var pos = getMouse(e);
    ctx.lineTo(pos.x, pos.y);
    ctx.stroke();
  }
};
el.onmouseup = function() {
  isDrawing = false;
};

function getMouse(e) {
  var rect = el.getBoundingClientRect();
  return {x: e.clientX - rect.left, y: e.clientY - rect.top}
}
canvas#c {
  border: 1px solid #ccc;
  background: url(http://i.imgur.com/yf6d9SX.jpg);
  position: relative;
  left: 0;
  top: 0;
  z-index: 2;
}
<canvas id="c" width="930" height="500"></canvas>
<br>
<button id="marker">Marker</button>
<button id="highlight">Highlight</button>
<button id="clear">Clear</button>
  • This removes my old drawing strokes. How would I keep the previous markings? – Mike Mar 25 '15 at 21:05
  • @Mike this is where the composite mode comes in, change destination-atop to destination-over (or use multiply). Added (some) corrections to answer. You still need to deal with long lines which are redrawn. These will become slow for long lines as well as appear jaggy (due to the over-drawings). –  Mar 25 '15 at 21:14
  • This is nearly correct. Changing destination-atop to destination-over or multiply do not give my highlighter the desired effect of being transparent. It does keep both brushes present. Is it possible to have both brushes, and the highlighter be truly transparent? – Mike Mar 25 '15 at 21:23
  • I have set strokeStyle to rgb(255, 255, 0, 0.1) and it doesn't seem to be 10% opacity. You can checkout this pen: http://codepen.io/dego89/pen/pvGOBL/ – Mike Mar 25 '15 at 21:27
  • @Mike it because you redraw the whole line every move, as mentioned already. You need to split each segment for every move using a previous position, beginPath etc. This will lead to other problems however when dealing with transparency. Have a look at [this answer](https://stackoverflow.com/questions/29072686/drawing-on-canvas-with-opacity-dots-in-line-javascript/29073539#29073539) which can be used as basis. –  Mar 25 '15 at 21:29