I'm drawing with a semi-transparent color (e.g. rgba(255,0,0,0.5)) over a canvas. When I draw over the same region again, the transparency value seems to be adding up resulting in an opaque color. Is there a way to keep the transparency value of the source (the semi-transparent color I'm using to draw) instead?
Draw to an offscreen canvas with alpha = 1.Then just render the off screen canvas to the display canvas with the ctx.globalAlpha set at whatever value you wish. That way you can draw till the sun goes down without adding anything to the alpha. It is also easy to change the alpha after you have drawn if needed.
Additional note
If you have other content included in the image, you will have to keep that on another layer as well because this method relies on the onscreen canvas being reset to a desired starting state for each update. In the snippet this is just a clearRect call. But can just as well be replaced with another existing layer, or a combination there of.
The browser can easily handle many off screen canvases, I just finished a job that had 60 full screen canvas stacked on top of each other (Note your GPU needs to have the RAM to hold the images or it's too slow) and Chrome did not even blink. Firefox and IE are just as capable.
UPDATE
I have added a snippet to demonstrate what I mean. Details in the comments of the relevant code at the bottom. Just a simple drawing interface.
// get canvas set up mouse and do the other things
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
var mouse = {
x:0,
y:0,
buttonLastRaw:0, // user modified value
buttonRaw:0,
over:false,
};
function mouseMove(event){
mouse.x = event.offsetX; mouse.y = event.offsetY;
if(mouse.x === undefined){ mouse.x = event.clientX; mouse.y = event.clientY;}
if(event.type === "mousedown"){ mouse.buttonRaw = 1;
}else if(event.type === "mouseup"){mouse.buttonRaw = 0;
}else if(event.type === "mouseout"){ mouse.buttonRaw = 0; mouse.over = false;
}else if(event.type === "mouseover"){ mouse.over = true; }
event.preventDefault();
}
canvas.addEventListener('mousemove',mouseMove);
canvas.addEventListener('mousedown',mouseMove);
canvas.addEventListener('mouseup' ,mouseMove);
canvas.addEventListener('mouseout' ,mouseMove);
canvas.addEventListener('mouseover' ,mouseMove);
canvas.addEventListener("contextmenu", function(e){ canvas.preventDefault();}, false);
// create off screen layer that we will draw to
var layer1 = document.createElement("canvas");
layer1.width = w; // same size as the onscreen canvas
layer1.height = h;
layer1.ctx = layer1.getContext("2d");
// set up drawing settings
layer1.ctx.lineCap = "round";
layer1.ctx.lineJoin = "round";
layer1.ctx.lineWidth = 16;
layer1.ctx.globalAlpha = 1; // draw to this layer with alpha set to 1;
// set up onscreen canvas
ctx.globalAlpha = 1;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "24px Arial black";
var instructions = true;
// colours to show that different layer are overwriting each other
var colours = "#F00,#FF0,#0F0,#0FF,#00F,#F0F".split(",");
var currentCol = 0;
// update on animation frame
function update(){
ctx.clearRect(0,0,w,h); // clear onscreen
var c = layer1.ctx; // short cut to the later1 context
if(mouse.buttonRaw){ // if mouse down
if(mouse.lastx === undefined){ // is this start of drawing stroke
mouse.lastx = mouse.x; // set up drawing stroke
mouse.lasty = mouse.y;
c.strokeStyle = colours[currentCol % colours.length];
currentCol += 1;
instructions = false; // tuen of the instructions as they have worked it out
ctx.globalAlpha = 0.6; // should do this near layering but lasy
}
// draw the dragged stroke to the offscreen layer
c.beginPath();
c.moveTo(mouse.lastx,mouse.lasty);
c.lineTo(mouse.x,mouse.y);
c.stroke();
mouse.lastx = mouse.x;
mouse.lasty = mouse.y;
}else{ // if the mouse button up show drawing brush and instructions if
// nothing has happened yet
mouse.lastx = undefined; // using this as a semaphore for drag start
ctx.fillStyle = colours[currentCol%colours.length];
ctx.globalAlpha = 0.6; // the brush will compound the alpha
// this can be avoided by drawing it onto
// the offscreen layer, but you will need
// another layer or some temp store to
// protect the offscreen layer. Again I am
// to lazy to implement that right now.
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,8,0,Math.PI*2);
ctx.fill();
if(instructions){ // show instructions if needed
ctx.fillStyle = "blue";
ctx.globalAlpha = 1;
ctx.fillText("Click drag mouse to draw",250,60);
}
}
// draw the offscreen layer onto the onscreen canvas at the alpha wanted
ctx.drawImage(layer1,0,0);
requestAnimationFrame(update); // do it all again.
}
mouse.lastx; // needed to draw lines.
mouse.lasty;
update()
body { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAlUlEQVRYR+2WsQ0EIQwEbXpAopbrAZESUhQ1AAkBXVEDAb6jBRP8B0s+yJpklnvvstYizRMRyjmTtVaD096buNYqzjnVB3NOaq3RGEPFhxBwAAzAAAzAAAz8gYFSijCzqmYH+ngyxqj4k3N+nkduep5Sops9wV+T5abnMUa62RM4AAZgAAZgAAZ+b8B7Lzc9PzW82RMvg0g+JLdy9xIAAAAASUVORK5CYII=');
background-size: 32px 32px;
background-repeat: repeat;
}
.canC { width:500px; height:600px;}
<canvas class="canC" id="canV" width=500 height=600></canvas>
Related
I'm drawing with a semi-transparent color (e.g. rgba(255,0,0,0.5)) over a canvas. When I draw over the same region again, the transparency value seems to be adding up resulting in an opaque color. Is there a way to keep the transparency value of the source (the semi-transparent color I'm using to draw) instead?
Draw to an offscreen canvas with alpha = 1.Then just render the off screen canvas to the display canvas with the ctx.globalAlpha set at whatever value you wish. That way you can draw till the sun goes down without adding anything to the alpha. It is also easy to change the alpha after you have drawn if needed.
Additional note
If you have other content included in the image, you will have to keep that on another layer as well because this method relies on the onscreen canvas being reset to a desired starting state for each update. In the snippet this is just a clearRect call. But can just as well be replaced with another existing layer, or a combination there of.
The browser can easily handle many off screen canvases, I just finished a job that had 60 full screen canvas stacked on top of each other (Note your GPU needs to have the RAM to hold the images or it's too slow) and Chrome did not even blink. Firefox and IE are just as capable.
UPDATE
I have added a snippet to demonstrate what I mean. Details in the comments of the relevant code at the bottom. Just a simple drawing interface.
// get canvas set up mouse and do the other things
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
var mouse = {
x:0,
y:0,
buttonLastRaw:0, // user modified value
buttonRaw:0,
over:false,
};
function mouseMove(event){
mouse.x = event.offsetX; mouse.y = event.offsetY;
if(mouse.x === undefined){ mouse.x = event.clientX; mouse.y = event.clientY;}
if(event.type === "mousedown"){ mouse.buttonRaw = 1;
}else if(event.type === "mouseup"){mouse.buttonRaw = 0;
}else if(event.type === "mouseout"){ mouse.buttonRaw = 0; mouse.over = false;
}else if(event.type === "mouseover"){ mouse.over = true; }
event.preventDefault();
}
canvas.addEventListener('mousemove',mouseMove);
canvas.addEventListener('mousedown',mouseMove);
canvas.addEventListener('mouseup' ,mouseMove);
canvas.addEventListener('mouseout' ,mouseMove);
canvas.addEventListener('mouseover' ,mouseMove);
canvas.addEventListener("contextmenu", function(e){ canvas.preventDefault();}, false);
// create off screen layer that we will draw to
var layer1 = document.createElement("canvas");
layer1.width = w; // same size as the onscreen canvas
layer1.height = h;
layer1.ctx = layer1.getContext("2d");
// set up drawing settings
layer1.ctx.lineCap = "round";
layer1.ctx.lineJoin = "round";
layer1.ctx.lineWidth = 16;
layer1.ctx.globalAlpha = 1; // draw to this layer with alpha set to 1;
// set up onscreen canvas
ctx.globalAlpha = 1;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "24px Arial black";
var instructions = true;
// colours to show that different layer are overwriting each other
var colours = "#F00,#FF0,#0F0,#0FF,#00F,#F0F".split(",");
var currentCol = 0;
// update on animation frame
function update(){
ctx.clearRect(0,0,w,h); // clear onscreen
var c = layer1.ctx; // short cut to the later1 context
if(mouse.buttonRaw){ // if mouse down
if(mouse.lastx === undefined){ // is this start of drawing stroke
mouse.lastx = mouse.x; // set up drawing stroke
mouse.lasty = mouse.y;
c.strokeStyle = colours[currentCol % colours.length];
currentCol += 1;
instructions = false; // tuen of the instructions as they have worked it out
ctx.globalAlpha = 0.6; // should do this near layering but lasy
}
// draw the dragged stroke to the offscreen layer
c.beginPath();
c.moveTo(mouse.lastx,mouse.lasty);
c.lineTo(mouse.x,mouse.y);
c.stroke();
mouse.lastx = mouse.x;
mouse.lasty = mouse.y;
}else{ // if the mouse button up show drawing brush and instructions if
// nothing has happened yet
mouse.lastx = undefined; // using this as a semaphore for drag start
ctx.fillStyle = colours[currentCol%colours.length];
ctx.globalAlpha = 0.6; // the brush will compound the alpha
// this can be avoided by drawing it onto
// the offscreen layer, but you will need
// another layer or some temp store to
// protect the offscreen layer. Again I am
// to lazy to implement that right now.
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,8,0,Math.PI*2);
ctx.fill();
if(instructions){ // show instructions if needed
ctx.fillStyle = "blue";
ctx.globalAlpha = 1;
ctx.fillText("Click drag mouse to draw",250,60);
}
}
// draw the offscreen layer onto the onscreen canvas at the alpha wanted
ctx.drawImage(layer1,0,0);
requestAnimationFrame(update); // do it all again.
}
mouse.lastx; // needed to draw lines.
mouse.lasty;
update()
body { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAlUlEQVRYR+2WsQ0EIQwEbXpAopbrAZESUhQ1AAkBXVEDAb6jBRP8B0s+yJpklnvvstYizRMRyjmTtVaD096buNYqzjnVB3NOaq3RGEPFhxBwAAzAAAzAAAz8gYFSijCzqmYH+ngyxqj4k3N+nkduep5Sops9wV+T5abnMUa62RM4AAZgAAZgAAZ+b8B7Lzc9PzW82RMvg0g+JLdy9xIAAAAASUVORK5CYII=');
background-size: 32px 32px;
background-repeat: repeat;
}
.canC { width:500px; height:600px;}
<canvas class="canC" id="canV" width=500 height=600></canvas>
I am working on ionic based application.
I want to functionality like user has Filled the red color on image(canvas) with finger. So I have done the filled functionality but I want to crop the Filled portion from canvas. I have attached one image for reference.
I want to crop red portion from above image. I has googling but not found any solution.
Creating a image mask.
If you are rendering the selection area (red) then the solution is simple.
Create a second canvas the same size as the drawing, and don't add it to the DOM. Draw the red marking content onto that canvas
The on the display canvas render the image first and then render that marking
canvas over the image with composite mode like "overlay" so that the original image can be seen and the marked areas are red.
Now you have two layers, one is the image and the other the mask you can use to get a copy of the marked content.
To do that create a 3rd canvas, draw the original image onto it, then set the composite mode to "destination-in". Then draw the mask over it. Only the marked pixels will remain.
See the example for more details
setTimeout(example,0); // ensures that the run us after parsing
function example(){
const ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var selectLayer = CImageCtx(w,h); // creates a canvas
var selectedContent = CImageCtx(w,h); // the selected content
document.body.appendChild(selectedContent);
var image = new Image; // the image
image.src = " https://i.stack.imgur.com/QhFct.png";
// updates the masked result
function updateSelected(){
var ctx = selectedContent.ctx;
ctx.drawImage(image,0,0);
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(selectLayer,0,0);
ctx.globalCompositeOperation = "source-over";
}
function update(){
// if mouse down then
if(mouse.but){
// clear the mask if on the right image
if(mouse.oldBut === false && mouse.x > 256){
selectLayer.ctx.clearRect(0,0,w,h);
mouse.but = false;
}else{
// draw the red
selectLayer.ctx.fillStyle = "red";
fillCircle(mouse.x, mouse.y, 20, selectLayer.ctx);
}
// update the masked result
updateSelected();
}
// clear the canvas
ctx.clearRect(0,0,w,h);
// draw the image
ctx.drawImage(image,0,0);
// then draw the marking layer over it with comp overlay
ctx.globalCompositeOperation = "overlay";
ctx.drawImage(selectLayer,0,0);
ctx.globalCompositeOperation = "source-over";
mouse.oldBut = mouse.but;
requestAnimationFrame(update);
}
requestAnimationFrame(update);
}
//#############################################################################
// helper functions not part of the answer
//#############################################################################
const mouse = {
x : 0, y : 0, but : false,
events(e){
const m = mouse;
const bounds = canvas.getBoundingClientRect();
m.x = e.pageX - bounds.left - scrollX;
m.y = e.pageY - bounds.top - scrollY;
m.but = e.type === "mousedown" ? true : e.type === "mouseup" ? false : m.but;
}
};
(["down","up","move"]).forEach(name => document.addEventListener("mouse" + name,mouse.events));
const CImage = (w = 128, h = w) => (c = document.createElement("canvas"),c.width = w,c.height = h, c);
const CImageCtx = (w = 128, h = w) => (c = CImage(w,h), c.ctx = c.getContext("2d"), c);
const fillCircle = (l,y=ctx,r=ctx,c=ctx) =>{if(l.p1){c=y; r=leng(l);y=l.p1.y;l=l.p1.x }else if(l.x){c=r;r=y;y=l.y;l=l.x}c.beginPath(); c.arc(l,y,r,0,Math.PI*2); c.fill()}
body { font-family : arial; }
canvas { border : 2px solid black; }
Draw on image and the selected parts are shown on the right<br>
Click right image to reset selection<br>
<canvas id="canvas" width=256 height=256></canvas>
Already masked.
If the red mask is already applied to the image then there is not much you can do apart from do a threshold filter depending on how red the image is. But even then you are going to have problems with darker areas, and areas that already contain red.
Unless you have the original image you will have poor results.
If you have the original image then you will have to access the image data and create a new image as a mask by comparing each pixel and selecting only pixels that are different. That will force you to same domain images only (or with CORS cross origin headers)
I am trying to create a pannable image viewer which also allows magnification. If the zoom factor or the image size is such that the image no longer paints over the entire canvas then I wish to have the area of the canvas which does not contain the image painted with a specified background color.
My current implementation allows for zooming and panning but with the unwanted effect that the image leaves a tiled trail after it during a pan operation (much like the cards in windows Solitaire when you win a game). How do I clean up my canvas such that the image does not leave a trail and my background rectangle properly renders in my canvas?
To recreate the unwanted effect set magnification to some level at which you see the dark gray background show and then pan the image with the mouse (mouse down and drag).
Code snippet added below and Plnkr link for those who wish to muck about there.
http://plnkr.co/edit/Cl4T4d13AgPpaDFzhsq1
<!DOCTYPE html>
<html>
<head>
<style>
canvas{
border:solid 5px #333;
}
</style>
</head>
<body>
<button onclick="changeScale(0.10)">+</button>
<button onclick="changeScale(-0.10)">-</button>
<div id="container">
<canvas width="700" height="500" id ="canvas1"></canvas>
</div>
<script>
var canvas = document.getElementById('canvas1');
var context = canvas.getContext("2d");
var imageDimensions ={width:0,height:0};
var photo = new Image();
var isDown = false;
var startCoords = [];
var last = [0, 0];
var windowWidth = canvas.width;
var windowHeight = canvas.height;
var scale=1;
photo.addEventListener('load', eventPhotoLoaded , false);
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function eventPhotoLoaded(e) {
imageDimensions.width = photo.width;
imageDimensions.height = photo.height;
drawScreen();
}
function changeScale(delta){
scale += delta;
drawScreen();
}
function drawScreen(){
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
canvas.onmousedown = function(e) {
isDown = true;
startCoords = [
e.offsetX - last[0],
e.offsetY - last[1]
];
};
canvas.onmouseup = function(e) {
isDown = false;
last = [
e.offsetX - startCoords[0], // set last coordinates
e.offsetY - startCoords[1]
];
};
canvas.onmousemove = function(e)
{
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
context.setTransform(1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]);
drawScreen();
}
</script>
</body>
</html>
You need to reset the transform.
Add context.setTransform(1,0,0,1,0,0); just before you clear the canvas and that will fix your problem. It sets the current transform to the default value. Then befor the image is draw set the transform for the image.
UPDATE:
When interacting with user input such as mouse or touch events it should be handled independently of rendering. The rendering will fire only once per frame and make visual changes for any mouse changes that happened during the previous refresh interval. No rendering is done if not needed.
Dont use save and restore if you don't need to.
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext("2d");
var photo = new Image();
var mouse = {}
mouse.lastY = mouse.lastX = mouse.y = mouse.x = 0;
mouse.down = false;
var changed = true;
var scale = 1;
var imageX = 0;
var imageY = 0;
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function changeScale(delta){
scale += delta;
changed = true;
}
// Turns mouse button of when moving out to prevent mouse button locking if you have other mouse event handlers.
function mouseEvents(event){ // do it all in one function
if(event.type === "mouseup" || event.type === "mouseout"){
mouse.down = false;
changed = true;
}else
if(event.type === "mousedown"){
mouse.down = true;
}
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if(mouse.down) {
changed = true;
}
}
canvas.addEventListener("mousemove",mouseEvents);
canvas.addEventListener("mouseup",mouseEvents);
canvas.addEventListener("mouseout",mouseEvents);
canvas.addEventListener("mousedown",mouseEvents);
function update(){
requestAnimationFrame(update);
if(photo.complete && changed){
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle="#333";
ctx.fillRect(0,0, canvas.width, canvas.height);
if(mouse.down){
imageX += mouse.x - mouse.lastX;
imageY += mouse.y - mouse.lastY;
}
ctx.setTransform(scale, 0, 0, scale, imageX,imageY);
ctx.drawImage(photo,0,0);
changed = false;
}
mouse.lastX = mouse.x
mouse.lastY = mouse.y
}
requestAnimationFrame(update);
canvas{
border:solid 5px #333;
}
<button onclick="changeScale(0.10)">+</button><button onclick="changeScale(-0.10)">-</button>
<canvas width="700" height="500" id ="canvas1"></canvas>
Nice Code ;)
You are seeing the 'tiled' effect in your demonstration because you are painting the scaled image to the canvas on top of itself each time the drawScreen() function is called while dragging. You can rectify this in two simple steps.
First, you need to clear the canvas between calls to drawScreen() and second, you need to use the canvas context.save() and context.restore() methods to cleanly reset the canvas transform matrix between calls to drawScreen().
Given your code as is stands:
Create a function to clear the canvas. e.g.
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
In the canavs.onmousemove() function, call clearCanvas() and invoke context.save() before redefining the transform matrix...
canvas.onmousemove = function(e) {
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
/* !!! */
clearCanvas();
context.save();
context.setTransform(
1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]
);
drawScreen();
}
... then conditionally invoke context.restore() at the end of drawScreen() ...
function drawScreen() {
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
/* !!! */
if (isDown) context.restore();
}
Additionally, you may want to call clearCanvas() before rescaling the image, and the canvas background could be styled with CSS rather than .fillRect() (in drawScreen()) - which could give a performance gain on low spec devices.
Edited in light of comments from Blindman67 below
See Also
Canvas.context.save : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
Canvas.context.restore : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
requestAnimationFrame : https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
Paul Irish, requestAnimationFrame polyfill : http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
Call context.save to save the transformation matrix before you call context.fillRect.
Then whenever you need to draw your image, call context.restore to restore the matrix.
For example:
function drawScreen(){
context.save();
context.fillStyle="#333333";
context.fillRect(0,0, windowWidth, windowHeight);
context.restore();
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
Also, to further optimize, you only need to set fillStyle once until you change the size of canvas.
Ok, I've noticed this bug happen not only in the following code(which is just to illustrate), but every code I've written for canvas;
basically, in chrome(I didn't test other browsers),
once you shift your current tab to either a different pc screen, or simply if you have a few tabs in a row and decide to make a new window out of your current tab,
the canvas itself fails to draw. it is stuck and no reason is given in the console.
Here is a GIF of it happening:
http://gifmaker.cc/PlayGIFAnimation.php?folder=2016081601ZEuvXaZnxC2T9fHNNqSW3l&file=output_f72O1H.gif
Just a simple canvas code if you want to try it out:
<canvas id="canvas" width="500" height="400" style="border:1px solid #000000;"></canvas>
<script>
// grab the canvas and context
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// inital coordinates of the black square
var x = 0;
var y = 200;
// speed of the movement in x- and y-direction
// actual no movement in y-direction
var vX = 1;
var vY = 0;
// width and height of the square
var width = 10;
var height = 10;
function animate() {
// clear
// comment this clear function to see the plot of the sine movement
ctx.clearRect(0, 0, canvas.width, canvas.height);
x += vX;
y += vY;
// if the block leaves the canvas on the right side
// bring it back to the left side
if(x>500) {
x = 0;
}
ctx.fillRect(x, y, width, height);
setTimeout(animate, 33);
}
// call the animate function manually for the first time
animate();
</script>
Some sites fixed this bug but I don't know if I am allowed to link them here.
Iām trying to use a drop down menu to change the shape of a brush from round to square in a paint program using Canvas.
Here is what I have so far in this Fiddle.
https://jsfiddle.net/ohdust/k7wzj3ww/2/
var tool = false;
var toolDefault = 'rect';
var toolSelect = document.getElementById('dtool');
I'm not sure how to go at this. Any examples would be helpful.
I've tried searching around but have not had any luck.
Define a function for each pencil types, for example:
function setRound() {
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.lineJoin = "round";
ctx.strokeStyle = '#2b39c0';
}
function setSquare() {
ctx.lineWidth = 5;
ctx.lineCap = 'butt';
ctx.lineJoin = "miter";
ctx.strokeStyle = '#c0392b';
}
You would also have to remove setting the stroke style from the mouse handlers (see fiddle for additions to the resize handler as well as the currentTool declaration used below).
(if you have many different styles I would suggest considering at least an array and custom pen objects).
Then use a switch selector when an event on the tool selector is triggered:
toolSelect.addEventListener('change', setPencil);
...
function setPencil() {
switch(this.value) {
case "rect":
currentTool = setSquare; break;
case "pencil":
currentTool = setRound; break;
}
currentTool();
}
Now the pencil will be updated according to the selected pencil in the menu.
Additionally, mouse position needs to be corrected - just add this to adjust:
function setPosition(e) {
var rect = canvas.getBoundingClientRect();
pos.x = e.clientX - rect.left;
pos.y = e.clientY - rect.top;
}
Updated fiddle
I have a small feeling you maybe ask for how to draw a rectangle and not a squared tip line. If so, check out this answer.
I used the onchange attribute to run a function to change the type of brush. There is no rectangle shape for a brush as of now. See here.
Note: Other improvements need to be done in your code. For example the drawing is occurring far away from the cursor. And on click and move the browser is trying to drag things by default. So I guess you have to add a mousemove function to the canvas with event.preventDefault().
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var toolSelect = document.getElementById('dtool');
var brush = {};
brush.shape = 'round';
brush.size = 1;
function setBrush(type) {
switch (type) {
case 'pencil':
brush.shape = 'round';
brush.size = 1;
break;
case 'square':
brush.shape = 'square';
brush.size = 10;
break;
}
}
// some hotfixes... ( ā_ā)
document.body.style.margin = 0;
canvas.style.position = 'fixed';
// get canvas 2D context and set him correct size
var ctx = canvas.getContext('2d');
resize();
// last known position
var pos = {
x: 0,
y: 0
};
window.addEventListener('resize', resize);
document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);
// new position from mouse event
function setPosition(e) {
pos.x = e.clientX;
pos.y = e.clientY;
}
// resize canvas
function resize() {
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
}
function draw(e) {
// mouse left button must be pressed
if (e.buttons !== 1) return;
ctx.beginPath(); // begin
ctx.lineWidth = brush.size;
ctx.lineCap = brush.shape;
ctx.strokeStyle = '#c0392b';
ctx.moveTo(pos.x, pos.y); // from
setPosition(e);
ctx.lineTo(pos.x, pos.y); // to
ctx.stroke(); // draw it!
}
<label>Drawing tool:
<select id="dtool" onchange="setBrush(this.value)">
<option value="">Select</option>
<option value="square">Square</option>
<option value="pencil">Pencil</option>
</select>
</label>