Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing overlapping semi-transparent lines without visible overlap

I'm developing a painter program using HTML5 canvas. I have created a drawing tool where the user drags and moves the mouse.

I have a listener on mousemove event that draws short lines:

Painter.mainCanvas.beginPath();
Painter.mainCanvas.moveTo(Painter.lastX, Painter.lastY);
Painter.lastX = e.offsetX;
Painter.lastY = e.offsetY;
Painter.mainCanvas.lineTo(Painter.lastX, Painter.lastY);
Painter.mainCanvas.stroke();

Everything works well until I set the global Alpha to < 1. When using this method to draw, the end dot is also start dot. So the dot is drawn twice. And because we have transparent color, the dot now has different color with other dots in line.

I tried another method that when mousemove fires, it only uses lineTo() and stroke() when mouseup fires.

This solves the double drawing problem, but also introduces a new problem: when user intend to draw same dot twice, ie, cross line without mouseup, the dot won't be drawn twice. Because lineTo() function won't draw a dot twice without stroke between.

like image 344
liuyanghejerry Avatar asked Dec 07 '11 09:12

liuyanghejerry


1 Answers

(Restating your problem) Your original problem was that by calling beginPath() and stroke() for each segment you had many overlapping semi-transparent paths. Your new "problem" is that by creating all your lineTo() commands as part of the same path and then calling stroke() once at the end any self-intersecting paths intended by the user do not show a visible overlap.

Here is an example showing the difference between making

  • many semi-transparent lines in a single path and stroking once (upper left), versus
  • many semi-transparent lines in distinct paths and stroking each (bottom right)

                        http://jsfiddle.net/jhyG5/2/

A semi-transparent set of crossing lines (all the same opacity) in the upper left and a set of crossing lines with increasing opacity where they cross in the bottom right.

I would say that your current solution (a single path) is the correct way to do it, even though a single self-crossing path does not double-up in opacity. This is what you see in Adobe Photoshop and Illustrator when drawing semi-transparent paths: all drawing with the mouse down is part of the same, single, non-overlapping transparent object. Only when the user releases and re-presses the mouse button do you accumulate more transparency:

  • Two Photoshop paintbrush strokes at 50% opacity:
    Photoshop Overlapping Style

  • Two Illustrator paths at 50% opacity: Illustrator Overlapping Strokes

Notice in particular that the self-intersecting stroke and path do not show double the opacity during crossing, but that a separate new path does.

I recommend that you stick with your current solution given that this is how these traditional, well-thought-out applications behave. I say this both because you want your package to mimic user expectations, and also because if these packages do it like this, there's probably a very good reason for it: the exact problem you originally had! :)

like image 176
Phrogz Avatar answered Sep 19 '22 05:09

Phrogz