Spots in semi-transparent line(canvas drawing)- js

Viewed 86

I am trying to make a highlighter,

The problem is with the transparency, maybe due to the lineCap=round, and some other reason there are many dark spots in just one line currently(it should not be). I can't explain more in words,compare the images below

Current look(when I draw 1 line):

The look I want when I draw 1 line:

The look I want when I draw 2 lines:

Gif


Current code:

lineThickess = 50;
lineColor = 'rgba(255,255,0,0.1)';

// wait for the content of the window element
// to load, then performs the operations.
// This is considered best practice.
window.addEventListener('load', () => {

  resize(); // Resizes the canvas once the window loads
  document.addEventListener('mousedown', startPainting);
  document.addEventListener('mouseup', stopPainting);
  document.addEventListener('mousemove', sketch);
  window.addEventListener('resize', resize);
});

const canvas = document.querySelector('#canvas');

// Context for the canvas for 2 dimensional operations
const ctx = canvas.getContext('2d');

// Resizes the canvas to the available size of the window.
function resize() {
  ctx.canvas.width = canvas.offsetWidth;
  ctx.canvas.height = canvas.offsetHeight;
}

// Stores the initial position of the cursor
let coord = {
  x: 0,
  y: 0
};

// This is the flag that we are going to use to
// trigger drawing
let paint = false;

// Updates the coordianates of the cursor when
// an event e is triggered to the coordinates where
// the said event is triggered.
function getPosition(event) {
  coord.x = event.clientX - canvas.offsetLeft;
  coord.y = event.clientY - canvas.offsetTop;
}

// The following functions toggle the flag to start
// and stop drawing
function startPainting(event) {
  paint = true;
  getPosition(event);
}

function stopPainting() {
  paint = false;
}

function sketch(event) {
  if (!paint) return;
  ctx.beginPath();

  ctx.lineWidth = lineThickess;

  // Sets the end of the lines drawn
  // to a round shape.
  ctx.lineCap = 'round';

  ctx.strokeStyle = lineColor;

  // The cursor to start drawing
  // moves to this coordinate
  ctx.moveTo(coord.x, coord.y);

  // The position of the cursor
  // gets updated as we move the
  // mouse around.
  getPosition(event);

  // A line is traced from start
  // coordinate to this coordinate
  ctx.lineTo(coord.x, coord.y);

  // Draws the line.
  ctx.stroke();
}
html,
body {
  height: 100%;
}

canvas {
  width: 100%;
  height: 100%;
  border: 1px solid;
}
<canvas id="canvas"></canvas>


The clue

Stack overflow recommended me reviewing other posts related, and I found a clue,and code

But

enter image description here
As shown (and said in the answer), two different paths work well, but loops on same paths don't work as needed

1 Answers

The issue you are seeing is because the intersection of two lines with transparency the result is not the same transparent color, just like in the real world if you hold two sunglasses on top of each other the resulting color is not the same as the individual ones.

What you should do is use Path2D to get the same transparency over the entire object, it could be lines, arches rectangles or any other items, if they are under a common Path2D they all get the same color even on intersections.

Below is a very simple example based on your code
This is how it looks like when the lines cross:

let path = new Path2D()
document.addEventListener('mousemove', sketch)

const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
ctx.lineWidth = 10
ctx.lineCap = 'round'
ctx.strokeStyle = 'rgba(255,0,255,0.3)'

function sketch(event) {
  let x = event.clientX - canvas.offsetLeft
  let y = event.clientY - canvas.offsetTop
  path.lineTo(x, y)
}

function drawCircles() {
  for (let i = 25; i<=100; i+=25) 
    ctx.arc(i*2, i, 5, 0, 8)
  ctx.fill()
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  drawCircles()  
  ctx.stroke(path)
  requestAnimationFrame(draw)
}
draw()
<canvas id="canvas" width=300 height=150></canvas>


Another example, a bit more complex this time, using an array of Path2D objects, and added a button to change colors, the creation of the color add a new item to the array:

let paths = []
paths.unshift({path: new Path2D(), color:'rgba(255,0,255,0.3)'})
document.addEventListener('mousemove', sketch)

const canvas = document.querySelector('#canvas')
const ctx = canvas.getContext('2d')
ctx.lineWidth = 10
ctx.lineCap = 'round'

function changeColor() {
  let newColor = 'rgba(0,255,255,0.3)'
  if (paths[0].color == newColor) 
    newColor = 'rgba(255,0,255,0.3)'
  paths.unshift({path:new Path2D(), color: newColor})
}

function sketch(event) {
  let x = event.clientX - canvas.offsetLeft
  let y = event.clientY - canvas.offsetTop
  paths[0].path.lineTo(x, y)
}

function drawCircles() {
  for (let i = 25; i<=100; i+=25)
    ctx.arc(i*2, i, 5, 0, 8)
  ctx.fill()
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  drawCircles()  
  paths.forEach(e => {
    ctx.strokeStyle = e.color
    ctx.stroke(e.path)
  })
  requestAnimationFrame(draw)
}
draw()
<canvas id="canvas" width=250 height=150></canvas>
<button onclick="changeColor()">Change Color</button>

This is how it looks like after changing colors a few times:

Now you have all the tools you need...
It's up to you to decide what is the end of a path and the start of a new one.

Related