Canvas.Context Restore on mousemove is failing HTML5 - javascript

I'm trying to create a hover effect for a canvas image when a mousemove over the image, a transparent overlay shows up. Once a user mouseout am trying to restore the canvas back to it initial stage by making use of the Canvas restore method but it keeps failing. Below is the whole code
var images = [];
var halfCircle;
var ctx;
var canvas;
var effect = true;
jQuery(document).ready(function() {
canvas = document.getElementById("myCanvas");
canvas.style.backgroundColor = '#fafafa';
ctx = canvas.getContext("2d");
halfCircle = new HalfCircle();
halfCircle.doArch(ctx);
placeImages(ctx);
addEventListenersToCanvas(canvas, ctx);
});
function placeImages(ctx){
first_image = new Image();
first_image.src = 'http://example.com/media/features/0.png';
first_image.onload = function(){
ctx.drawImage(first_image, 20, 20);
images.push({x:20,y:20,link: "http://example.com/shoppinglist-infographic", img : first_image});
ctx.save();
}
second_image = new Image();
second_image.src = "http://example.com/media/features/1.png";
second_image.onload = function(){
ctx.drawImage(second_image, 130, 150);
images.push({x:130,y:150,link: "http://example.com/referral/invite?g=banner", img : second_image});
ctx.save();
}
third_image = new Image();
third_image.src = "http://example.com/media/features/2.png";
third_image.onload = function(){
ctx.drawImage(third_image, 230, 220);
images.push({x:230,y:220,link: "http://example.com/all-fast-delivery/", img : third_image});
ctx.save();
}
fourth_image = new Image();
fourth_image.src = "http://example.com/media/features/3.png";
fourth_image.onload = function(){
ctx.drawImage(fourth_image,460, 220);
images.push({x:460,y:220,link:"http://example.com/busyhomemaker/", img : fourth_image});
ctx.save();
}
fifth_image = new Image();
fifth_image.src = "http://example.com/media/features/4.png";
fifth_image.onload = function(){
ctx.drawImage(fifth_image,570, 150);
images.push({x:570,y:150,link:"#", img: fifth_image});
ctx.save();
}
sixth_image = new Image();
sixth_image.src = "http://example.com/media/features/5.png";
sixth_image.onload = function(){
ctx.drawImage(sixth_image,620, 20);
images.push({x:620,y:20,link:"#", img:sixth_image});
ctx.save();
}
text_image = new Image();
text_image.src = "http://example.com/media/features/text.png";
text_image.onload = function(){
ctx.drawImage(text_image,285, 20);
ctx.save();
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
function addEventListenersToCanvas(canvas, ctx){
ctx.save();
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
for(var i = 0; i < images.length; i++){
if( (mousePos.x > images[i].x) && (mousePos.x < (images[i].x + images[i].img.width)) &&
(mousePos.y > images[i].y) && (mousePos.y < (images[i].y + images[i].img.height))
){
document.body.style.cursor = "pointer";
if(effect) {
ctx.fillStyle = "#fafafa";
ctx.globalAlpha = 0.1;
ctx.fillRect(images[i].x, images[i].y, images[i].img.width, images[i].img.height);
effect = false;
}
}else{
document.body.style.cursor = "auto";
ctx.restore();
effect = true;
}
}
});
//
canvas.addEventListener('click', function(event){
var mousePos = getMousePos(canvas, event);
for(var i = 0; i < images.length; i++){
if(
(mousePos.x > images[i].x) && (mousePos.x < images[i].x + images[i].img.width) &&
(mousePos.y > images[i].y) && (mousePos.y < images[i].y + images[i].img.height)
){
// console.log('clicking on: ' + images[i].link);
window.open(images[i].link);
}
}
});
}
var HalfCircle = function(){
this.numOfArch = 6;
this.posX = 438;
this.posY = 20;
this.rad = 170;
this.color = [
{ start_color: 'rgb(255,182,54)', end_color: 'rgb(255,220,159)' },
{ start_color: 'rgb(240,97,38)', end_color: 'rgb(249,166,57)' },
{ start_color: 'rgb(254,107,108)', end_color: 'rgb(250,74,78)' },
{ start_color: 'rgb(0,131,195)', end_color: 'rgb(0,150,219)' },
{ start_color: 'rgb(115,174,14)', end_color: 'rgb(214,243,137)' },
{ start_color: 'rgb(133,29,250)', end_color: 'rgb(203,159, 255)' },
];
this.lineWidth = 5;
};
HalfCircle.prototype = {
smallDot: function (posX, posY, ctx, colr){
ctx.beginPath();
ctx.fillStyle = colr;
ctx.arc(posX, posY, 7, 0, Math.PI*2, false);
ctx.fill();
ctx.closePath();
},
bigDot : function (posX, posY, ctx, colr){
ctx.beginPath();
ctx.fillStyle = colr;
ctx.arc(posX, posY, 10, 0, Math.PI*2, false);
ctx.fill();
ctx.closePath();
},
getEndCord: function(startCord){
return startCord + Math.PI/this.numOfArch;
},
doArch : function (ctx){
var startCord = 0;
for( i = 0; i < this.numOfArch; i++ ){
dotStartX = this.rad * Math.cos(startCord) + this.posX;
dotStartY = this.rad * Math.sin(startCord) + this.posY;
this.smallDot(dotStartX, dotStartY, ctx , this.color[i].start_color);
ctx.lineWidth = this.lineWidth;
ctx.beginPath();
ctx.strokeStyle = this.color[i].start_color;
var endCord = this.getEndCord(startCord);
ctx.arc(this.posX, this.posY, this.rad, startCord, endCord , false);
ctx.stroke();
ctx.closePath();
startCord = endCord;
dotStartX = this.rad * Math.cos(endCord) + this.posX;
dotStartY = this.rad * Math.sin(endCord) + this.posY;
this.bigDot(dotStartX, dotStartY, ctx , this.color[i].end_color);
}
}
}
Am seriously would need someone input on these. Thanks

context.save only saves the context state (stylings, transformations, etc). It does not save anything you have drawn on the canvas. So context.restore will only restore the context state, not the drawings.
To remove something you have previously drawn on the canvas, you must clear the entire canvas and redraw everything that you do want on the canvas.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
//
var halfCircle;
var effect = true;
var overlayIndex=-1;
//
var images = [];
images.push({x:20,y:20, link: "http://example.com/shoppinglist-infographic"});
images.push({x:130,y:150,link: "http://example.com/referral/invite?g=banner"});
images.push({x:230,y:220,link: "http://example.com/all-fast-delivery/"});
images.push({x:460,y:220,link:"http://example.com/busyhomemaker/"});
images.push({x:570,y:150,link:"#"});
images.push({x:620,y:20, link:"#"});
images.push({x:285,y:20, link:"#"});
// Define HalfCircle
var HalfCircle = function(){
this.numOfArch = 6;
this.posX = 438;
this.posY = 20;
this.rad = 170;
this.color = [
{ start_color: 'rgb(255,182,54)', end_color: 'rgb(255,220,159)' },
{ start_color: 'rgb(240,97,38)', end_color: 'rgb(249,166,57)' },
{ start_color: 'rgb(254,107,108)', end_color: 'rgb(250,74,78)' },
{ start_color: 'rgb(0,131,195)', end_color: 'rgb(0,150,219)' },
{ start_color: 'rgb(115,174,14)', end_color: 'rgb(214,243,137)' },
{ start_color: 'rgb(133,29,250)', end_color: 'rgb(203,159, 255)' },
];
this.lineWidth = 5;
};
//
HalfCircle.prototype = {
smallDot: function (posX, posY, ctx, colr){
ctx.beginPath();
ctx.fillStyle = colr;
ctx.arc(posX, posY, 7, 0, Math.PI*2, false);
ctx.fill();
ctx.closePath();
},
bigDot : function (posX, posY, ctx, colr){
ctx.beginPath();
ctx.fillStyle = colr;
ctx.arc(posX, posY, 10, 0, Math.PI*2, false);
ctx.fill();
ctx.closePath();
},
getEndCord: function(startCord){
return startCord + Math.PI/this.numOfArch;
},
doArch : function (ctx){
var startCord = 0;
for( i = 0; i < this.numOfArch; i++ ){
dotStartX = this.rad * Math.cos(startCord) + this.posX;
dotStartY = this.rad * Math.sin(startCord) + this.posY;
this.smallDot(dotStartX, dotStartY, ctx , this.color[i].start_color);
ctx.lineWidth = this.lineWidth;
ctx.beginPath();
ctx.strokeStyle = this.color[i].start_color;
var endCord = this.getEndCord(startCord);
ctx.arc(this.posX, this.posY, this.rad, startCord, endCord , false);
ctx.stroke();
ctx.closePath();
startCord = endCord;
dotStartX = this.rad * Math.cos(endCord) + this.posX;
dotStartY = this.rad * Math.sin(endCord) + this.posY;
this.bigDot(dotStartX, dotStartY, ctx , this.color[i].end_color);
}
}
}
// preload all images
// put the paths to your images in imageURLs[]
var imageURLs=[];
imageURLs.push('http://example.com/media/features/0.png');
imageURLs.push('http://example.com/media/features/1.png');
imageURLs.push('http://example.com/media/features/2.png');
imageURLs.push('http://example.com/media/features/3.png');
imageURLs.push('http://example.com/media/features/4.png');
imageURLs.push('http://example.com/media/features/5.png');
imageURLs.push('http://example.com/media/features/text.png');
//
// the loaded images will be placed in imgs[]
var imgs=[];
var imagesOK=0;
startLoadingAllImages(imagesAreNowLoaded);
//
// Create a new Image() for each item in imageURLs[]
// When all images are loaded, run the callback (==imagesAreNowLoaded)
function startLoadingAllImages(callback){
// iterate through the imageURLs array and create new images for each
for (var i=0; i<imageURLs.length; i++) {
// create a new image an push it into the imgs[] array
var img = new Image();
imgs.push(img);
// when this image loads, call this img.onload
img.onload = function(){
// this img loaded, increment the image counter
imagesOK++;
// if we've loaded all images, call the callback
if (imagesOK>=imageURLs.length ) {
callback();
}
};
// notify if there's an error
img.onerror=function(){alert("image load failed");}
// set img properties
img.src = imageURLs[i];
}
}
//
function imagesAreNowLoaded(){
// the imgs[] array now holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
halfCircle = new HalfCircle();
draw();
$("#canvas").mousemove(function(e){handleMouseMove(e);});
}
function draw(){
ctx.fillStyle='#fafafa';
ctx.fillRect(0,0,cw,ch);
halfCircle.doArch(ctx);
for(var i=0;i<imgs.length;i++){
ctx.drawImage(imgs[i], images[i].x,images[i].y);
if(i==overlayIndex){
ctx.fillStyle = "#fafafa";
ctx.globalAlpha = 0.35;
ctx.fillRect( images[i].x, images[i].y, imgs[i].width, imgs[i].height);
ctx.globalAlpha = 1.00;
}
}
}
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
//
overlayIndex=-1;
for(var i=0;i<images.length;i++){
var img=images[i];
var image=imgs[i];
if(
mx>img.x && mx<img.x+image.width &&
my>img.y && my<img.y+image.height
){
overlayIndex=i;
}
}
draw();
}
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=900 height=500></canvas>

hope it will helps you..
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Canvas Test</title>
</head>
<body>
<header> </header>
<nav> </nav>
<section>
<div>
<canvas id="canvas" width="320" height="200">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
</div>
<script type="text/javascript">
var canvas;
var ctx;
function init() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
draw();
}
function draw() {
ctx.fillStyle = '#FA6900';
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.shadowBlur = 4;
ctx.shadowColor = 'rgba(204, 204, 204, 0.5)';
ctx.fillRect(0,0,15,150);
ctx.save();
ctx.fillStyle = '#E0E4CD';
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 4;
ctx.shadowColor = 'rgba(204, 204, 204, 0.5)';
ctx.fillRect(30,0,30,150);
ctx.save();
ctx.fillStyle = '#A7DBD7';
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 15;
ctx.shadowBlur = 4;
ctx.shadowColor = 'rgba(204, 204, 204, 0.5)';
ctx.fillRect(90,0,45,150);
ctx.save();
ctx.restore();
ctx.beginPath();
ctx.arc(185, 75, 22, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.arc(260, 75, 15, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.arc(305, 75, 8, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
init();
</script>
</section>
<aside> </aside>
<footer> </footer>
</body>
</html>

Related

line rotation in class javascript canvas

I try to create a class with a function which might be use to change the angle of a line already drawn.
With what I write, the line doesn't move. When I press the right or left key, I have this error :
TypeError: this.changeAngle is not a function
Indeed, I don't have "function" keyword in my code ... I don't know what to use instead.
Could you help me ?
Thank you very much.
window.onload = init;
let canvas, ctx;
let mousePos;
let angle = 0;
class Lanceur {
constructor() {
this.changeAngle(this.angle);
}
update(ctx) {
this.drawAiguille(ctx);
}
drawSocleLanceur(ctx) {
ctx.save();
ctx.beginPath();
ctx.lineWidth = 2;
ctx.arc(w/2, h, 20, 0, 2 * Math.PI);
ctx.stroke();
ctx.restore();
}
drawAiguille(ctx) {
ctx.save();
ctx.rotate(this.angle);
ctx.strokeStyle = "rgb(255, 0, 0)";
ctx.lineWidth=3;
ctx.beginPath();
ctx.moveTo(w/2, h-h*0.12);
ctx.lineTo(w/2, h-h*0.035);
ctx.stroke();
ctx.restore();
}
changeAngle(a) {
this.angle = a;
}
getAngle() {
return this.angle;
}
}
function init() {
canvas = document.querySelector("#jeu");
ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
a = new Lanceur();
requestAnimationFrame(mainloop);
}
function mainloop() {
ctx.clearRect(0, 0, w, h);
a.update(ctx);
requestAnimationFrame(mainloop);
}
document.addEventListener('keypress', function(event){
gereTouches(event);
});
function gereTouches(event) {
if(event.key == "ArrowRight") {
this.changeAngle(this.getAngle - 1);
console.log("ça bouge : " + this.angle);
}else if(event.key == "ArrowLeft") {
this.changeAngle(this.getAngle + 1);
}
}
Main changes:
added this.angle to thr constructor()
using keydown event
the function gereTouches(event) uses a instead of this and a.getAngle() instead of a.getAngle
also 1 for the the angle is way too big (those are radians). I'm using .01 instead.
I hope it helps.
window.onload = init;
let canvas, ctx;
let mousePos;
let angle = 0;
class Lanceur {
constructor() {
this.angle = 0;
this.changeAngle(this.angle);
}
update(ctx) {
this.drawAiguille(ctx);
}
drawSocleLanceur(ctx) {
ctx.save();
ctx.beginPath();
ctx.lineWidth = 2;
ctx.arc(w/2, h, 20, 0, 2 * Math.PI);
ctx.stroke();
ctx.restore();
}
drawAiguille(ctx) {
ctx.save();
ctx.rotate(this.angle);
ctx.strokeStyle = "rgb(255, 0, 0)";
ctx.lineWidth=3;
ctx.beginPath();
ctx.moveTo(w/2, h-h*0.12);
ctx.lineTo(w/2, h-h*0.035);
ctx.stroke();
ctx.restore();
}
changeAngle(a) {
this.angle = a;
}
getAngle() {
return this.angle;
}
}
function init() {
canvas = document.querySelector("#jeu");
ctx = canvas.getContext("2d");
w = canvas.width = 500;
h = canvas.height = 500;
a = new Lanceur();
requestAnimationFrame(mainloop);
}
function mainloop() {
ctx.clearRect(0, 0, w, h);
a.update(ctx);
requestAnimationFrame(mainloop);
}
document.addEventListener('keydown', function(event){
gereTouches(event);
});
function gereTouches(event) {
if(event.key == "ArrowRight") {
a.changeAngle(a.getAngle() - .01);
console.log("ça bouge : " + a.angle);
}else if(event.key == "ArrowLeft") {
a.changeAngle(a.getAngle() + .01);
}
}
canvas{border:1px solid}
<canvas id="jeu"></canvas>

calling a method into another function

Why doesn't the updatePosition read the fall function in Square? Its suppose to be drawing a block falling from the top, but nothing is happening. Can't seem to wrap my head around the problem. It has something to do with invoking a function call within another.
var WIDTH = 300,
HEIGHT = 400,
c = document.getElementById('canvas'),
ctx = c.getContext('2d');
setInterval(function () {
clearCanvas();
updatePosition();
drawOnCanvas();
}, 1000 / 50);
var clearCanvas = function () {
ctx.fillStyle = 'White';
ctx.beginPath();
ctx.rect(0, 0, WIDTH, HEIGHT);
ctx.closePath();
ctx.fill();
}
var drawLine = function () {
ctx.beginPath();
ctx.moveTo(200, 0);
ctx.lineTo(200, 400);
ctx.stroke();
}
var updatePosition = function () {
Square.fall();
}
var drawOnCanvas = function () {
drawLine();
Square.draw();
}
var speedLevels = [20, 16, 12, 10, 8],
currSpeed = speedLevels[0];
var Square = function (speed) {
var self = this;
self.color = "Black";
self.vPosition = 0;
self.hPosition = 4;
self.speed = speed;
self.temp = 0;
self.fall = function () {
if (self.temp == self.speed) {
self.vPosition++;
self.temp = 0;
}
self.temp++;
}
self.draw = function () {
console.log(self.vPosition * squareLength);
ctx.fillStyle = self.color;
ctx.fillRect(self.hPosition * squareLength, self.vPosition * squareLength, squareLength, squareLength);
}
return self;
}
Firstly, you're calling Square.fall() and Square.draw() as if Square were an instance of an object - it's not, because you haven't done Square = new ... anywhere.
Secondly, you're using a variable squareLength which isn't defined anywhere.
var _Square = function(speed) {
var self = this;
self.color = "Black";
self.vPosition = 0;
self.hPosition = 4;
self.speed = speed;
self.temp = 0;
squareLength = 1;
self.fall = function() {
if (self.temp == self.speed) {
self.vPosition++;
self.temp = 0;
}
self.temp++;
}
self.draw = function() {
console.log(self.vPosition * squareLength);
ctx.fillStyle = self.color;
ctx.fillRect(self.hPosition * squareLength, self.vPosition * squareLength, squareLength, squareLength);
}
return self;
}
var WIDTH = 300,
HEIGHT = 400,
c = document.getElementById('canvas'),
ctx = c.getContext('2d');
var Square = new _Square(10);
setInterval(function() {
clearCanvas();
updatePosition();
drawOnCanvas();
}, 1000 / 50);
var clearCanvas = function() {
ctx.fillStyle = 'White';
ctx.beginPath();
ctx.rect(0, 0, WIDTH, HEIGHT);
ctx.closePath();
ctx.fill();
}
var drawLine = function() {
ctx.beginPath();
ctx.moveTo(200, 0);
ctx.lineTo(200, 400);
ctx.stroke();
}
var updatePosition = function() {
Square.fall();
}
var drawOnCanvas = function() {
drawLine();
Square.draw();
}
var speedLevels = [20, 16, 12, 10, 8],
currSpeed = speedLevels[0];
canvas {
border: 1px solid red;
width: 300px;
height: 400px;
}
<canvas id='canvas'></canvas>

HTML5 canvas globalAlpha not working

I need create smooth drawing lines with transparent without clearRect method i try use: globalAlpha and strokeStyle with rgba like this:
ctx.strokeStyle = "rgba( redChannel, greenChannel, blueChannel, AlphaChannel)";
But did not work both methods. How i can drawing transparent lines without clearRect method. While i use clearRect before each drawing globalAlpha works, but i need that working without it.
my code example:
var el = document.getElementById('c');
var ctx = el.getContext('2d');
ctx.lineWidth = 10;
ctx.lineJoin = ctx.lineCap = 'round';
ctx.globalAlpha = "0.2";
//ctx.strokeStyle = "rgba(255, 0, 0, 150)";
ctx.strokeStyle = "red";
var isDrawing, points = [ ];
el.onmousedown = function(e) {
isDrawing = true;
points.push({ x: e.clientX, y: e.clientY });
};
el.onmousemove = function(e) {
if (!isDrawing) return;
//ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
points.push({ x: e.clientX, y: e.clientY });
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (var i = 1; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.stroke();
ctx.closePath();
};
el.onmouseup = function() {
isDrawing = false;
points.length = 0;
};
canvas { border: 1px solid #ccc }
<canvas id="c" width="500" height="300"></canvas>
You are redrawing the entire path every time the mouse moves, so even though the globalAlpha value is set to 0.2 the layering effect makes the line appear solid.
Option 1:
use clearRect to clear the path onmousemove then redraw;
Option 2:
draw only the last segment of the path, but overlaps are visible:
var el = document.getElementById('c');
var ctx = el.getContext('2d');
ctx.lineWidth = 10;
ctx.lineJoin = ctx.lineCap = 'round';
ctx.globalAlpha = "0.2";
//ctx.strokeStyle = "rgba(255, 0, 0, 150)";
ctx.strokeStyle = "red";
var isDrawing, points = [ ];
el.onmousedown = function(e) {
isDrawing = true;
points.push({ x: e.clientX, y: e.clientY });
};
el.onmousemove = function(e) {
if (!isDrawing) return;
//ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
points.push({ x: e.clientX, y: e.clientY });
ctx.beginPath();
//draw just the last segment
if(points.length>1) {
ctx.moveTo(points[points.length-2].x, points[points.length-2].y);
ctx.lineTo(points[points.length-1].x, points[points.length-1].y);
}
ctx.stroke();
ctx.closePath();
};
el.onmouseup = function() {
isDrawing = false;
points.length = 0;
};
canvas { border: 1px solid #ccc }
<canvas id="c" width="500" height="300"></canvas>
Option 3:
set the opacity of the canvas element
var el = document.getElementById('c');
var ctx = el.getContext('2d');
ctx.lineWidth = 10;
ctx.lineJoin = ctx.lineCap = 'round';
//ctx.globalAlpha = "0.2";
//ctx.strokeStyle = "rgba(255, 0, 0, 150)";
ctx.strokeStyle = "red";
var isDrawing, points = [ ];
el.onmousedown = function(e) {
isDrawing = true;
points.push({ x: e.clientX, y: e.clientY });
};
el.onmousemove = function(e) {
if (!isDrawing) return;
//ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
points.push({ x: e.clientX, y: e.clientY });
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (var i = 1; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y);
}
ctx.stroke();
ctx.closePath();
};
el.onmouseup = function() {
isDrawing = false;
points.length = 0;
};
canvas { border: 1px solid #ccc; opacity:0.2; }
<canvas id="c" width="500" height="300"></canvas>
Option 3: use two canvas, on main - draw only on mouse release, and on second preview canvas, just show progress, so drawing will be visible even on mouse move. Also for preview canvas we need to do some tricks like clean previous line segment, so everything looks good.
//main canvas
var el = document.getElementById('c');
var ctx = el.getContext('2d');
//preview
var el2 = document.getElementById('c2');
var ctx2 = el2.getContext('2d');
ctx.lineWidth = 10;
ctx2.lineWidth = 10;
ctx.lineJoin = ctx.lineCap = 'round';
ctx2.lineJoin = ctx2.lineCap = 'round';
ctx.strokeStyle = "rgba(255, 0, 0, 0.5)";
ctx2.strokeStyle = "rgba(255, 0, 0, 0.5)";
var isDrawing, points = [ ];
el2.onmousedown = function(e) {
isDrawing = true;
points.push({ x: e.clientX, y: e.clientY });
ctx.beginPath();
ctx2.beginPath();
};
el2.onmousemove = function(e) {
if (!isDrawing) return;
points.push({ x: e.clientX, y: e.clientY });
//draw just the last segment
if(points.length>1) {
ctx.moveTo(points[points.length-2].x, points[points.length-2].y);
ctx.lineTo(points[points.length-1].x, points[points.length-1].y);
//start preview
ctx2.beginPath();
//clean from last line
ctx2.globalCompositeOperation = "destination-out";
ctx2.strokeStyle = "rgba(255, 0, 0, 1)";
ctx2.moveTo(points[points.length-2].x, points[points.length-2].y);
ctx2.lineTo(points[points.length-1].x, points[points.length-1].y);
ctx2.stroke();
//rest
ctx2.strokeStyle = "rgba(255, 0, 0, 0.5)";
ctx2.globalCompositeOperation = "source-over";
//draw new line segment
ctx2.moveTo(points[points.length-2].x, points[points.length-2].y);
ctx2.lineTo(points[points.length-1].x, points[points.length-1].y);
ctx2.stroke();
}
};
el2.onmouseup = function() {
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx.stroke();
isDrawing = false;
points.length = 0;
};
canvas {
border: 1px solid #ccc;
position:absolute;
}
<canvas id="c" width="500" height="200"></canvas>
<canvas id="c2" width="500" height="200"></canvas>

HTML5 canvas paint redrawing lineJoin not rounded

Hello i make like a paint with undo function, i write all coordinates in array and then try undo just redrawing without last coordinates but problem while i redrawing my canvas parameters incorrect. i use lineJoin = "roind" but after redrawing i see without round..
this result with round line begin and line end while i drawing:
this result without round begin and line end after undo function:
I don't have idea where disappear my round lines while i redrawing all drawings coordinate by coordinate..
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var points = [];
var size = 10;
var prevX = 0;
var prevY = 0;
var isCanDraw = false;
$("#canvas").on("mousedown", function(e) {
isCanDraw = true;
prevX = e.clientX;
prevY = e.clientY;
points.push({x: prevX, y: prevY, size: size, mode: "begin"});
});
$("#canvas").on("mousemove", function(e) {
if(isCanDraw) {
stroke(e.clientX, e.clientY);
points.push({x: prevX, y: prevY, size: size, mode: "draw"});
}
});
$("#canvas").on("mouseup", function(e) {
isCanDraw = false;
points.push({x: prevX, y: prevY, size: size, mode: "end"});
});
$("#canvas").on("mouseleave", function(e) {
isCanDraw = false;
});
$("#undo").on("click", function(e) {
deleteLast();
redraw();
});
function deleteLast() {
if(points.length != 0) {
var i = points.length - 1;
while(points[i].mode != "begin") {
i--;
points.pop();
}
points.pop();
}
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if(points.length != 0) {
for(var i=0; i < points.length; i++) {
var pt = points[i];
var begin=false;
if(size != pt.size) {
size = pt.size;
begin=true;
}
if(pt.mode == "begin" || begin) {
ctx.moveTo(pt.x, pt.y)
}
ctx.lineTo(pt.x, pt.y)
if( pt.mode == "end" || (i == points.length-1) ) {
ctx.lineJoin = "round";
ctx.stroke()
}
}
}
}
function stroke(x,y) {
ctx.lineWidth = size;
ctx.lineJoin = "round";
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
prevX = x;
prevY = y;
}
#canvas {
border: 1px solid #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="canvas" width="500" height="300"></canvas>
<input type="button" id="undo" value="undo">
I think that you're looking for ctx.lineCap property.
+ I modified your redraw function to use a switchinstead of your confusing if statements :
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.lineCap = "round";
ctx.beginPath();
for(var i=0; i < points.length; i++) {
var pt = points[i];
switch(pt.mode){
case "begin" : ctx.moveTo(pt.x, pt.y);
case "draw" : ctx.lineTo(pt.x, pt.y);
case "end" : ctx.stroke();
}
}
}
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var points = [];
var size = 10;
var prevX = 0;
var prevY = 0;
var isCanDraw = false;
var rect = canvas.getBoundingClientRect();
$("#canvas").on("mousedown", function(e) {
isCanDraw = true;
prevX = e.clientX;
prevY = e.clientY;
points.push({
x: prevX,
y: prevY,
size: size,
mode: "begin"
});
ctx.beginPath();
ctx.arc(prevX, prevY, size/2, 0, Math.PI*2);
ctx.fill();
});
$("#canvas").on("mousemove", function(e) {
if (isCanDraw) {
stroke(e.clientX - rect.left, e.clientY - rect.top);
points.push({
x: prevX,
y: prevY,
size: size,
mode: "draw"
});
}
});
$("#canvas").on("mouseup", function(e) {
isCanDraw = false;
points.push({
x: prevX,
y: prevY,
size: size,
mode: "end"
});
});
$("#canvas").on("mouseleave", function(e) {
isCanDraw = false;
});
$("#undo").on("click", function(e) {
deleteLast();
redraw();
});
function deleteLast() {
if (points.length != 0) {
var i = points.length - 1;
while (points[i].mode != "begin") {
i--;
points.pop();
}
points.pop();
}
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.lineCap = "round";
ctx.beginPath();
for (var i = 0; i < points.length; i++) {
var pt = points[i];
switch (pt.mode) {
case "begin":
ctx.moveTo(pt.x, pt.y);
case "draw":
ctx.lineTo(pt.x, pt.y);
case "end":
ctx.stroke();
}
}
}
function stroke(x, y) {
ctx.lineWidth = size;
ctx.lineJoin = "round";
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
prevX = x;
prevY = y;
}
// debounce our rect update func
var scrolling = false;
function scrollHandler(){
rect = canvas.getBoundingClientRect();
scrolling = false;
}
$(window).on('scroll resize', function(e){
if(!scrolling){
requestAnimationFrame(scrollHandler);
}
});
#canvas {
border: 1px solid #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="canvas" width="500" height="300"></canvas>
<input type="button" id="undo" value="undo">
#Kaiido answers correctly that applying lineCap='round' will round the ends of your redrawn line.
Two further thoughts:
You should account for your canvas's offset position from the top-left corner of the document. Otherwise your prevX & prevY positions will be slightly off if the canvas is not on the top-left of the document.
You will have a more crisp line (less "bulgy") if you allow only the beginning and ending linecaps to be rounded and all the interim linecaps to be butted. Leave the linejoins as the default of mitered.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var points = [];
var size = 10;
var prevX = 0;
var prevY = 0;
var isCanDraw = false;
$("#canvas").on("mousedown", function(e) {
isCanDraw = true;
prevX = e.clientX-offsetX;
prevY = e.clientY-offsetY;
points.push({x: prevX, y: prevY, size: size, mode: "begin"});
});
$("#canvas").on("mousemove", function(e) {
if(isCanDraw) {
stroke(e.clientX-offsetX, e.clientY-offsetY);
points.push({x: prevX, y: prevY, size: size, mode: "draw"});
}
});
$("#canvas").on("mouseup", function(e) {
isCanDraw = false;
points.push({x: prevX, y: prevY, size: size, mode: "end"});
});
$("#canvas").on("mouseleave", function(e) {
isCanDraw = false;
});
$("#undo").on("click", function(e) {
deleteLast();
redraw();
});
function deleteLast() {
if(points.length != 0) {
var i = points.length - 1;
while(points[i].mode !== "begin") {
i--;
points.pop();
}
points.pop();
}
}
function redraw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var savedFillStyle=ctx.fillStyle;
ctx.fillStyle=ctx.strokeStyle;
var i=0;
while(i<points.length){
var p=points[i];
// draw "begin" as circle instead of line
ctx.beginPath();
ctx.arc(p.x,p.y,p.size/2,0,Math.PI*2);
ctx.closePath();
ctx.fill();
// draw "draw"
ctx.lineWidth=p.size;
ctx.beginPath();
ctx.moveTo(p.x,p.y);
i++;
while(i<points.length && points[i].mode!='end'){
var p=points[i];
ctx.lineTo(p.x,p.y);
i++;
}
ctx.stroke();
// draw "end" as circle instead of line
var p=points[i];
ctx.beginPath();
ctx.arc(p.x,p.y,p.size/2,0,Math.PI*2);
ctx.closePath();
ctx.fill();
i++;
}
ctx.fillStyle=savedFillStyle;
}
function stroke(x,y) {
ctx.lineWidth = size;
ctx.lineJoin = "round";
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
prevX = x;
prevY = y;
}
body{ background-color: ivory; padding:10px; }
#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="500" height="300"></canvas>
<input type="button" id="undo" value="undo"> </body>

HTML5 Canvas Javascript how to make smooth brush

Hello i need make smooth brush likes this:
I try to create it, i make circle and fill it, but result not successful:
Can be seen circles.. this is not smooth like first example
my example code:
function distanceBetween(point1, point2) {
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}
function angleBetween(point1, point2) {
return Math.atan2( point2.x - point1.x, point2.y - point1.y );
}
var el = document.getElementById('c');
var ctx = el.getContext('2d');
//ctx.fillStyle = "rgba('255, 0, 0, 0.1')";
ctx.fillStyle = "red";
ctx.strokeStyle = "red";
ctx.globalAlpha = "0.05";
ctx.lineWidth = 0;
ctx.globalCompositeOperation = "source-over";
var isDrawing, lastPoint;
el.onmousedown = function(e) {
isDrawing = true;
lastPoint = { x: e.clientX, y: e.clientY };
};
el.onmousemove = function(e) {
if (!isDrawing) return;
var currentPoint = { x: e.clientX, y: e.clientY };
var dist = distanceBetween(lastPoint, currentPoint);
var angle = angleBetween(lastPoint, currentPoint);
for (var i = 0; i < dist; i+=5) {
x = lastPoint.x + (Math.sin(angle) * i) - 25;
y = lastPoint.y + (Math.cos(angle) * i) - 25;
ctx.beginPath();
ctx.arc(x+10, y+10, 20, false, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
lastPoint = currentPoint;
};
el.onmouseup = function() {
isDrawing = false;
};
function clearit() {
ctx.clearRect(0,0, 1000, 1000);
}
canvas { border: 1px solid #ccc }
<canvas id="c" width="500" height="300"></canvas>
<input type="button" id="clear-btn" value="Clear it" onclick="clearit()">
http://codepen.io/anon/pen/NPjwry
Try with a smaller globalAlpha and decrease the stepping (so you draw more circles)
function distanceBetween(point1, point2) {
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}
function angleBetween(point1, point2) {
return Math.atan2( point2.x - point1.x, point2.y - point1.y );
}
var el = document.getElementById('c');
var ctx = el.getContext('2d');
//ctx.fillStyle = "rgba('255, 0, 0, 0.1')";
ctx.fillStyle = "red";
ctx.strokeStyle = "red";
ctx.globalAlpha = "0.01";
ctx.lineWidth = 0;
ctx.globalCompositeOperation = "source-over";
var isDrawing, lastPoint;
el.onmousedown = function(e) {
isDrawing = true;
lastPoint = { x: e.clientX, y: e.clientY };
};
el.onmousemove = function(e) {
if (!isDrawing) return;
var currentPoint = { x: e.clientX, y: e.clientY };
var dist = distanceBetween(lastPoint, currentPoint);
var angle = angleBetween(lastPoint, currentPoint);
for (var i = 0; i < dist; i+=3) {
x = lastPoint.x + (Math.sin(angle) * i) - 25;
y = lastPoint.y + (Math.cos(angle) * i) - 25;
ctx.beginPath();
ctx.arc(x+10, y+10, 20, false, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
lastPoint = currentPoint;
};
el.onmouseup = function() {
isDrawing = false;
};
function clearit() {
ctx.clearRect(0,0, 1000, 1000);
}
canvas { border: 1px solid #ccc }
<canvas id="c" width="500" height="300"></canvas>
<input type="button" id="clear-btn" value="Clear it" onclick="clearit()">
Updated codepen: http://codepen.io/gpetrioli/pen/ramqBz
There is more simple solution: just set width of line to 25px
let ctx = this.d.transparentUpdateImage.getContext('2d');
if (!this.lastPos) {
this.lastPos = {x: x, y: y};
return
}
ctx.beginPath();
ctx.moveTo(this.lastPos.x, this.lastPos.y);
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.strokeStyle = 'red';
ctx.lineWidth = 25;
ctx.lineTo(x, y);
ctx.stroke();
ctx.closePath();
this.lastPos = {x: x, y: y};

Categories

Resources