Ball-Box Collision Detection - javascript

var canvas,cxt,h,w,mousePos;
var player= {
x: 10,
y: 10,
height: 20,
width: 20,
color: 'black'
};
function init(){
canvas= document.querySelector('#style');
cxt= canvas.getContext('2d');
h= canvas.height;
w= canvas.width;
createBalls(10);
main();
}
function createBalls(c){
ball= [];
var i;
for(i=0;i<c;i++){
var k= {
x: h/2,
y: w/2,
color: colorGenerate(),
radius: 5+Math.round(30*Math.random()),
a: -5+Math.round(10*Math.random()),
b: -5+Math.round(10*Math.random())
}
ball.push(k);
}
}
function main(){
cxt.clearRect(0,0,h,w);
canvas.addEventListener('mousemove',function(evt){
mousePos= getMousePos(canvas,evt);
});
createPlayer();
draw(ball.length);
ballAlive();
move(ball.length);
movePlayer();
requestAnimationFrame(main);
}
function ballAlive(){
cxt.save();
cxt.font="30px Arial";
if(ball.length==0) cxt.fillText("You Win",20,20);
else cxt.fillText(ball.length,20,40);
cxt.restore();
}
function getMousePos(canvas,evt){
var rect= canvas.getBoundingClientRect();
return{
x: evt.clientX-rect.left,
y: evt.clientY-rect.top
}
}
function createPlayer(){
cxt.save();
cxt.translate(0,0);
cxt.fillStyle= player.color;
cxt.fillRect(player.x,player.y,player.height,player.width);
cxt.restore();
}
function movePlayer(){
if(mousePos !== undefined){
player.x= mousePos.x;
player.y= mousePos.y;
}
}
function draw(d){
var i;
for(i=0;i<d;i++){
cxt.save();
cxt.translate(0,0);
cxt.beginPath();
cxt.fillStyle= ball[i].color;
cxt.arc(ball[i].x,ball[i].y,ball[i].radius,0,2*Math.PI)
cxt.fill();
cxt.restore();
}
}
function move(m){
var i;
for(i=0;i<m;i++){
ball[i].x+= ball[i].a;
ball[i].y+= ball[i].b;
checkCollision(ball[i]);
checkCollisionPlayer(ball[i],i);
}
}
function checkCollision(n){
if(n.x+n.radius>w){
n.a= -n.a;
n.x= w-n.radius;
}
else if(n.x-n.radius<0){
n.a= -n.a;
n.x= n.radius;
}
if(n.y+n.radius>h){
n.b= -n.b;
n.y= h-n.radius;
}
else if(n.y-n.radius<0){
n.b= -n.b;
n.y= n.radius;
}
}
function checkCollisionPlayer(n,j){
if(overlap(n.x,n.y,n.radius,player.x,player.y,player.height,player.width)){
ball.splice(j,1);
}
}
function overlap(cx,cy,r,px,py,ph,pw){
var testX= cx;
var testY= cy;
// THESE LINES ARE FOR MOVING THE BALLS TOWARDS THE PLAYER
if(testX<px) testX=px;
if(testX>(px+pw)) testX=px+pw;
if(testY<py) testy=py;
if(testY>(py+ph)) testY=py+ph;
//DISTANCE FORMULA FOR CHECKING THE OVERLAPING BETWEEN THE BOX AND CIRCLE
return((cx-px)*(cx-px)+(cy-py)*(cy-py)<r*r);
}
function colorGenerate(){
var col= ['green','blue','pink','red','brown','yellow','black','orange','grey','golden'];
var i= Math.round((col.length-1)*Math.random()); //RETURN VALUES FROM 0 TO 9
return col[i];
}
#style{
border: 4px dotted green;
}
<!DOCTYPE html>
<html lang= 'en-us'>
<head>
<title>Feed The Monster</title>
</head>
<body onload= 'init();'>
<canvas id= 'style' height= '400' width= '400'>
Your browser does not support canvas...
</canvas>
</body>
</html>
I am getting error in my code as soon as the first collision takes place between the player(box) and the balls.
The only part which is giving error is the splice() function. If I comment the splice() function then code is not showing any error.
But if I use splice() function part in my code then it is showing error in the move function and I don't know why this is happening.
Please help me…

var canvas,cxt,h,w,mousePos;
var player= {
x: 10,
y: 10,
height: 20,
width: 20,
color: 'black'
};
function init(){
canvas= document.querySelector('#style');
cxt= canvas.getContext('2d');
h= canvas.height;
w= canvas.width;
createBalls(10);
main();
}
function createBalls(c){
ball= [];
var i;
for(i=0;i<c;i++){
var k= {
x: h/2,
y: w/2,
color: colorGenerate(),
radius: 5+Math.round(30*Math.random()),
a: -5+Math.round(10*Math.random()),
b: -5+Math.round(10*Math.random())
}
ball.push(k);
}
}
function main(){
cxt.clearRect(0,0,h,w);
canvas.addEventListener('mousemove',function(evt){
mousePos= getMousePos(canvas,evt);
});
createPlayer();
draw(ball.length);
ballAlive();
move(ball);
movePlayer();
requestAnimationFrame(main);
}
function ballAlive(){
cxt.save();
cxt.font="30px Arial";
if(ball.length==0) cxt.fillText("You Win",20,20);
else cxt.fillText(ball.length,20,40);
cxt.restore();
}
function getMousePos(canvas,evt){
var rect= canvas.getBoundingClientRect();
return{
x: evt.clientX-rect.left,
y: evt.clientY-rect.top
}
}
function createPlayer(){
cxt.save();
cxt.translate(0,0);
cxt.fillStyle= player.color;
cxt.fillRect(player.x,player.y,player.height,player.width);
cxt.restore();
}
function movePlayer(){
if(mousePos !== undefined){
player.x= mousePos.x;
player.y= mousePos.y;
}
}
function draw(d){
var i;
for(i=0;i<d;i++){
cxt.save();
cxt.translate(0,0);
cxt.beginPath();
cxt.fillStyle= ball[i].color;
cxt.arc(ball[i].x,ball[i].y,ball[i].radius,0,2*Math.PI)
cxt.fill();
cxt.restore();
}
}
function move(m){
var i;
for(i=0;i<m.length;i++){
ball[i].x+= ball[i].a;
ball[i].y+= ball[i].b;
checkCollision(ball[i]);
checkCollisionPlayer(ball[i],i);
}
}
function checkCollision(n){
if(n.x+n.radius>w){
n.a= -n.a;
n.x= w-n.radius;
}
else if(n.x-n.radius<0){
n.a= -n.a;
n.x= n.radius;
}
if(n.y+n.radius>h){
n.b= -n.b;
n.y= h-n.radius;
}
else if(n.y-n.radius<0){
n.b= -n.b;
n.y= n.radius;
}
}
function checkCollisionPlayer(n,j){
if(overlap(n.x,n.y,n.radius,player.x,player.y,player.height,player.width)){
ball.splice(j,1);
}
}
function overlap(cx,cy,r,px,py,ph,pw){
var testX= cx;
var testY= cy;
// THESE LINES ARE FOR MOVING THE BALLS TOWARDS THE PLAYER
if(testX<px) testX=px;
if(testX>(px+pw)) testX=px+pw;
if(testY<py) testy=py;
if(testY>(py+ph)) testY=py+ph;
//DISTANCE FORMULA FOR CHECKING THE OVERLAPING BETWEEN THE BOX AND CIRCLE
return((cx-px)*(cx-px)+(cy-py)*(cy-py)<r*r);
}
function colorGenerate(){
var col= ['green','blue','pink','red','brown','yellow','black','orange','grey','golden'];
var i= Math.round((col.length-1)*Math.random()); //RETURN VALUES FROM 0 TO 9
return col[i];
}
#style{
border: 4px dotted green;
}
<!DOCTYPE html>
<html lang= 'en-us'>
<head>
<title>Feed The Monster</title>
</head>
<body onload= 'init();'>
<canvas id= 'style' height= '400' width= '400'>
Your browser does not support canvas...
</canvas>
</body>
</html>
If you pass the array as an argument in the move() function not the length of the array then your code will work perfectly.

This appears to be to do with the loop inside your move function. You call it with the parameter m representing the lengh of the ball array - but the function checkCollisionPlayer which is called within that can remove a ball from the array if there is a collision. This means that the array is now shorter, so whenever you try to access a property on ball[m-1] later in the loop, as you will, you'll get this error.
I'm sure there are lots of different ways to fix this. The easiest I can think of (which I have successfully used myself in making a browser game) is to, instead of directly deleting the balls from the array upon collision, set a property on them to mark them as "deleted". Then add a "cleanup" function onto the end of the loop to filter the array into only those that are not marked as deleted. This ensures that every object you are looping over actually exists, while still changing the length dynamically to reflect the game state.

Related

Why isnt the .clearRect function working properly?

So im making this space invaders game in javascript. My problem right now is that when the enemies move left and right they dont clear rect so you can see thier footage as shown here:
I just wanted to know what i could fix to solve this problem. I would love if you could help here is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Space Invaders</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<style>
canvas{
position: absolute;
top:0px;
left:0px;
background: transparent;
}
#backgroundCanvas{
background-color: black;
}
</style>
</head>
<body>
<canvas id="backgroundCanvas" width="550" height="600"></canvas>
<canvas id="playerCanvas" width="550" height="600"></canvas>
<canvas id="enemiesCanvas" width="550" height="600"></canvas>
<script>
(function(){
$(document).ready(function(){
var game = {};
game.stars = [];
game.width = 550;
game.height = 600;
game.images = [];
game.doneImages = 0;
game.requiredImages = 0;
game.keys = [];
game.enemies = [];
game.count = 0;
game.division = 48;
game.left = false;
game.enemySpeed = 3;
game.contextBackground = document.getElementById("backgroundCanvas").getContext('2d');
game.contextPlayer = document.getElementById("playerCanvas").getContext('2d');
game.contextEnemies = document.getElementById("enemiesCanvas").getContext('2d');
game.player = {
x: game.width / 2 -50,
y: game.height - 110,
width:100,
height:100,
speed: 3,
rendered: false
}
$(document).keydown(function(e){
game.keys[e.keyCode ? e.keyCode : e.which] = true;
})
$(document).keyup(function(e){
delete game.keys[e.keyCode ? e.keyCode : e.which];
})
/*
up -38
down-40
left -37
right-39
w-87
a-65
s-83
d-68
space-32
*/
function init(){
for(i=0; i<600;i++){
game.stars.push({
x:Math.floor(Math.random()* game.width),
y:Math.floor(Math.random()* game.height),
size: Math.random()*5
})
}
for(y=0;y<5;y++){
for(x =0;x<5;x++){
game.enemies.push({
x: (x*70) + (70*x) + 10,
y: (y*70) + (10*y) + 40,
width:70,
height: 70,
image:1
})
}
}
loop();
}
function addStars(num){
for(i=0; i<num;i++){
game.stars.push({
x:Math.floor(Math.random()* game.width),
y:game.height+10,
size: Math.random()*5
})
}
}
function update(){
addStars(1);
game.count++;
for(i in game.stars){
if(game.stars[i].y <= -5){
game.stars.splice(i,1);
}
game.stars[i].y--;
}
if(game.keys[37] || game.keys[65]){
if(game.player.x>=0){
game.player.x-=game.player.speed;
game.player.rendered = false;
}
}
if(game.keys[39] || game.keys[68]){
if(game.player.x<=500-50){
game.player.x+=game.player.speed;
game.player.rendered = false;
}
}
if(game.count % game.division == 0){
game.left = !game.left;
}
for(i in game.enemies){
if(game.left){
game.enemies[i].x-=game.enemySpeed;
}else{
game.enemies[i].x+=game.enemySpeed;
}
}
}
function render(){
game.contextBackground.clearRect(0,0,game.width,game.height)
game.contextBackground.fillStyle = "white";
for(i in game.stars){
var star = game.stars[i];
game.contextBackground.fillRect(star.x,star.y,star.size,star.size);
}
if(!game.player.rendered){
game.contextPlayer.clearRect(0, 0, game.width, game.height);
game.contextPlayer.drawImage(game.images[0], game.player.x, game.player.y, game.player.width, game.player.height);
game.player.rendered = true;
}
for(i in game.enemies){
var enemy = game.enemies[i];
game.contextEnemies.clearRect(enemy.x, enemy.y, enemy.width, enemy.height);
game.contextEnemies.drawImage(game.images[enemy.image], enemy.x, enemy.y, enemy.width, enemy.height);
}
}
function loop(){
requestAnimFrame(function(){
loop();
});
update();
render();
}
function initImages(paths){
game.requiredImages = paths.length;
for(i in paths){
var img = new Image;
img.src = paths[i];
game.images[i] = img;
game.images[i].onload = function(){
game.doneImages++;
}
}
}
function checkImages(){
if(game.doneImages>=game.requiredImages){
init();
}
else{
setTimeout(function(){
checkImages();
},1)
}
}
game.contextBackground.font = "bold 50px monaco"
game.contextBackground.fillStyle = "white";
game.contextBackground.fillText("loading" , game.width/2-100 ,game.height/2-25)
initImages(["player.gif", "enemy.png", "bullet.png","Image1.png","Image2.png"])
checkImages();
});
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
})();
</script>
</body>
</html>
Right now, you clear contextEnemies with this
game.contextEnemies.clearRect(enemy.x, enemy.y, enemy.width, enemy.height);
The problem here is that you are clearing the square where you are about to draw the enemy, not the square where it was drawn last time. You need to keep track of where you drew the enemy at previous render (enemy.x), and clear that part.
Either you add something like enemy.lastx to the enemy object, to keep track of it's previous position. Or the easiest solution would be to just clear the entire contextEnemies as you are doing with contextBackground.
game.contextBackground.clearRect(0, 0, game.width, game.height);
game.contextEnemies.clearRect(0, 0, game.width, game.height);
Hope that helps. Cheers!

How do I access JS functions from an array storage?

I'm working with custom "buttons" in html5 canvas. Because I've got so many buttons I think it makes more sense to store then in an array. My dilemma is, I'm not quite sure on how to implement custom functions which are 'attached' to a particular button. I've seen this posting, not exactly sure if that is applicable here.
Clearly btn[i].function+"()"; isn't cutting it.
Here's a JSFiddle.
How can I store a custom function within the button array, and successfully call it upon mouseclick?
Code follows:
<!doctype html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body {background-color: black; margin: 8px; }
#canvas {border: 1px solid gray;}
</style>
<script>
$(function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
var cw = canvas.width;
var ch = canvas.height;
var btn = [{
x: 50,
y: 100,
width: 80,
height: 50,
display: "Hello There",
function: "functionA" // Is this right?
}, {
x: 150,
y: 100,
width: 80,
height: 50,
display: "Why Not?",
function: "functionB" // Is this right?
}, {
x: 250,
y: 100,
width: 80,
height: 50,
display: "Let's Go!",
function: "functionC" // Is this right?
}];
function drawAll() {
ctx.clearRect(0, 0, cw, ch);
for (var i = 0; i < btn.length; i++) {
drawButton(ctx, btn[i].x, btn[i].y, btn[i].width, btn[i].height, btn[i].display);
}
}
drawAll();
// listen for mouse events
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
function handleMouseDown(e) {
// tell the browser we'll handle this event
e.preventDefault();
e.stopPropagation();
// save the mouse position
lastX = parseInt(e.clientX - offsetX);
lastY = parseInt(e.clientY - offsetY);
// hit all existing buttons
for (var i = 0; i < btn.length; i++) {
if ((lastX < (btn[i].x + btn[i].width)) &&
(lastX > btn[i].x) &&
(lastY < (btn[i].y + btn[i].height)) &&
(lastY > btn[i].y)) {
// execute button function
btn[i].function+"()"; // obviously this is just wrong.
console.log("Button #" + (i + 1) + " has been clicked!!" );
}
}
}
function drawButton(context, x, y, width, height, text) {
var myGradient = context.createLinearGradient(0, y, 0, y + height);
myGradient.addColorStop(0, '#999999');
myGradient.addColorStop(1, '#222222');
context.fillStyle = myGradient;
context.fillRect(x, y, width, height);
context.fillStyle = 'white';
// textAlign aligns text horizontally relative to placement
context.textAlign = 'center';
// textBaseline aligns text vertically relative to font style
context.textBaseline = 'middle';
context.font = 'bold 15px sans-serif';
context.fillText(text, x + width / 2, y + height / 2);
}
function functionA() {
alert("Yipee Function A!");
}
function functionB() {
alert("Yowza, it's Function B!");
}
function functionC() {
alert("Now showing Function C!");
}
})
</script>
</head>
<body>
<canvas id="canvas" width=400 height=300></canvas>
</body>
</html>
Try to change your buttons to:
{
display: "Hello There",
action: functionA
}
And to invoke:
btn[i].action();
I changed the name function to action because function is a reserved word and cannot be used as an object property name.
You can store references to the functions in your array, just lose the " signs around their names (which currently makes them strings instead of function references), creating the array like this:
var btn = [{
x: 50,
y: 100,
width: 80,
height: 50,
display: "Hello There",
function: functionA
}, {
x: 150,
y: 100,
width: 80,
height: 50,
display: "Why Not?",
function: functionB
}]
Then you can call either by writing btn[i].function().
Don't put the name of the function in the array, put a reference to the function itself:
var btn = [{
x: 50,
y: 100,
width: 80,
height: 50,
display: "Hello There",
'function': functionA
}, {
x: 150,
y: 100,
width: 80,
height: 50,
display: "Why Not?",
'function': functionB
}, {
x: 250,
y: 100,
width: 80,
height: 50,
display: "Let's Go!",
'function': functionC
}];
To call the function, you do:
btn[i]['function']();
I've put function in quotes in the literal, and used array notation to access it, because it's a reserved keyword.

How do I hand draw on canvas with JavaScript?

Question
How do I draw free (using my mouse / fingers) on a canvas element like you can do it in paint with a pencil?
About this question
There are a lot of questions that want to achieve free hand drawing on canvas:
draw by mouse with HTML5 Canvas
KineticJS - Draw free with mouse
Free drawing on canvas using fabric.js
Sketching with JS
Paint canvas not working properly
Mouse position on canvas painting
Implementing smooth sketching and drawing on the element
So I thought it would be a good idea to make a reference question, where every answer is community wiki and contains a explanation for exactly one JavaScript library / pure JavaScript how to do paint on canvas.
Structure of answers
The answers should be community wiki and use the following template:
## [Name of library](Link to project page)
### Simple example
A basic, complete example. That means it has to contain HTML
and JavaScript. You can start with this:
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<script type='text/javascript' src='http://cdnjs.com/[your library]'></script>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
<script type='text/javascript'>
window.onload=function(){
// TODO: Adjust
}
</script>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
If possible, this example should work with both, mouse and touch events.
[JSFiddle](Link to code on jsfiddle.net)
This solution works with:
<!-- Please test it the following way: Write "Hello World"
Problems that you test this way are:
* Does it work at all?
* Are lines separated?
* Does it get slow when you write too much?
-->
* Desktop computers:
* [Browser + Version list]
* Touch devices:
* [Browser + Version list] on [Device name]
### Import / Export
Some explanations how to import / export user drawn images.
### Line smoothing
Explanations about how to manipulate the line the user draws.
This can include:
* Bézier curves
* Controlling thickness of lines
Fabric.js
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js'></script>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
<script type='text/javascript'>
window.onload=function(){
var canvas = new fabric.Canvas('sheet');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.width = 5;
canvas.freeDrawingBrush.color = "#ff0000";
}
</script>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
JSFiddle - Demo
The width of the lines can be controlled with canvas.freeDrawingBrush.width.
The color of the lines can be controlled with canvas.freeDrawingBrush.color.
This solution works with:
Desktop computers:
Chrome 33
Firefox 28
Touch devices:
Chrome 34 on Nexus 4
Opera 20 on Nexus 4
Firefox 28 on Nexus 4
Import / Export
Is only possible by serializing the complete canvas, see Tutorial
Line smoothing
Is done automatically and it seems not to be possible to deactivate it.
Plain JavaScript
Simple example
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
<script type='text/javascript'>
/*jslint browser:true */
"use strict";
var context = document.getElementById('sheet').getContext("2d");
var canvas = document.getElementById('sheet');
context = canvas.getContext("2d");
context.strokeStyle = "#ff0000";
context.lineJoin = "round";
context.lineWidth = 5;
var clickX = [];
var clickY = [];
var clickDrag = [];
var paint;
/**
* Add information where the user clicked at.
* #param {number} x
* #param {number} y
* #return {boolean} dragging
*/
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
/**
* Redraw the complete canvas.
*/
function redraw() {
// Clears the canvas
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
for (var i = 0; i < clickX.length; i += 1) {
if (!clickDrag[i] && i == 0) {
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else if (!clickDrag[i] && i > 0) {
context.closePath();
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else {
context.lineTo(clickX[i], clickY[i]);
context.stroke();
}
}
}
/**
* Draw the newly added point.
* #return {void}
*/
function drawNew() {
var i = clickX.length - 1
if (!clickDrag[i]) {
if (clickX.length == 0) {
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else {
context.closePath();
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
}
} else {
context.lineTo(clickX[i], clickY[i]);
context.stroke();
}
}
function mouseDownEventHandler(e) {
paint = true;
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
if (paint) {
addClick(x, y, false);
drawNew();
}
}
function touchstartEventHandler(e) {
paint = true;
if (paint) {
addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, false);
drawNew();
}
}
function mouseUpEventHandler(e) {
context.closePath();
paint = false;
}
function mouseMoveEventHandler(e) {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
if (paint) {
addClick(x, y, true);
drawNew();
}
}
function touchMoveEventHandler(e) {
if (paint) {
addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, true);
drawNew();
}
}
function setUpHandler(isMouseandNotTouch, detectEvent) {
removeRaceHandlers();
if (isMouseandNotTouch) {
canvas.addEventListener('mouseup', mouseUpEventHandler);
canvas.addEventListener('mousemove', mouseMoveEventHandler);
canvas.addEventListener('mousedown', mouseDownEventHandler);
mouseDownEventHandler(detectEvent);
} else {
canvas.addEventListener('touchstart', touchstartEventHandler);
canvas.addEventListener('touchmove', touchMoveEventHandler);
canvas.addEventListener('touchend', mouseUpEventHandler);
touchstartEventHandler(detectEvent);
}
}
function mouseWins(e) {
setUpHandler(true, e);
}
function touchWins(e) {
setUpHandler(false, e);
}
function removeRaceHandlers() {
canvas.removeEventListener('mousedown', mouseWins);
canvas.removeEventListener('touchstart', touchWins);
}
canvas.addEventListener('mousedown', mouseWins);
canvas.addEventListener('touchstart', touchWins);
</script>
</body>
</html>
JSFiddle
The width of the lines can be controlled with context.lineWidth.
The color of the lines can be controlled with strokeStyle.
This solution works with:
Desktop computers:
Chrome 33
Firefox 28
Touch devices:
Firefox 28 on Nexus 4
It does not work with
Touch devices:
Chrome 34 / Opera 20 on Nexus 4 (see issue)
Import / Export
Importing and exporting the image can be done by importing / exporting clickX, clickY and clickDrag.
Line smoothing
Can eventually be done by replacing lineTo() with bezierCurveTo()
Plain JS - ES6
Simple example
Plain Javascript example above has some serious issues: it does not reflect the comments objections, the paint state is redundant, events are not unhooked properly, the redraw() function is not used, it can be simplified a lot and it doesn't work with modern syntax. The fix is here:
var canvas = document.getElementById('sheet'), g = canvas.getContext("2d");
g.strokeStyle = "hsl(208, 100%, 43%)";
g.lineJoin = "round";
g.lineWidth = 1;
g.filter = "blur(1px)";
const
relPos = pt => [pt.pageX - canvas.offsetLeft, pt.pageY - canvas.offsetTop],
drawStart = pt => { with(g) { beginPath(); moveTo.apply(g, pt); stroke(); }},
drawMove = pt => { with(g) { lineTo.apply(g, pt); stroke(); }},
pointerDown = e => drawStart(relPos(e.touches ? e.touches[0] : e)),
pointerMove = e => drawMove(relPos(e.touches ? e.touches[0] : e)),
draw = (method, move, stop) => e => {
if(method=="add") pointerDown(e);
canvas[method+"EventListener"](move, pointerMove);
canvas[method+"EventListener"](stop, g.closePath);
};
canvas.addEventListener("mousedown", draw("add","mousemove","mouseup"));
canvas.addEventListener("touchstart", draw("add","touchmove","touchend"));
canvas.addEventListener("mouseup", draw("remove","mousemove","mouseup"));
canvas.addEventListener("touchend", draw("remove","touchmove","touchend"));
<canvas id="sheet" width="400" height="400" style="border: 1px solid black"></canvas>
Support
It should work everywhere today. I could be further simplified by pointer events, but Safari lacks support for it as of 2021.
Import / Export
For import, use g.drawImage()
g.drawImage(img, 0, 0);
For export, see canvas.toBlob()
function save(blob) {
var fd = new FormData();
fd.append("myFile", blob);
// handle formData to your desire here
}
canvas.toBlob(save,'image/jpeg');
Line smoothing
For antialiasing, See blur() from SVG filters; if you import, don't forget to apply it AFTER the image is imported
context.filter = "blur(1px)";
Paper.js
Simple example
<!DOCTYPE html>
<html>
<head>
<title>Paper.js example</title>
<script type='text/javascript' src='http://paperjs.org/assets/js/paper.js'></script>
<style type='text/css'>
#sheet {
border:1px solid black;
}
</style>
</head>
<body>
<script type="text/paperscript" canvas="sheet">
var path;
function onMouseDown(event) {
// If we produced a path before, deselect it:
if (path) {
path.selected = false;
}
// Create a new path and set its stroke color to black:
path = new Path({
segments: [event.point],
strokeColor: 'black',
strokeWidth: 3
});
}
// While the user drags the mouse, points are added to the path
// at the position of the mouse:
function onMouseDrag(event) {
path.add(event.point);
}
// When the mouse is released, we simplify the path:
function onMouseUp(event) {
path.simplify();
}
</script>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
JSFiddle
The width of the lines can be controlled with strokeWidth.
The color of the lines can be controlled with strokeColor.
This solution works with:
Desktop computers:
Chrome 33
Import / Export
?
Line smoothing
Line smoothing can be done by adjusting path.simplify();.
EaselJs
Simple example
A basic, complete example. That means it has to contain HTML
and JavaScript. You can start with this:
<!DOCTYPE html>
<html>
<head>
<title>EaselJS example</title>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/EaselJS/0.7.1/easeljs.min.js"></script>
<script>
var canvas, stage;
var drawingCanvas;
var oldPt;
var oldMidPt;
var color;
var stroke;
var index;
function init() {
if (window.top != window) {
document.getElementById("header").style.display = "none";
}
canvas = document.getElementById("sheet");
index = 0;
//check to see if we are running in a browser with touch support
stage = new createjs.Stage(canvas);
stage.autoClear = false;
stage.enableDOMEvents(true);
createjs.Touch.enable(stage);
createjs.Ticker.setFPS(24);
drawingCanvas = new createjs.Shape();
stage.addEventListener("stagemousedown", handleMouseDown);
stage.addEventListener("stagemouseup", handleMouseUp);
stage.addChild(drawingCanvas);
stage.update();
}
function stop() {}
function handleMouseDown(event) {
color = "#ff0000";
stroke = 5;
oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
oldMidPt = oldPt;
stage.addEventListener("stagemousemove" , handleMouseMove);
}
function handleMouseMove(event) {
var midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1);
drawingCanvas.graphics.clear().setStrokeStyle(stroke, 'round', 'round').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPt.x;
oldMidPt.y = midPt.y;
stage.update();
}
function handleMouseUp(event) {
stage.removeEventListener("stagemousemove" , handleMouseMove);
}
</script>
</head>
<body onload="init();">
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
Demo
The interesting parts in the documentation are:
EaselJS: A starting point for getting into EaselJS.
Stage Class:
This solution works with:
Desktop computers:
Chrome 33
Firefox 28
Touch devices:
Chrome 34 / Firefox 28 / Opera 20 on Nexus 4
Import / Export
?
Line smoothing
?
Here, try my canvas free drawing and erase.
https://jsfiddle.net/richardcwc/d2gxjdva/
//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
var tooltype = 'draw';
//Mousedown
$(canvas).on('mousedown', function(e) {
last_mousex = mousex = parseInt(e.clientX-canvasx);
last_mousey = mousey = parseInt(e.clientY-canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function(e) {
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function(e) {
mousex = parseInt(e.clientX-canvasx);
mousey = parseInt(e.clientY-canvasy);
if(mousedown) {
ctx.beginPath();
if(tooltype=='draw') {
ctx.globalCompositeOperation = 'source-over';
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
} else {
ctx.globalCompositeOperation = 'destination-out';
ctx.lineWidth = 10;
}
ctx.moveTo(last_mousex,last_mousey);
ctx.lineTo(mousex,mousey);
ctx.lineJoin = ctx.lineCap = 'round';
ctx.stroke();
}
last_mousex = mousex;
last_mousey = mousey;
//Output
$('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});
//Use draw|erase
use_tool = function(tool) {
tooltype = tool; //update
}
canvas {
cursor: crosshair;
border: 1px solid #000000;
}
<canvas id="canvas" width="800" height="500"></canvas>
<input type="button" value="draw" onclick="use_tool('draw');" />
<input type="button" value="erase" onclick="use_tool('erase');" />
<div id="output"></div>
(Disclaimer: I wrote this library)
Scrawl.js
Simple example
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<style type='text/css'>
#sheet {border:1px solid black;}
</style>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
<script src="http://scrawl.rikweb.org.uk/js/scrawlCore-min.js"></script>
<script>
var mycode = function(){
//define variables
var myPad = scrawl.pad.sheet,
myCanvas = scrawl.canvas.sheet,
sX, sY, here,
drawing = false,
currentSprite = false,
startDrawing,
endDrawing;
//event listeners
startDrawing = function(e){
drawing = true;
currentSprite = scrawl.newShape({
start: here,
lineCap: 'round',
lineJoin: 'round',
method: 'draw',
lineWidth: 4,
strokeStyle: 'red',
data: 'l0,0 ',
});
sX = here.x;
sY = here.y;
if(e){
e.stopPropagation();
e.preventDefault();
}
};
myCanvas.addEventListener('mousedown', startDrawing, false);
endDrawing = function(e){
if(currentSprite){
currentSprite = false;
}
drawing = false;
if(e){
e.stopPropagation();
e.preventDefault();
}
};
myCanvas.addEventListener('mouseup', endDrawing, false);
//animation object
scrawl.newAnimation({
fn: function(){
//get current mouse position
here = myPad.getMouse();
if(here.active){
if(drawing){
if(here.x !== sX || here.y !== sY){
//extend the line
currentSprite.set({
data: currentSprite.data+' '+(here.x - sX)+','+(here.y - sY),
});
sX = here.x;
sY = here.y;
}
}
}
else{
//stop drawing if mouse leaves canvas area
if(currentSprite){
endDrawing();
}
}
//update display
scrawl.render();
},
});
};
//Scrawl is modular - load additional modules
scrawl.loadModules({
path: 'js/',
modules: ['animation', 'shape'],
callback: function(){
window.addEventListener('load', function(){
scrawl.init(); //start Scrawl
mycode(); //run code
}, false);
},
});
</script>
</body>
</html>
JSFiddle
This solution works with:
recent versions of IE, Chrome, Firefox, Opera (desktop)
(not tested on mobile/touch devices)
Adding touch support
(try adding a dedicated touch library like Hammer.js?)
Import / Export
Scrawl has experimental support for saving and loading JSON strings
tutorial page: load and save
Line smoothing and other sprite manipulations
line data is saved internally as an SVGTiny Path.d value - any algorithm that can take line data in that format and smooth it should work
line attributes - thickness, color, positioning, rotation, etc - can be set, and animated.

A simple program to display a rolling ball

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var canvas_variable;
var init_x = 200;
var init_y = 300;
var x_move = 1;
function initialize_canvas()
{setInterval(draw_ball, 10);
canvas_variable = bouncing_ball_canvas.getContext('2d');
}
function draw_ball()
{
canvas_variable.clearRect(0,0, 1000, 500);
canvas_variable.beginPath();
canvas_variable.fillStyle="#FF0000";
canvas_variable.arc(init_x, init_y, 50, 0+init_x/50, Math.PI*2+init_x/50, true);
canvas_variable.lineTo(init_x, init_y);
canvas_variable.stroke();
if( init_x<0 || init_x>1000) x_move = -x_move;
init_x += x_move;
}
</script>
</head>
<body>
<canvas id="bouncing_ball_canvas" width="1000" height="500">
</canvas>
<body onLoad="initialize_canvas();">
</body>
</html>
This is a program of a rolling ball. The function draw_ball is called after every 10 milliseconds.The ball blinks during its motion. What is the solution for this problem?
You forgot to declare the variable bouncing_ball_canvas
Try adding:
bouncing_ball_canvas = document.getElementById("bouncing_ball_canvas");
before you declare canvas_variable.
EDIT:
The problem lies in the line:
canvas_variable.arc(init_x, init_y, 50, 0+init_x/50, Math.PI*2+init_x/50, true);
Change the last variable to false and it should work.
It is blinking because your computation is wrong.
You need to compute the perimeter: 2*pi*r.
Then do a simple cross product:
distance = x
perimeter = 2*pi
So the angle (in radian) is distance*2*pi/perimeter
So try this:
const getAngle=function(distance) {
return distance*2*Math.PI/perimeter;
}
const getPos=function(x, y, angle, r) {
return [x + Math.cos(angle)*r, y + Math.sin(angle)*r];
}
canvas_variable.arc(init_x, init_y, 50, 0, Math.PI*2, true);
canvas_variable.moveTo(..getPos(init_x, init_y, getAngle(init_x-starting_x), r));
canvas_variable.lineTo(init_x, init_y);
canvas_variable.stroke();

Iteratively change the fillStyle color in canvas while drawing a fractal

I'm using the following code to generate a Pythagoras fractal tree using HTML5 canvas element:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="jquery.js" type="text/javascript"></script>
<style>
#sketch
{
border: 1px solid black;
}
</style>
<script type="text/javascript">
window.onload = function()
{
var canvas = document.getElementById("sketch");
var context = canvas.getContext("2d");
context.fillStyle = "rgba(0, 0, 200, 0.5)";
context.beginPath();
context.moveTo(450,550);
context.lineTo(450,450);
context.lineTo(550,450);
context.lineTo(550,550);
context.fill();
fractal(context,[450,550],[450,450],[550,450],[550,550],5);
};
function fractal(context,P1,P2,P3,P4,depth)
{
context.fillStyle = "rgba(0,0,200,"+(depth/8).toString()+")";
context.save();
if(depth < 0)
{
return null;
}
/*Find C*/
C = divide(add(divide(add(P1,P2),2),divide(add(P3,P4),2)),2);
var V1 = divide(minus(C,P1),length(C,P1));
var V2 = divide(minus(C,P4),length(C,P4));
var P6 = add(P2,multiply(V2,length(P1,P2)/Math.sqrt(2)));
var P7 = add(P6,multiply(V1,length(P1,P2)/Math.sqrt(2)));
var P5 = add(P2,multiply(V1,length(P1,P2)/Math.sqrt(2)));
var P9 = add(P3,multiply(V1,length(P1,P2)/Math.sqrt(2)));
var P8 = add(P9,multiply(V2,length(P1,P2)/Math.sqrt(2)));
context.moveTo(P2[0],P2[1]);
context.lineTo(P6[0],P6[1]);
context.lineTo(P7[0],P7[1]);
context.lineTo(P5[0],P5[1]);
context.fill();
context.moveTo(P5[0],P5[1]);
context.lineTo(P8[0],P8[1]);
context.lineTo(P9[0],P9[1]);
context.lineTo(P3[0],P3[1]);
context.fill();
fractal(context,P2,P6,P7,P5,depth-1);
fractal(context,P5,P8,P9,P3,depth-1);
}
function multiply(v, num){
return [v[0]*num, v[1]*num];
}
function divide(v, num){
return [v[0]/num, v[1]/num];
}
function add(a, b){
return [a[0]+b[0], a[1]+b[1]];
}
function minus(a, b){
return [a[0]-b[0], a[1]-b[1]];
}
function length(a, b){
return Math.sqrt(Math.pow(a[0] - b[0],2) +
Math.pow(a[1] - b[1],2));
}
</script>
<title>Square</title>
</head>
<body>
<canvas id="sketch" height="1000" width="1000"></canvas>
</body>
</html>
I'm changing the opacity value with every iteration. But I don't see it in the result.
How can this be fixed??
You were really close to the right solution. Look here:
http://jsfiddle.net/mbessey/Wj4VH/
The key difference here is calling beginPath() before starting the moveTo() and lineTo() for each square. So, instead of:
context.moveTo(P2[0],P2[1]);
context.lineTo(P6[0],P6[1]);
context.lineTo(P7[0],P7[1]);
context.lineTo(P5[0],P5[1]);
context.fill();
context.moveTo(P5[0],P5[1]);
context.lineTo(P8[0],P8[1]);
context.lineTo(P9[0],P9[1]);
context.lineTo(P3[0],P3[1]);
context.fill();
You want:
context.beginPath()
context.moveTo(P2[0],P2[1]);
context.lineTo(P6[0],P6[1]);
context.lineTo(P7[0],P7[1]);
context.lineTo(P5[0],P5[1]);
context.fill();
context.beginPath()
context.moveTo(P5[0],P5[1]);
context.lineTo(P8[0],P8[1]);
context.lineTo(P9[0],P9[1]);
context.lineTo(P3[0],P3[1]);
context.fill();
What you were doing was essentially creating one large path with all the squares in it and then filling it all with the same color.
Check This I have changed the opacity of the canvas itself ,making it simple and easy to achieve the target.
Adding rgba(0, 0, 200, 1) and
#sketch
{
border: 1px solid black;
opacity: 0.3;
}
will suffice what you want

Categories

Resources