HTML5 Canvas eraser tool without overdraw white color
Your idea of using compositing to create an eraser is a good idea.
destination-out
will remove existing drawings where a new drawing overlaps those existing drawings.
var canvas=document.getElementById("canvas");var ctx=canvas.getContext("2d");var lastX;var lastY;var strokeColor="red";var strokeWidth=5;var mouseX;var mouseY;var canvasOffset=$("#canvas").offset();var offsetX=canvasOffset.left;var offsetY=canvasOffset.top;var isMouseDown=false;
function handleMouseDown(e){ mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here lastX=mouseX; lastY=mouseY; isMouseDown=true;}
function handleMouseUp(e){ mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY);
// Put your mouseup stuff here isMouseDown=false;}
function handleMouseOut(e){ mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY);
// Put your mouseOut stuff here isMouseDown=false;}
function handleMouseMove(e){ mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY);
// Put your mousemove stuff here if(isMouseDown){ ctx.beginPath(); if(mode=="pen"){ ctx.globalCompositeOperation="source-over"; ctx.moveTo(lastX,lastY); ctx.lineTo(mouseX,mouseY); ctx.stroke(); }else{ ctx.globalCompositeOperation="destination-out"; ctx.arc(lastX,lastY,8,0,Math.PI*2,false); ctx.fill(); } lastX=mouseX; lastY=mouseY; }}
$("#canvas").mousedown(function(e){handleMouseDown(e);});$("#canvas").mousemove(function(e){handleMouseMove(e);});$("#canvas").mouseup(function(e){handleMouseUp(e);});$("#canvas").mouseout(function(e){handleMouseOut(e);});
var mode="pen";$("#pen").click(function(){ mode="pen"; });$("#eraser").click(function(){ mode="eraser"; });
body{ background-color: ivory; }canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script><canvas id="canvas" width=300 height=300></canvas></br><button id="pen">Pen</button><button id="eraser">Eraser</button>
Erasing in html5 canvas
If you want to draw a black transparent stroke, you probably want:
context.globalCompositeOperation = "destination-out";
context.strokeStyle = "rgba(0,0,0,1)";
Remember to save the previous globalCompositeOperation and then restore it later or transparency won't work properly!
Add erase ink functionality in HTML5 canvas whiteboard
Try using a brush the same color as the background color of the canvas. It will look like erasing, but it's like putting white-out on white paper.
sugs on your code:
use let
instead of var
for onPaint
, do function onPaint() {...}
instead.
Eraser tool in html5 canvas
Use multiple layers. Have one canvas for the background image and another for the drawing; that why you never erase any of the background image.
If you need to, you can have multiple layers as they don't generally impact performance.
And of course if you can combine layers, say the last drawn squiggle to the background layer, if you deem a drawing to be "permanent".
How to clear the canvas for redrawing
Given that canvas
is a canvas element or an OffscreenCanvas
object, use clearRect
:
const context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
Erasing Parts of an Image on HTML5 Canvas?
You can use Compositing to "erase" pixels.
Specifically you use destination-out
compositing.
KineticJS does not support compositing, but you still have a couple of options:
(Note: KineticJS has become KonvaJS and I haven't checked whether KonvaJs supports compositing. If it now does, just use destination-out
compositing inside KonvaJS)
Option#1: Use a native canvas element as your Kinetic.Image source
Create an in-memory html5 canvas using
var c=document.createElement
,Resize the canvas to image size,
drawImage
your image onto the canvas,Create a
Kinetic.Image
and set its image property to a reference to the native canvas. The Kinetic.Image will display whatever is drawn onto the native canvas.var kImage=new Kinetic.Image({
...
image:c,
...Set the canvas Compositing to cause new drawings to "erase" existing pixels:
c.globalCompositeOperation='destination-out';
Listen for drag events on your circle-eraser. Use those events to draw a circle on the canvas that move just like the Kinetic circle-eraser moves. Since the canvas's compositing is set to "erase", new drawings of the circle on the canvas will erase the image on the canvas.
Your Kinetic.Image exactly reflects its canvas source (var c), so your Kinetic.Image will also display the image being erased in response to the Kinetic circle-eraser movements.
Option#2: Use a Kinetic.Shape
You can do the same operation as Option#1 by creating a Kinetic.Shape on a separate layer and getting a reference to the native canvas context using:
var ctx = myShapeLayer.getContext()._context;
This is a weaker option because KineticJS will redraw the shape--causing your erasing to be undone. Therefore you must do the additional step of saving all your circle-eraser's movements and replaying those movements (in drawFunc
) to redo your erasing.
Related Topics
Webgl - Wait for Texture to Load
Sticky Header and Footer Scrollable Content
How to Make a Fixed Column in CSS Using CSS Grid Layout
Valid Order for Attributes of Input Type Tag
Display: Flex Not Working on Internet Explorer
Getting Rid of Bullet Points from <Ul>
When Multiple Instances of Same Images Are Embedded in an HTML, Does That Load The Image Once
Overflow-Y:Visible Not Working When Overflow-X:Hidden Is Present
Block Level Elements Inside Inline Elements
Is There a Vr (Vertical Rule) in HTML
Difference Between HTML Link Media and CSS Media Queries
How to Make a 45 Degree Responsive Ribbon with Folded Corner
Angular Performance: Dom Event Causes Unnecessary Function Calls