im learnig javascript and try to develop a page to draw on. Change colors and linewidth are already in place. Now it is drawing, but just when I move the mouse. I'd like to add also on click on just on move. So I'm able to draw also points. I have put the drawCircle function in the function stopDraw, but this is not working as it should. Any ideas?
Second problem is that, I'd like to draw a circle without stroke. But as soon as I change the lineWidth, also my circle gets a stroke around. Please help...
// Setup event handlers
cb_canvas = document.getElementById("cbook");
cb_lastPoints = Array();
if (cb_canvas.getContext) {
cb_ctx = cb_canvas.getContext('2d');
cb_ctx.lineWidth = 14;
cb_ctx.strokeStyle = "#0052f8";
cb_ctx.lineJoin="round";
cb_ctx.lineCap="round"
cb_ctx.beginPath();
cb_canvas.onmousedown = startDraw;
cb_canvas.onmouseup = stopDraw;
cb_canvas.ontouchstart = startDraw;
cb_canvas.ontouchend = stopDraw;
cb_canvas.ontouchmove = drawMouse;
}
function startDraw(e) {
if (e.touches) {
// Touch event
for (var i = 1; i <= e.touches.length; i++) {
cb_lastPoints[i] = getCoords(e.touches[i - 1]); // Get info for finger #1
}
}
else {
// Mouse event
cb_lastPoints[0] = getCoords(e);
cb_canvas.onmousemove = drawMouse;
}
return false;
}
// Called whenever cursor position changes after drawing has started
function stopDraw(e) {
drawCircle(e);
e.preventDefault();
cb_canvas.onmousemove = null;
}
//Draw circle
function drawCircle(e) {
var canvasOffset = canvas.offset();
var canvasX = Math.floor(e.pageX-canvasOffset.left);
var canvasY = Math.floor(e.pageY-canvasOffset.top);
//var canvasPos = getCoords(e);
cb_ctx.beginPath();
cb_ctx.arc(canvasX, canvasY, cb_ctx.lineWidth/2, 0, Math.PI*2, true);
cb_ctx.fillStyle = cb_ctx.strokeStyle;
cb_ctx.fill();
}
function drawMouse(e) {
cb_ctx.beginPath();
if (e.touches) {
// Touch Enabled
for (var i = 1; i <= e.touches.length; i++) {
var p = getCoords(e.touches[i - 1]); // Get info for finger i
cb_lastPoints[i] = drawLine(cb_lastPoints[i].x, cb_lastPoints[i].y, p.x, p.y);
}
}
else {
// Not touch enabled
var p = getCoords(e);
cb_lastPoints[0] = drawLine(cb_lastPoints[0].x, cb_lastPoints[0].y, p.x, p.y);
}
cb_ctx.stroke();
cb_ctx.closePath();
//cb_ctx.beginPath();
return false;
}
// Draw a line on the canvas from (s)tart to (e)nd
function drawLine(sX, sY, eX, eY) {
cb_ctx.moveTo(sX, sY);
cb_ctx.lineTo(eX, eY);
return { x: eX, y: eY };
}
// Get the coordinates for a mouse or touch event
function getCoords(e) {
if (e.offsetX) {
return { x: e.offsetX, y: e.offsetY };
}
else if (e.layerX) {
return { x: e.layerX, y: e.layerY };
}
else {
return { x: e.pageX - cb_canvas.offsetLeft, y: e.pageY - cb_canvas.offsetTop };
}
}
Your drawCircle function is wrapped in the canvas's onclick event. It should be
function drawCircle(e) {
var canvasOffset = canvas.offset();
var canvasX = Math.floor(e.pageX-canvasOffset.left);
var canvasY = Math.floor(e.pageY-canvasOffset.top);
cb_ctx.beginPath();
cb_ctx.lineWidth = 0;
cb_ctx.arc(canvasX,canvasY,cb_ctx.lineWidth/2,0,Math.PI*2,true);
cb_ctx.fillStyle = cb_ctx.strokeStyle;
cb_ctx.fill();
}
So you'll have to pass the event object from stop draw as well
function stopDraw(e) {
drawCircle(e); //notice the change here
e.preventDefault();
cb_canvas.onmousemove = null;
}
Note that you're also setting you circle's radius to 0 which might be another reason why it's not showing up.
cb_ctx.lineWidth = 0;
cb_ctx.arc(canvasX,canvasY,cb_ctx.lineWidth/2,0,Math.PI*2,true);
0 divided by 2 is always zero. If you don't want to stroke around the circle just don't call stroke(). the drawCircle function isn't causing that, since there's a beginPath() call. I suspect it's because you don't beginPath() before calling stroke() in drawMouse.
Related
I have 2 sets of code in here. One is the simple bounce off code. The other is a function. I've tried to make it a function but it doesn't seem to be working properly.
The bg framerate doesn't clear in the sense that a string of balls show rather than a ball bouncing and animating.
if(this.y_pos > 400) this condition doesn't seem to be working even tho it works when it is drawn in the draw function.
var sketch = function (p) {
with(p) {
p.setup = function() {
createCanvas(800, 600);
// x_pos = 799;
// y_pos = 100;
// spdx = -random(5,10);
// spdy = random(12,17);
};
p.draw = function() {
background(0);
// fill(255);
// ellipse(x_pos,y_pos,50);
// x_pos += spdx;
// y_pos += spdy;
// if(y_pos > 400)
// {
// spdy *= -1;
// }
// for( var i = 0; i < 10; i++)
avalance(799, 100, random(5,10), random(12,17));
};
function avalance(x, y, spdx, spdy)
{
this.x_pos = x;
this.y_pos = y;
this.spdx = spdx;
this.spdy = spdy;
this.x_pos = 799;
this.y_pos = 100;
this.spdx = 1;
this.spdy = 1;
this.movement = function()
{
this.x_pos += -spdx;
this.y_pos += spdy;
if(this.y_pos > 400)
{
this.spdy *= -1;
}
}
this.draw = function()
{
this.movement();
this.drawnRox();
}
this.drawnRox = function()
{
fill(255);
ellipse(this.x_pos,this.y_pos,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Let's address both issues:
The draw() function is called for each new frame and in it you call avalance which creates a new ball. To fix that you need to
Create a global variable let ball; out of setup() and draw() so that you can reuse that later;
In setup create a new ball and assign it to your ball variable: ball = new avalance(799, 100, random(5,10), random(12,17));
In draw() you want to update the ball and that's what its own draw() function does (I would advise renaming it update() for example, to avoid confusion with the p5 specific draw() function). So you just need to call ball.draw() in draw().
This creates a ball which moves but still don't respect your 400px limit.
The issue is that in movement() you add spdx and spdy to the position but when the ball crosses the limit you update this.spdy, so you need to update the function with this code:
this.x_pos += -this.spdx;
this.y_pos += this.spdy;
And you should be good! Here is a code pend with your code working as you intend.
Also as a bonus advise: You probably want to use some p5.Vector objects to store positions, speeds and accelerations it really makes your code easier to read and to use. You could also rename your function Avalance (capital A) to show that you actually use a class and that this function shouldn't be called without new.
As #statox suggested, do new avalance(799, 100, random(5,10), random(12,17)) in the setup() section and call draw of the ball in draw() section.
You can test the code below by clicking "Run code snippet".
var sketch = function (p) {
with(p) {
var ball;
p.setup = function() {
createCanvas(800, 600);
ball = new avalance(799, 100, random(5,10), random(12,17));
};
p.draw = function() {
background(0);
ball.draw();
};
function avalance(x, y, spdx, spdy)
{
function movement ()
{
x += -spdx;
y += spdy;
if (y > height || y < 0)
{
spdy *= -1;
}
if (x > width || x < 0)
{
spdx *= -1;
}
}
this.draw = function()
{
movement();
drawnRox();
}
function drawnRox ()
{
fill(255);
ellipse(x,y,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Below is a script which defines two functions that draw 4 rectangular buttons and 1 circular button respectively. I am trying to implement specific Hover and Click functionality into the buttons (as described in the script alerts) but I am at a bit of a loss as to how to do this. I tried calling the makeInteractiveButton() functions on each click but this caused a lot of odd overlap and lag. I want the script to do the following:
If the circular button is hovered, I would like it's fillColour to change and if it is clicked I would like it to change again to the colours described in the code (#FFC77E for hover, #FFDDB0 for clicked). This should only happen for the duration of the hover or click.
HTML:
<html lang="en">
<body>
<canvas id="game" width = "750" height = "500"></canvas>
<script type='text/javascript' src='stack.js'></script>
</body>
</html>
JavaScript:
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d')
elements = [];
c.style.background = 'grey';
function makeInteractiveButton(x, strokeColor, fillColor) {
ctx.strokeStyle=strokeColor;
ctx.fillStyle=fillColor;
ctx.beginPath();
ctx.lineWidth=6;
ctx.arc(x, 475, 20, 0, 2*Math.PI);
ctx.closePath();
ctx.stroke();
ctx.fill();
elements.push({
arcX: x,
arcY: 475,
arcRadius: 20
});
}
b1 = makeInteractiveButton(235, '#FFFCF8', '#FFB85D');
c.addEventListener('mousemove', function(event) {
x=event.pageX-canvasX; // cursor location
y=event.pageY-canvasY;
elements.forEach(function(element) {
if (x > element.left && x < element.left + element.width &&
y > element.top && y < element.top + element.height) { // if cursor in rect
alert('Rectangle should undergo 5 degree rotation and 105% scale');
}
else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) <
Math.pow(element.arcRadius, 2)) { // if cursor in circle
alert('Set b1 fillColour to #FFC77E.');
}
});
}, false);
c.addEventListener('click', function(event) {
x=event.pageX-canvasX; // cursor location
y=event.pageY-canvasY;
elements.forEach(function(element) {
if (x > element.left && x < element.left + element.width &&
y > element.top && y < element.top + element.height) { // if rect clicked
alert('Move all cards to centre simultaneously.');
}
else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) <
Math.pow(element.arcRadius, 2)) { // if circle clicked
alert('Set b1 fillColour to #FFDDB0.');
}
});
}, false);
One way is keep all element data and write a hitTest(x,y) function but when you have a lot of complex shapes its better to use a secondary canvas to render element with their ID instead of their color in it and the color of x,y in second canvas is ID of hitted element, I should mention that the second canvas is'nt visible and its just a gelper for get the hitted element.
Github Sample:
https://siamandmaroufi.github.io/CanvasElement/
Simple implementation of hitTest for Rectangles :
var Rectangle = function(id,x,y,width,height,color){
this.id = id;
this.x=x;
this.y=y;
this.width = width;
this.height = height;
this.color = color || '#7cf';
this.selected = false;
}
Rectangle.prototype.draw = function(ctx){
ctx.fillStyle = this.color;
ctx.fillRect(this.x,this.y,this.width,this.height);
if(this.selected){
ctx.strokeStyle='red';
ctx.setLineDash([5,5]);
ctx.lineWidth = 5;
ctx.strokeRect(this.x,this.y,this.width,this.height);
}
}
Rectangle.prototype.hitTest=function(x,y){
return (x >= this.x) && (x <= (this.width+this.x)) &&
(y >= this.y) && (y <= (this.height+this.y));
}
var Paint = function(el) {
this.element = el;
this.shapes = [];
}
Paint.prototype.addShape = function(shape){
this.shapes.push(shape);
}
Paint.prototype.render = function(){
//clear the canvas
this.element.width = this.element.width;
var ctx = this.element.getContext('2d');
for(var i=0;i<this.shapes.length;i++){
this.shapes[i].draw(ctx);
}
}
Paint.prototype.setSelected = function(shape){
for(var i=0;i<this.shapes.length;i++){
this.shapes[i].selected = this.shapes[i]==shape;
}
this.render();
}
Paint.prototype.select = function(x,y){
for(var i=this.shapes.length-1;i>=0;i--){
if(this.shapes[i].hitTest(x,y)){
return this.shapes[i];
}
}
return null;
}
var el = document.getElementById('panel');
var paint = new Paint(el);
var rectA = new Rectangle('A',10,10,150,90,'yellow');
var rectB = new Rectangle('B',150,90,140,100,'green');
var rectC = new Rectangle('C',70,85,200,70,'rgba(0,0,0,.5)');
paint.addShape(rectA);
paint.addShape(rectB);
paint.addShape(rectC);
paint.render();
function panel_mouseUp(evt){
var p = document.getElementById('panel');
var x = evt.x - p.offsetLeft;
var y = evt.y - p.offsetTop;
var shape = paint.select(x,y);
if(shape){
alert(shape.id);
}
//console.log('selected shape :',shape);
}
function panel_mouseMove(evt){
var p = document.getElementById('panel');
var x = evt.x - p.offsetLeft;
var y = evt.y - p.offsetTop;
var shape = paint.select(x,y);
paint.setSelected(shape);
}
el.addEventListener('mouseup',panel_mouseUp);
el.addEventListener('mousemove',panel_mouseMove);
body {background:#e6e6e6;}
#panel {
border:solid thin #ccc;
background:#fff;
margin:0 auto;
display:block;
}
<canvas id="panel" width="400px" height="200px" >
</canvas>
just click or move over the shapes
I'm having trouble creating a click event for my Javascript canvas game. So far I have been following a tutorial, however the way you interact with the game is through mouse hover. I would like to change it so that instead of hovering over objects in the canvas to interact, I instead use a mouse click.
The following is the code I use to detect the mouse hover.
getDistanceBetweenEntity = function (entity1,entity2) //return distance
{
var vx = entity1.x - entity2.x;
var vy = entity1.y - entity2.y;
return Math.sqrt(vx*vx+vy*vy);
}
testCollisionEntity = function (entity1,entity2) //return if colliding
{
var distance = getDistanceBetweenEntity(entity1,entity2);
return distance < 50;
}
I then use this in a loop to interact with it.
var isColliding = testCollisionEntity(player,nounList[key]);
if(isColliding)
{
delete nounList[key];
player.score = player.score + 10;
}
Below is a complete copy of my game at its current state.
<canvas id="ctx" width="500" height="500" style="border:1px solid #000000;"></canvas>
<script>
var ctx = document.getElementById("ctx").getContext("2d");
ctx.font = '30px Arial';
//Setting the height of my canvas
var HEIGHT = 500;
var WIDTH = 500;
//Player class
var player =
{
x:50,
spdX:30,
y:40,
spdY:5,
name:'P',
score:0,
};
//Creating arrays
var nounList ={};
var adjectivesList ={};
var verbsList ={};
getDistanceBetweenEntity = function (entity1,entity2) //return distance
{
var vx = entity1.x - entity2.x;
var vy = entity1.y - entity2.y;
return Math.sqrt(vx*vx+vy*vy);
}
testCollisionEntity = function (entity1,entity2) //return if colliding
{
var distance = getDistanceBetweenEntity(entity1,entity2);
return distance < 50;
}
Nouns = function (id,x,y,name)
{
var noun =
{
x:x,
y:y,
name:name,
id:id,
};
nounList[id] = noun;
}
Adjectives = function (id,x,y,name)
{
var adjective =
{
x:x,
y:y,
name:name,
id:id,
};
adjectivesList[id] = adjective;
}
Verbs = function (id,x,y,name)
{
var verb =
{
x:x,
y:y,
name:name,
id:id,
};
verbsList[id] = verb;
}
document.onmousemove = function(mouse)
{
var mouseX = mouse.clientX;
var mouseY = mouse.clientY;
player.x = mouseX;
player.y = mouseY;
}
updateEntity = function (something)
{
updateEntityPosition(something);
drawEntity(something);
}
updateEntityPosition = function(something)
{
}
drawEntity = function(something)
{
ctx.fillText(something.name,something.x,something.y);
}
update = function ()
{
ctx.clearRect(0,0,WIDTH,HEIGHT);
drawEntity(player);
ctx.fillText("Score: " + player.score,0,30);
for(var key in nounList)
{
updateEntity(nounList[key]);
var isColliding = testCollisionEntity(player,nounList[key]);
if(isColliding)
{
delete nounList[key];
player.score = player.score + 10;
}
}
for(var key in adjectivesList)
{
updateEntity(adjectivesList[key])
var isColliding = testCollisionEntity(player,adjectivesList[key]);
if(isColliding)
{
delete adjectivesList[key];
player.score = player.score - 1;
}
}
for(var key in verbsList)
{
updateEntity(verbsList[key])
var isColliding = testCollisionEntity(player,verbsList[key]);
if(isColliding)
{
delete verbsList[key];
player.score = player.score - 1;
}
}
if(player.score >= 46)
{
ctx.clearRect(0,0,WIDTH,HEIGHT);
ctx.fillText("Congratulations! You win!",50,250);
ctx.fillText("Refresh the page to play again.",50,300);
}
}
Nouns('N1',150,350,'Tea');
Nouns('N2',400,450,'Park');
Nouns('N3',250,150,'Knee');
Nouns('N4',50,450,'Wall');
Nouns('N5',410,50,'Hand');
Adjectives('A1',50,100,'Broken');
Adjectives('A2',410,300,'Noisy');
Verbs('V1',50,250,'Smell');
Verbs('V2',410,200,'Walk');
setInterval(update,40);
To summarize all I want to do is change it so that instead of mousing over words to delete them you have to click.
(Apologies for not using correct terminology in places, my programming knowledge is quite limited.)
You can have your canvas listen for mouse clicks on itself like this:
// get a reference to the canvas element
var canvas=document.getElementById('ctx');
// tell canvas to listen for clicks and call "handleMouseClick"
canvas.onclick=handleMouseClick;
In the click handler, you'll need to know the position of your canvas relative to the viewport. That's because the browser always reports mouse coordinates relative to the viewport. You can get the canvas position relative to the viewport like this:
// get the bounding box of the canvas
var BB=canvas.getBoundingClientRect();
// get the left X position of the canvas relative to the viewport
var BBoffsetX=BB.left;
// get the top Y position of the canvas relative to the viewport
var BBoffsetY=BB.top;
So your mouseClickHandler might look like this:
// this function will be called when the user clicks
// the mouse in the canvas
function handleMouseClick(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the canvas postion relative to the viewport
var BB=canvas.getBoundingClientRect();
var BBoffsetX=BB.left;
var BBoffsetY=BB.top;
// calculate the mouse position
var mouseX=e.clientX-BBoffsetX;
var mouseY=e.clientY-BBoffsetY;
// report the mouse position using the h4
$position.innerHTML='Click at '+parseInt(mouseX)+' / '+parseInt(mouseY);
}
If your game doesn't let the window scroll or resize then the canvas postion won't change relative to the viewport. Then, for better performance, you can move the 3 lines relating to getting the canvas position relative to the viewport to the top of your app.
// If the browser window won't be scrolled or resized then
// get the canvas postion relative to the viewport
// once at the top of your app
var BB=canvas.getBoundingClientRect();
var BBoffsetX=BB.left;
var BBoffsetY=BB.top;
function handleMouseClick(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the mouse position
var mouseX=e.clientX-BBoffsetX;
var mouseY=e.clientY-BBoffsetY;
// report the mouse position using the h4
$position.innerHTML='Click at '+parseInt(mouseX)+' / '+parseInt(mouseY);
}
I have a canvas that I can draw things what I want to do is generate new canvases dynamically when clicking a button.I've defined a generate function but it did not work
here is script
//<![CDATA[
window.addEventListener('load', function () {
// get the canvas element and its context
var canvas = document.getElementById('sketchpad');
var context = canvas.getContext('2d');
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function (coors) {
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function (coors) {
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function (coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event) {
var type = null;
// map mouse events to touch events
switch(event.type){
case "mousedown":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchstart";
break;
case "mousemove":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchmove";
break;
case "mouseup":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchend";
break;
}
// touchend clear the touches[0], so we need to use changedTouches[0]
var coors;
if(event.type === "touchend") {
coors = {
x: event.changedTouches[0].pageX,
y: event.changedTouches[0].pageY
};
}
else {
// get the touch coordinates
coors = {
x: event.touches[0].pageX,
y: event.touches[0].pageY
};
}
type = type || event.type
// pass the coordinates to the appropriate handler
drawer[type](coors);
}
// detect touch capabilities
var touchAvailable = ('createTouch' in document) || ('ontouchstart' in window);
// attach the touchstart, touchmove, touchend event listeners.
if(touchAvailable){
canvas.addEventListener('touchstart', draw, false);
canvas.addEventListener('touchmove', draw, false);
canvas.addEventListener('touchend', draw, false);
}
// attach the mousedown, mousemove, mouseup event listeners.
else {
canvas.addEventListener('mousedown', draw, false);
canvas.addEventListener('mousemove', draw, false);
canvas.addEventListener('mouseup', draw, false);
}
// prevent elastic scrolling
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
}, false); // end body.onTouchMove
}, false); // end window.onLoad
function generate(){
var newCanvas = document.createElement('canvas');
newCanvas.width = 400;
newCanvas.height = 400;
document.getElementById('container').appendChild(newCanvas);
ctx = newCanvas.getContext('2d');
}
//]]>
here is jsfiddle http://jsfiddle.net/regeme/WVUwn/
ps:drawing not displayed on jsfiddle however it works on my localhost I have totally no idea about it , anyway what I need is generate function , I did but I think I am missing something..
Any ideas? thanks..
Below is a function I wrote to dynamically create canvas.
If the canvas already exists (same ID) then that canvas is returned.
The pixelRatio parameter can be defaulted to 1. It's used for setting the correct size on retina displays (so for iPhone with Retina the value would be 2)
function createLayer(sizeW, sizeH, pixelRatio, id, zIndex) {
// *** An id must be given.
if (typeof id === undefined) {
return false;
}
// *** If z-index is less than zero we'll make it a buffer image.
isBuffer = (zIndex < 0) ? true : false;
// *** If the canvas exist, clean it and just return that.
var element = document.getElementById(id);
if (element !== null) {
return element;
}
// *** If no zIndex is passed in then default to 0.
if (typeof zIndex === undefined || zIndex < 0) {
zIndex = 0;
}
var canvas = document.createElement('canvas');
canvas.width = sizeW;
canvas.height = sizeH;
canvas.id = id;
canvas.style.width = sizeW*pixelRatio + "px";
canvas.style.height = sizeH*pixelRatio + "px";
canvas.style.position = "absolute";
canvas.style.zIndex = zIndex;
if (!isBuffer) {
var body = document.getElementsByTagName("body")[0];
body.appendChild(canvas);
}
return canvas;
}
Change the jsfiddle option
"onLoad"
to
"No Wrap - in <body>"
EDIT: See also similar question over here: Uncaught ReferenceError for a function defined in an onload function
JSFiddle options: http://doc.jsfiddle.net/basic/introduction.html#frameworks-and-extensions
This is the working update of your JSFIDDLE
javascript:
document.getElementById('generate').addEventListener('mousedown', generate, false);
I guess this is what you want.
I've just added an eventListener to your button in javascript code itself.
P.S.: I've also added black background color to canvas to show it on white background.
I was trying to create a sample paint application using HTML 5 canvas. Then I added a button to redraw what user had drawn earlier. I am not sure what I am doing wrong or may be completely wrong. When I click redraw button multiple times it generates some magical animation by drawing lines all over. Even though if I log the starting point of drawing the image its same every time.
Demo: http://jsfiddle.net/BW57H/6/
Steps to reproduce:
Draw some circle or rectangle or something by clicking the mouse and dragging it on the rectangular box. Then click reset and redraw , click redraw couple of times after that and see the result.
I am not sure what I have done. I have not read a lot about Canvas. But I am curious to know what is going on here. Thanks.
html
<body>
<canvas id="paint" width="600px" height="300px"></canvas>
<div id="controls">
<button name="reset" id="reset">Reset</button>
<button name="redraw" id="redraw">Re-Draw</button>
</div>
</body>
css
#paint{
border: solid;
}
js
$(document).ready(function(){
var x, y, context, painter;
var xCounter = 0 , yCounter = 0;
var xarray = [];
var yarray = [];
function init(){
while(document.getElementById("paint") === undefined){
//do nothing
}
console.log("Loaded document now registering events for canvas");
var canvas = document.getElementById("paint");
context = canvas.getContext('2d');
painter = new Painter();
canvas.addEventListener('mousedown', capture, false);
canvas.addEventListener('mouseup', capture, false);
canvas.addEventListener('mousemove', capture, false);
document.getElementById("reset").addEventListener("click",function(){ clearCanvas(canvas);}, false);
document.getElementById("redraw").addEventListener("click", function(){
autoDraw();
}, false);
}
function clearCanvas(canvas){
context.save();
// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
context.restore();
};
function capture(event){
if(event.which !== 1){
return;
}
x = event.layerX;
y = event.layerY;
switch(event.type){
case 'mousedown':
painter.startPaint(event);
break;
case 'mouseup':
painter.endPaint(event);
break;
case 'mousemove':
painter.paint(event);
break;
}
};
var Painter = function(){
var self = this;
self.paintStarted = false;
self.startPaint = function(event){
self.resetRecordingParams();
self.paintStarted = true;
context.beginPath();
context.moveTo(x,y);
self.record();
}
self.endPaint = function(event){
self.paintStarted = false;
self.record();
self.paint(event)
}
self.paint = function(event){
if(self.paintStarted){
context.lineTo(x,y);
context.stroke();
self.record();
}
}
self.record = function(){
xarray[xCounter++] = x;
yarray[yCounter++] = y;
}
self.resetRecordingParams = function(){
xarray = [];
yarray = [];
xCounter = 0;
yCounter= 0;
}
return self;
}
function autoDraw(){
context.beginPath();
console.log('starting at: '+xarray[0]+','+yarray[0]);
context.moveTo(xarray[0],yarray[0]);
for (var i = 0; i < xarray.length; i++) {
setTimeout(drawLineSlowly, 1000+(i*20), i);
};
}
function drawLineSlowly(i)
{
context.lineTo(xarray[i],yarray[i]);
context.stroke();
}
init();
});
You don't have any kind of check to see whether or not you are already drawing, so here is your code with those changes commented, as well as the real-pixel-location fixes (http://jsfiddle.net/upgradellc/htJXy/1/):
$(document).ready(function(){
var x, y, context, painter, canvas;
var xCounter = 0 , yCounter = 0;
var xarray = [];
var yarray = [];
function init(){
while(document.getElementById("paint") === undefined){
//do nothing
}
console.log("Loaded document now registering events for canvas");
canvas = document.getElementById("paint");
context = canvas.getContext('2d');
painter = new Painter();
canvas.addEventListener('mousedown', capture, false);
canvas.addEventListener('mouseup', capture, false);
canvas.addEventListener('mousemove', capture, false);
document.getElementById("reset").addEventListener("click",function(){ clearCanvas(canvas);}, false);
document.getElementById("redraw").addEventListener("click", function(){
autoDraw();
}, false);
}
function clearCanvas(canvas){
context.save();
// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
context.restore();
};
function capture(event){
if(event.which !== 1){
return;
}
tempPos = getMousePos(canvas, event);
x = tempPos.x;
y = tempPos.y;
switch(event.type){
case 'mousedown':
painter.startPaint(event);
break;
case 'mouseup':
painter.endPaint(event);
break;
case 'mousemove':
painter.paint(event);
break;
}
};
var Painter = function(){
var self = this;
self.paintStarted = false;
//this keeps track of whether or not we are currently auto drawing
self.currentlyAutoDrawing = false;
self.startPaint = function(event){
self.resetRecordingParams();
self.paintStarted = true;
context.beginPath();
context.moveTo(x,y);
self.record();
}
self.endPaint = function(event){
self.paintStarted = false;
self.record();
self.paint(event);
}
self.paint = function(event){
if(self.paintStarted){
context.lineTo(x,y);
context.stroke();
self.record();
}
}
self.record = function(){
xarray[xCounter++] = x;
yarray[yCounter++] = y;
}
self.resetRecordingParams = function(){
xarray = [];
yarray = [];
xCounter = 0;
yCounter= 0;
}
return self;
}
function autoDraw(){
context.beginPath();
//If we are already auto-drawing, then we should just return instead of starting another drawing loop cycle
if(painter.currentlyAutoDrawing){
console.log("painter is already auto drawing");
return;
}
painter.currentlyAutoDrawing = true;
console.log('starting at: '+xarray[0]+','+yarray[0]);
context.moveTo(xarray[0],yarray[0]);
for (var i = 0; i < xarray.length; i++) {
setTimeout(drawLineSlowly, 1000+(i*20), i);
};
}
function drawLineSlowly(i)
{
//when we reach the last element in the array, update painter with the fact that autodrawing is now complete
if(xarray.length == i+1){
painter.currentlyAutoDrawing=false;
}
console.log(xarray.length+" "+i);
context.lineTo(xarray[i],yarray[i]);
context.stroke();
}
function getMousePos(canv, evt) {
var rect = canv.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
init();
});
Obviously you don't stop the previous timeout loop before you start a new...
Use setInterval instead of setTimeout, and clearInterval by next push. So I think the problem is not with the canvas, just with your redraw animation. Btw it is strange because there is some difference between the redraw and the original draw...
var drawInterval = null;
function autoDraw(){
if (drawInterval) {
//here your can reset the previous - still running - redraw
clearInterval(drawInterval);
}
context.beginPath();
console.log('starting at: '+xarray[0]+','+yarray[0]);
context.moveTo(xarray[0],yarray[0]);
var i=0;
setInterval(function (){
++i;
if (i<xarray.length)
drawLineSlowly(i);
else
clearInterval(drawInterval);
},20);
}
note:
There is still bug in the redraw, but at least it does not kill the browser...
Btw the strange "animation" is because you does not check by redraw if you are currently drawing or not, so you start draw and redraw together and they interfere... You have to stop redraw when you start drawing.