Related
I am just using this to learn HTML, PHP, MySQL and Javascript as a hobby. The game code is straight off of w3schools and I have only added a button to start the game. For some reason, canvas keeps being displayed above the header like this: https://gyazo.com/9fa59ccfe238b2147583e036a69e88c7
What can be done to have canvas be displayed between the header and footer?
Here's the code for the game:
<body>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
canvas {
border:1px solid #d3d3d3;
background-color: #f1f1f1;
}
</style>
</body>
<button type="button"
onclick="startGame()">Click here to play!</button>
<script>
var myGamePiece;
var myObstacles = [];
var myScore;
function startGame() {
myGamePiece = new component(30, 30, "red", 10, 120);
myGamePiece.gravity = 0.05;
myScore = new component("30px", "Consolas", "black", 280, 40, "text");
myGameArea.start();
}
var myGameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.frameNo = 0;
this.interval = setInterval(updateGameArea, 20);
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function component(width, height, color, x, y, type) {
this.type = type;
this.score = 0;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
this.x = x;
this.y = y;
this.gravity = 0;
this.gravitySpeed = 0;
this.update = function() {
ctx = myGameArea.context;
if (this.type == "text") {
ctx.font = this.width + " " + this.height;
ctx.fillStyle = color;
ctx.fillText(this.text, this.x, this.y);
} else {
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
this.y += this.speedY + this.gravitySpeed;
this.hitBottom();
}
this.hitBottom = function() {
var rockbottom = myGameArea.canvas.height - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
this.gravitySpeed = 0;
}
}
this.crashWith = function(otherobj) {
var myleft = this.x;
var myright = this.x + (this.width);
var mytop = this.y;
var mybottom = this.y + (this.height);
var otherleft = otherobj.x;
var otherright = otherobj.x + (otherobj.width);
var othertop = otherobj.y;
var otherbottom = otherobj.y + (otherobj.height);
var crash = true;
if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
crash = false;
}
return crash;
}
}
function updateGameArea() {
var x, height, gap, minHeight, maxHeight, minGap, maxGap;
for (i = 0; i < myObstacles.length; i += 1) {
if (myGamePiece.crashWith(myObstacles[i])) {
return;
}
}
myGameArea.clear();
myGameArea.frameNo += 1;
if (myGameArea.frameNo == 1 || everyinterval(150)) {
x = myGameArea.canvas.width;
minHeight = 20;
maxHeight = 200;
height = Math.floor(Math.random()*(maxHeight-minHeight+1)+minHeight);
minGap = 50;
maxGap = 200;
gap = Math.floor(Math.random()*(maxGap-minGap+1)+minGap);
myObstacles.push(new component(10, height, "green", x, 0));
myObstacles.push(new component(10, x - height - gap, "green", x, height + gap));
}
for (i = 0; i < myObstacles.length; i += 1) {
myObstacles[i].x += -1;
myObstacles[i].update();
}
myScore.text="SCORE: " + myGameArea.frameNo;
myScore.update();
myGamePiece.newPos();
myGamePiece.update();
}
function everyinterval(n) {
if ((myGameArea.frameNo / n) % 1 == 0) {return true;}
return false;
}
function accelerate(n) {
myGamePiece.gravity = n;
}
</script>
<br>
<button onmousedown="accelerate(-0.2)" onmouseup="accelerate(0.05)">ACCELERATE</button>
<p>Use the ACCELERATE button to stay in the air</p>
<p>How long can you stay alive?</p>
Additionally, here is the code for the header, in case that is the problem:
<!DOCTYPE html>
<html>
<head>
<title>This is my PHP site</title>
<?php include ('log-ip.php') ?>
</head>
<body>
<header>
<link type="text/css" rel="stylesheet" href="style/core.css" />
<p id="time1"></p>
<script>
document.getElementById('time1').innerHTML = Date();
</script
<h2>Test Site</h2>
<nav>
<ul>
<li>Home</li>
<li>Products</li>
<li>Contact</li>
<li>Ayy</li>
<li>NMR Data</li>
<li>About</li>
<li>Location</li>
<li>Game</li>
<li>Chat</li>
</ul>
</nav>
</header>
</body>
</html>
Is there possibly some code that will tell the header to always stay on top?
This line says to insert the canvas element before all other elements in body:
document.body.insertBefore(this.canvas, document.body.childNodes[0])
If the header and the game are both children of body, you could try
document.body.insertAfter(this.canvas, document.getElementsByTagName('header')[0])
So I have been looking around and trying tutorials but I can't seem to get any collision detection systems to work. If someone would be able to explain what I am doing wrong or any syntax errors that would be great.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Bouncing Ball</title>
<style>
#mycanvas {
outline: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="mycanvas" width="1280" height="750"></canvas>
<script>
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext('2d');
var xx = 50;
var yy = 100;
var velY = 0;
var velX = 0;
var speed = 6;
var friction = 0.7;
var keys = [];
var velocity = 0;
var acceleration = 1;
function physics() {
velocity+=acceleration;
yy += velocity;
if(yy > 597) {
var temp =0;
temp =velocity/4;
velocity=-temp;
yy = 597;
}
}
function collision(first, second){
return !(first.x > second.x + second.width || first.x + first.width < second.x || first.y > second.y + second.height || first.y + first.height < second.y);
}
var player = {
color: "#2B2117",
x: xx,
y: yy,
width: 75,
height: 75,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(xx, yy, this.width, this.height);
}
}
var floor = {
color: "#A67437",
x: 0,
y: 670,
width: 1280,
height: 80,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
var bucket = {
color: "#B25E08",
x: 300,
y: 600,
width: 50,
height: 100,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
function update() {
if (keys[39]) {
if (velX < speed) {
velX+=3;
}
}
if (keys[37]) {
if (velX > -speed) {
velX--;
}
}
if (keys[32]) {
velY -= 1.5;
velY += 1;
}
velX *= friction;
xx += velX;
yy += velY;
physics();
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.rect(0,0,canvas.width, canvas.height);
ctx.fillStyle = "#EEE3B9";
ctx.fill();
floor.draw();
bucket.draw();
player.draw();
if (collision(player, bucket)) {
console.log('collision');
}
setTimeout(update, 10);
}
update();
document.body.addEventListener("keydown", function (e) {
keys[e.keyCode] = true;
});
document.body.addEventListener("keyup", function (e) {
keys[e.keyCode] = false;
});
</script>
</body>
I can only append to what markE already says in his answer (we seem to be working on this simultaneously :) ).
I will first point out a more serious bug in the code:
You are using fill() without a beginPath() first. This will slow down the code loop rapidly. Replace fill() with a fillRect() instead.
Also, there is no need to use clearRect() when the next draw operation fills the entire canvas:
//ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#EEE3B9";
ctx.fillRect(0, 0, canvas.width, canvas.height);
You're using global vars xx/yy. One main point of using objects is to avoid global vars. The render method uses the same so it appears to work, but the player's local properties are never updated. Remove global vars and only work with the object's x and y (I would recommend some refactoring so you can pass in the object to the physics() etc. instead of doing global referencing).
var xx = 50;var yy = 100;
To:
var player = {
x: 50, // numbers cannot be referenced, their value
y: 100, // is copied. Use these properties directly instead..
// ...
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height); // and here..
}
}
and
function physics() {
//...
player.y += velocity;
if (player.y > 597) {
//...
player.y = 597;
}
}
and in the loop:
player.x += velX;
player.y += velY;
Add preventDefault() in you key handlers to prevent the key moves to affect the brower's window scroll (not all users have a large screen so the left/right key will also affect the scrolling).
document.body.addEventListener("keydown", function(e) {
e.preventDefault();
...
And of course, use requestAnimationFrame to loop the animation.
I would also recommend using a function object that can be instantiated as you have methods in all of them. With a function object you can prototype the draw() method and share it across all the instances without the need of extra memory (and it's good for the internal optimizer as well). This if you plan to draw more than those three objects.
Updated code
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext('2d');
var velY = 0;
var velX = 0;
var speed = 6;
var friction = 0.7;
var keys = [];
var velocity = 0;
var acceleration = 1;
function physics() {
velocity += acceleration;
player.y += velocity;
if (player.y > 597) {
var temp = 0;
temp = velocity / 4;
velocity = -temp;
player.y = 597;
}
}
function collision(first, second) {
return !(first.x > second.x + second.width || first.x + first.width < second.x || first.y > second.y + second.height || first.y + first.height < second.y);
}
var player = {
color: "#2B2117",
x: 50,
y: 100,
width: 75,
height: 75,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
var floor = {
color: "#A67437",
x: 0,
y: 670,
width: 1280,
height: 80,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
var bucket = {
color: "#B25E08",
x: 300,
y: 600,
width: 50,
height: 100,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
function update() {
if (keys[39]) {
if (velX < speed) {
velX += 3;
}
}
else if (keys[37]) {
if (velX > -speed) {
velX -= 3;
}
}
else if (keys[32]) {
velY -= 1.5;
velY += 1;
}
velX *= friction;
player.x += velX;
player.y += velY;
physics();
//ctx.clearRect(0, 0, canvas.width, canvas.height); // not really needed
ctx.fillStyle = "#EEE3B9";
ctx.fillRect(0, 0, canvas.width, canvas.height); // as this clears too...
floor.draw();
bucket.draw();
player.draw();
if (collision(player, bucket)) {
console.log('collision');
}
requestAnimationFrame(update);
}
update();
document.body.addEventListener("keydown", function(e) {
e.preventDefault();
keys[e.keyCode] = true;
});
document.body.addEventListener("keyup", function(e) {
e.preventDefault();
keys[e.keyCode] = false;
});
#mycanvas {outline: 1px solid #000;}
<canvas id="mycanvas" width="1280" height="750"></canvas>
There's no problem with your collision function--it will correctly recognize the collision between any 2 un-rotated rectangles.
Visually, your black rect drops to the floor, bounces once or twice and then settles on the floor.
3 issues though...
I do see that your setTimeout is set to trigger every 10ms. That's a bit fast since the display will only refresh about every 1000/60=16.67ms--so your setTimeout should be at least 16.67. Also, You might consider using requestAnimationFrame instead of setTimeout because rAF synchronizes itself with the display and gives better performance.
One design glitch, even if the user constantly holds down the right-key to move the rect towards the bucket as fast as possible, the rect is never given enough "delta X" to hit the bucket.
You are testing collisions with player and bucket but you are not updating the player.x & player.y. Therefore your collision() is only testing the initial player position and not its current position.
// in update()
...
player.x=xx;
player.y=yy;
Demo of refactored code:
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext('2d');
var xx = 50;
var yy = 100;
var velY = 0;
var velX = 0;
var speed = 6;
var friction = 0.7;
var keys = [];
var velocity = 0;
var acceleration = 1;
function physics() {
velocity+=acceleration;
yy += velocity;
if(yy > 597) {
var temp =0;
temp =velocity/4;
velocity=-temp;
yy = 597;
}
}
function collision(first, second){
return !(first.x > second.x + second.width || first.x + first.width < second.x || first.y > second.y + second.height || first.y + first.height < second.y);
}
var player = {
color: "#2B2117",
x: xx,
y: yy,
width: 75,
height: 75,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(xx, yy, this.width, this.height);
}
}
var floor = {
color: "#A67437",
x: 0,
y: 670,
width: 1280,
height: 80,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
var bucket = {
color: "#B25E08",
x: 50,
y: 600,
width: 50,
height: 100,
draw: function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
function update() {
if (keys[39]) {
if (velX < speed) {
velX+=3;
}
}
if (keys[37]) {
if (velX > -speed) {
velX--;
}
}
if (keys[32]) {
velY -= 1.5;
velY += 1;
}
velX *= friction;
xx += velX;
yy += velY;
//
player.x=xx;
player.y=yy;
physics();
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.rect(0,0,canvas.width, canvas.height);
ctx.fillStyle = "#EEE3B9";
ctx.fill();
floor.draw();
bucket.draw();
player.draw();
if (collision(player, bucket)) {
alert('collision when player at:'+player.x+"/"+player.y);
}else{
setTimeout(update, 1000/60*3);
}
}
update();
document.body.addEventListener("keydown", function (e) {
keys[e.keyCode] = true;
});
document.body.addEventListener("keyup", function (e) {
keys[e.keyCode] = false;
});
<canvas id="mycanvas" width="1280" height="750"></canvas>
Copy and paste
<html>
<head>
<title> new </title>
<script>
var ctx1;
var ctx;
var balls = [
{x:50, y:50, r:20, m:1, vx:1, vy:1.5},
{x:200, y:80, r:30, m:1, vx:-1, vy:0.3},
];
function start()
{
ctx1 = document.getElementById("ctx");
ctx = ctx1.getContext("2d");
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
setInterval(draw, 10);
}
function draw()
{
ctx.clearRect(0, 0, ctx1.width, ctx1.height);
for (var a = 0; a < balls.length; a ++)
{
for (var b = 0; b < balls.length; b ++)
{
if (a != b)
{
var distToBalls = Math.sqrt(Math.pow(balls[a].x - balls[b].x, 2) + Math.pow(balls[a].y - balls[b].y, 2));
if (distToBalls <= balls[a].r + balls[b].r)
{
var newVel = getBallCollision(balls[a], balls[b]);
balls[a].vx = newVel.ball1.vx;
balls[a].vy = newVel.ball1.vy;
balls[b].vx = newVel.ball2.vx;
balls[b].vy = newVel.ball2.vy;
balls[a].x += balls[a].vx;
balls[a].y += balls[a].vy;
balls[b].x += balls[b].vx;
balls[b].y += balls[b].vy;
}
}
}
}
for (var a = 0; a < balls.length; a ++)
{
ctx.beginPath();
ctx.arc(balls[a].x, balls[a].y, balls[a].r, 0, Math.PI*2, true);
ctx.fill();
ctx.closePath();
if (balls[a].x <= balls[a].r) balls[a].vx *= -1;
if (balls[a].y <= balls[a].r) balls[a].vy *= -1;
if (balls[a].x >= ctx1.width - balls[a].r) balls[a].vx *= -1;
if (balls[a].y >= ctx1.height - balls[a].r) balls[a].vy *= -1;
balls[a].x += balls[a].vx;
balls[a].y += balls[a].vy;
}
}
function getBallCollision(b1, b2)
{
var dx = b1.x - b2.x;
var dy = b1.y - b2.y;
var dist = Math.sqrt(dx*dx + dy*dy);
if (Math.abs(dy) + Math.abs(dx) != 0 && dist <= b1.r + b2.r)
{
var colAng = Math.atan2(dy, dx);
var sp1 = Math.sqrt(b1.vx*b1.vx + b1.vy*b1.vy);
var sp2 = Math.sqrt(b2.vx*b2.vx + b2.vy*b2.vy);
var dir1 = Math.atan2(b1.vy, b1.vx);
var dir2 = Math.atan2(b2.vy, b2.vx);
var vx1 = sp1 * Math.cos(dir1 - colAng);
var vy1 = sp1 * Math.sin(dir1 - colAng);
var vx2 = sp2 * Math.cos(dir2 - colAng);
var vy2 = sp2 * Math.sin(dir2 - colAng);
var fvx1 = ((b1.m - b2.m) * vx1 + (2 * b2.m) * vx2) / (b1.m + b2.m);
var fvx2 = ((2 * b1.m) * vx1 + (b2.m - b1.m) * vx2) / (b1.m + b2.m);
var fvy1 = vy1;
var fvy2 = vy2;
b1.vx = Math.cos(colAng) * fvx1 + Math.cos(colAng + Math.PI/2) * fvy1;
b1.vy = Math.sin(colAng) * fvx1 + Math.sin(colAng + Math.PI/2) * fvy1;
b2.vx = Math.cos(colAng) * fvx2 + Math.cos(colAng + Math.PI/2) * fvy2;
b2.vy = Math.sin(colAng) * fvx2 + Math.sin(colAng + Math.PI/2) * fvy2;
return { ball1:b1, ball2:b2 };
}
else return false;
}
</script>
</head>
<body onLoad="start();">
<canvas id="ctx" width="500" height="300" style = "border : 2px solid #854125 ;"> </canvas>
</body>
</html>
Drawing was working fine, until I decided to use multiple canvas's. I have a stage canvas, an entity canvas, and an object canvas. I'll probably end up combining the object and entity canvas's, though. Anyway, as you can see below, my hero class draws fine. I then tried to make an entity class with the same draw function, but it won't let me draw when I call the function. I have almost the same issue with my background canvas. I don't have a class for the background yet, but I will. But I try simply drawing the image with the stage's context, and it breaks the code.
(I tried setting up a JSFiddle, but I wouldn't be able to get the images on there.)
UPDATE
Half of my issue was fixed by markE. The only issue I currently have, is my entitiesCtx is the the only context that can draw images/rectangles. The other ctx's just can't draw anything. Help, please! I updated the code.
var stage = document.getElementById('stage');
var ctxStage = stage.getContext('2d');
var entitiesStage = document.getElementById('entities');
var ctxEntities = entitiesStage.getContext('2d');
var bg = document.getElementById('bg');
var ctxBg = bg.getContext('2d');
var playerImg = new Image();
playerImg.src = 'res/player_sprite_sheet.png';
var bgImg = new Image();
bgImg.onload = function() {
ctxBg.drawImage(bgImg,0,0,80,50,-200,-90,1000,700);
};
bgImg.src = 'res/background.png';
var consoleImg = new Image();
consoleImg.onload = function() {
ctxEntities.drawImage(consoleImg,0,0,80,50,20,20,1000,700);
};
console.src = 'res/console.png';
var hero = new Hero();
var prop;
var isPlaying = false;
window.onload = init;
var requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
function init() {
console.debug('initializing...');
document.addEventListener('keydown',keyDown,false);
document.addEventListener('keyup',keyUp,false);
ctxStage.imageSmoothingEnabled = false;
ctxStage.webkitImageSmoothingEnabled = false;
ctxStage.mozImageSmoothingEnabled = false;
ctxEntities.imageSmoothingEnabled = false;
ctxEntities.webkitImageSmoothingEnabled = false;
ctxEntities.mozImageSmoothingEnabled = false;
prop = new Entity(consoleImg,20,20,80,50,0,0);
startLoop();
}
function startLoop(){
console.debug('starting loop...');
isPlaying = true;
loop();
}
function stopLoop(){
console.debug('stopping loop...');
isPlaying = false;
}
function loop(){
if(isPlaying){
requestAnimFrame(loop);
draw();
update();
}
}
function update(){
hero.update();
}
function clearCtx(){
ctxEntities.clearRect(0,0,stage.width,stage.height);
}
function draw(){
clearCtx();
ctxEntities.fillStyle = 'black';
ctxEntities.fillRect(0,0,stage.width,stage.height);
ctxEntities.drawImage(bgImg,0,0,80,50,-200,-90,1000,700);
hero.draw();
prop.draw();
}
// hero class
function Hero() {
this.xpos = 140;
this.ypos = 320;
this.srcX = 0;
this.srcY = 0;
this.width = 10;
this.height = 20;
this.scaleX = 50;
this.scaleY = 80;
this.isUpKey;
this.isDownKey;
this.isLeftKey;
this.isRightKey;
this.img = playerImg;
this.speed = 2;
this.defspeed = 3.5;
this.dir = 'right';
}
Hero.prototype.draw = function() {
ctxEntities.drawImage(this.img,this.srcX,this.srcY,this.width,this.height,this.xpos,this.ypos,this.scaleX,this.scaleY);
};
Hero.prototype.update = function() {
this.checkKeys();
if(this.dir == 'right'){
if(this.scaleX >= 0){
this.srcX = 0;
}
if(this.scaleX >= 40){
this.scaleX = 40;
this.speed = this.defspeed;
}else{
this.xpos -= 2.3;
this.speed = 0;
this.scaleX += 5;
}
}else if(this.dir =='left'){
if(this.scaleX <= 0){
this.srcX = 10;
}
if(this.scaleX <= -40){
this.scaleX = -40;
this.speed = this.defspeed;
}else{
this.xpos += 2.3;
this.speed = 0;
this.scaleX -= 5;
}
}
};
Hero.prototype.checkKeys = function() {
if(this.isLeftKey){
this.xpos += -this.speed;
this.dir = 'left';
}
if(this.isRightKey){
this.xpos += this.speed;
this.dir = 'right';
}
};
// end of hero class
// entity class
function Entity(img,xpos,ypos,width,height,scaleX,scaleY){
this.img = img;
this.xpos = xpos;
this.ypos = ypos;
this.width = width;
this.height = height;
this.scaleX = scaleX;
this.scaleY = scaleY;
}
Entity.prototype.draw = function(){
ctxEntities.drawImage(this.img,0,0,this.width,this.height,this.xpos,this.ypos,this.scaleX,this.scaleY);
};
// end of entity class
// input handling
function keyDown(e){
var keyID = (e.keyCode) ? e.keyCode : e.which;
if(keyID == 38 || keyID == 87){ //w
e.preventDefault();
hero.isUpKey = true;
}
if(keyID == 37 || keyID == 65){ //a
e.preventDefault();
hero.isLeftKey = true;
}
if(keyID == 40 || keyID == 83){ //s
e.preventDefault();
hero.isDownKey = true;
}
if(keyID == 39 || keyID == 68){ //d
e.preventDefault();
hero.isRightKey = true;
}
}
function keyUp(e){
var keyID = (e.keyCode) ? e.keyCode : e.which;
if(keyID == 38 || keyID == 87){
hero.isUpKey = false;
}
if(keyID == 37 || keyID == 65){
hero.isLeftKey = false;
}
if(keyID == 40 || keyID == 83){
hero.isDownKey = false;
}
if(keyID == 39 || keyID == 68){
hero.isRightKey = false;
}
}
// end of input handling
UPDATE
Half of my issue was fixed by markE. The only issue I currently have, is my entitiesCtx is the the only context that can draw images/rectangles. The other ctx's just can't draw anything. I updated the code.
Using JS “classes” to draw on multiple canvas’s
[I expanded my answer to include example of using your JS classes]
This example illustrates your 2 js-classes that draw images on canvases
The Entity class controls and draws an image on a canvas.
The Hero class controls and draws spritesheets on a canvas.
There is also an image loader so that all your images are fully loaded before they are used.
In your question you included only your js-class code and no specifics on your project.
So I made up my own project using your Hero and Entity classes (pardon my taking liberty).
This image shows your Entity and Hero classes in action drawing on all 3 of your canvases...
This is the background canvas containing:
A sky-blue rect filling the canvas (the sky)
The background contains 2 Entity class objects.
A sun which is an image wrapped in an Entity class object
A wall which is an image wrapped in an Entity class object
This is the stage canvas containing:
A cannon which is an Entity class object that animates up and down
This is the entities canvas containing:
A cat image is a spritesheet image wrapped in a Hero class object
The cat object animates sprites in response to the cannon object
The cat is composed of a spritesheet that’s controlled by the Hero class
The Entity class controls and draws an image on a canvas:
The image can be moved and scaled.
The Entity class has 3 methods.
Entity.draw() will draw the image on the canva.
Entity.set() will set the XY position of the image on the canvas.
Entity.scale() will scale the image.
Here is the code for the Entity class:
// Entity class
function Entity(context,img,x,y){
this.context=context;
this.img = img;
this.xpos = x;
this.ypos = y;
this.width = img.width;
this.height = img.height;
this.scaleX = img.width;
this.scaleY = img.height;
}
// Entity.set()
Entity.prototype.set = function(x,y){
this.xpos=x;
this.ypos=y;
}
// Entity.scale()
Entity.prototype.scale = function(scaleX,scaleY){
this.scaleX=scaleX;
this.scaleY=scaleY;
}
// Entity.draw()
Entity.prototype.draw = function(){
this.context.drawImage(this.img,
0,0,this.width,this.height,
this.xpos,this.ypos,this.scaleX,this.scaleY);
}
The Hero class controls and draws spritesheets on a canvas
The individual sprites are pulled from the spritesheet image.
Each sprite is defined by an object having its x,y,width,height within the spritesheet.
The sprites can be moved and scaled.
The Hero class has 3 methods.
Hero.draw() will draw one of the sprites on the canvas.
Hero.set() will set which sprite is drawn and its XY position on the canvas
Hero.scale() will scale the sprite.
Here is the code for the Hero class:
// Hero class
function Hero(context,img,spriteDefs) {
this.context=context;
this.spriteDefs=spriteDefs;
this.img = img;
this.xpos = 0;
this.ypos = 0;
this.srcX = 0;
this.srcY = 0;
this.width = img.width;
this.height = img.height;
this.scaleX = img.width;
this.scaleY = img.height;
this.isUpKey;
this.isDownKey;
this.isLeftKey;
this.isRightKey;
this.speed = 2;
this.defspeed = 3.5;
this.dir = 'right';
}
// Hero.set()
Hero.prototype.set = function(spriteNumber,x,y){
// pull the specified sprite
var sprite=this.spriteDefs[spriteNumber];
this.srcX=sprite.x;
this.srcY=sprite.y;
this.width=sprite.width;
this.height=sprite.height;
// default scale to 100%
this.scaleX=sprite.width;
this.scaleY=sprite.height;
this.xpos=x;
this.ypos=y;
}
// Hero.scale()
Hero.prototype.scale = function(scaleX,scaleY){
this.scaleX=scaleX;
this.scaleY=scaleY;
}
// Hero.draw()
Hero.prototype.draw = function() {
this.context.drawImage(this.img,
this.srcX,this.srcY,this.width,this.height,
this.xpos,this.ypos,this.scaleX,this.scaleY);
}
This is an image loader that makes sure all images are loaded before they are used
var imageURLs=[];
var imagesOK=0;
var imgs=[];
imageURLs.push("cats.png");
imageURLs.push("cannonLifted.png");
imageURLs.push("brickwall.jpg");
imageURLs.push("sun.png");
loadAllImages();
function loadAllImages(){
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){ imagesOK++; imagesAllLoaded(); };
img.src = imageURLs[i];
}
}
var imagesAllLoaded = function() {
if (imagesOK==imageURLs.length ) {
// all images are fully loaded an ready to use
cat=imgs[0];
cannon=imgs[1];
wall=imgs[2];
sun=imgs[3];
start();
}
};
Here is complete code and a Fiddle: http://jsfiddle.net/m1erickson/yCW9U/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
h3{ font-size:2em; }
#wrapper{
position:relative;
width:350px;
height:400px;
}
#bg,#stage,#entities{
position:absolute; top:0px; left:0px;
border:1px solid green;
width:100%;
height:100%;
}
</style>
<script>
$(function(){
//////////////////////////////
// get context references
//////////////////////////////
// stage
var stage = document.getElementById('stage');
var ctxStage = stage.getContext('2d');
// entities
var entitiesStage = document.getElementById('entities');
var ctxEntities = entitiesStage.getContext('2d');
// background
var bg = document.getElementById('bg');
var ctxBg = bg.getContext('2d');
//////////////////////////////
// public variables
//////////////////////////////
// images
var wall,cat,cannon,sun;
// display objectx
var sunEntity,wallEntity,cannonEntity,catHero;
// animation vars
var cannonX=65;
var cannonMove=-10;
var cannonMin=75;
var cannonMax=185;
var cannonY=185;
var cannonSafe=145;
// cat hero sprites
var catSpriteNames={
laying:0,
layingX:250,
layingY:127,
standing:1,
standingX:165,
standingY:25
};
var catSprites=[
{x:80, y:30, width:67, height:48},
{x:15, y:8, width:47, height:78}
];
//////////////////////////////
// preload all images
//////////////////////////////
var imageURLs=[];
var imagesOK=0;
var imgs=[];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/cats.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/cannonLifted.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/BrickWall.jpg");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/sun.png");
loadAllImages();
function loadAllImages(){
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){ imagesOK++; imagesAllLoaded(); };
img.src = imageURLs[i];
}
}
var imagesAllLoaded = function() {
if (imagesOK==imageURLs.length ) {
// all images are fully loaded an ready to use
cat=imgs[0];
cannon=imgs[1];
wall=imgs[2];
sun=imgs[3];
start();
}
};
//////////////////////////////
// build the display objects
// and start animation
//////////////////////////////
function start(){
// static background (canvas: bg)
// rectangle=blue sky
ctxBg.rect(0,0,bg.width,bg.height);
ctxBg.fillStyle="skyblue";
ctxBg.fill();
// sun image # 75% scale
sunEntity=new Entity(ctxBg,sun,185,15);
sunEntity.set(25,15);
sunEntity.scale(sun.width*.75,sun.height*.75);
sunEntity.draw();
// wall image
wallEntity=new Entity(ctxBg,wall,250,bg.height-wall.height);
wallEntity.set(250,bg.height-wall.height,wall.width,wall.height);
wallEntity.draw();
// stage (canvas: stage)
// contents: wall
cannonEntity=new Entity(ctxStage,cannon,cannonX,cannonY,cannon.width,cannon.height,cannon.width,cannon.height);
cannonEntity.draw();
// entities (canvas: entities)
// contents:
catHero=new Hero(ctxEntities,cat,catSprites);
catHero.set(catSpriteNames.laying,catSpriteNames.layingX,catSpriteNames.layingY);
catHero.draw();
animate();
}
function animate(){
cannonY+=cannonMove;
if(cannonY<cannonMin){ cannonY=cannonMin; cannonMove=-cannonMove; }
if(cannonY>cannonMax){ cannonY=cannonMax; cannonMove=-cannonMove; }
cannonEntity.context.clearRect(0,0,stage.width,stage.height);
cannonEntity.set(cannonX,cannonY);
cannonEntity.draw();
if(cannonY>cannonSafe){
catHero.set(catSpriteNames.laying,catSpriteNames.layingX,catSpriteNames.layingY);
}else{
catHero.set(catSpriteNames.standing,catSpriteNames.standingX,cannonY-50);
}
catHero.context.clearRect(0,0,entities.width,entities.height);
catHero.draw()
window.setTimeout(function(){animate();},500);
}
// Hero class
function Hero(context,img,spriteDefs) {
this.context=context;
this.spriteDefs=spriteDefs;
this.img = img;
this.xpos = 0;
this.ypos = 0;
this.srcX = 0;
this.srcY = 0;
this.width = img.width;
this.height = img.height;
this.scaleX = img.width;
this.scaleY = img.height;
this.isUpKey;
this.isDownKey;
this.isLeftKey;
this.isRightKey;
this.speed = 2;
this.defspeed = 3.5;
this.dir = 'right';
}
// Hero.set()
Hero.prototype.set = function(spriteNumber,x,y){
// pull the specified sprite
var sprite=this.spriteDefs[spriteNumber];
this.srcX=sprite.x;
this.srcY=sprite.y;
this.width=sprite.width;
this.height=sprite.height;
// default scale to 100%
this.scaleX=sprite.width;
this.scaleY=sprite.height;
this.xpos=x;
this.ypos=y;
}
// Hero.scale()
Hero.prototype.scale = function(scaleX,scaleY){
this.scaleX=scaleX;
this.scaleY=scaleY;
}
// Hero.draw()
Hero.prototype.draw = function() {
this.context.drawImage(this.img,
this.srcX,this.srcY,this.width,this.height,
this.xpos,this.ypos,this.scaleX,this.scaleY);
}
// Entity class
function Entity(context,img,x,y){
this.context=context;
this.img = img;
this.xpos = x;
this.ypos = y;
this.width = img.width;
this.height = img.height;
this.scaleX = img.width;
this.scaleY = img.height;
}
// Entity.set()
Entity.prototype.set = function(x,y){
this.xpos=x;
this.ypos=y;
}
// Entity.scale()
Entity.prototype.scale = function(scaleX,scaleY){
this.scaleX=scaleX;
this.scaleY=scaleY;
}
// Entity.draw()
Entity.prototype.draw = function(){
this.context.drawImage(this.img,
0,0,this.width,this.height,
this.xpos,this.ypos,this.scaleX,this.scaleY);
}
}); // end $(function(){});
</script>
</head>
<body>
<h3>Watch out Kitty!</h3><br>
<div id="wrapper">
<canvas id="bg" width=350 height=400></canvas>
<canvas id="stage" width=350 height=400></canvas>
<canvas id="entities" width=350 height=400></canvas>
</div>
</body>
</html>
Been working on an easel game tutorial that involves an animated character going across the screen and avoiding crates falling from above. I've followed the code in the tutorial exactly but no joy;nothing seems to be loading (including images which are mapped correctly). No errors regarding syntax seem to occur so not sure what the problem is. It's a fair bit of code so totally understand if no-one has the time but just in case here it is ;
Site Page code (ill post the individual JavaScript file codes if anyone is interested;
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: #000;
}
</style>
<script src="lib/easeljs-0.6.0.min.js"></script>
<script src="JS/Platform.js"></script>
<script src="JS/Hero.js"></script>
<script src="JS/Crate.js"></script>
<script>
var KEYCODE_SPACE = 32, KEYCODE_LEFT = 37, KEYCODE_RIGHT = 39;
var canvas, stage, lfheld, rtheld, platforms, crates, hero, heroCenter, key, door, gameTxt;
var keyDn = false, play=true, dir="right";
var loaded = 0, vy = 0, vx = 0;
var jumping = false, inAir = true, gravity = 2;
var img = new Image();
var bgimg = new Image();
var kimg = new Image();
var dimg = new Image();
var platformW = [300, 100, 180, 260, 260, 100, 100];
var platformX = [40, 220, 320, 580, 700, 760, 760];
var platformY = [460, 380, 300, 250, 550, 350, 450];
document.onkeydown=handleKeyDown;
document.onkeyup=handleKeyUp;
function init(){
canvas = document.getElementById("canvas");
stage = new createjs.Stage(canvas);
bgimg.onload = this.handleImageLoad;
bgimg.src = "img/scene.jpg";
kimg.onload = this.handleImageLoad;
kimg.src="img/key.png";
dimg.onload = this.handleImageLoad;
dimg.src = "img/door.jpg";
img.onload = this.handleImageLoad;
img.src = "img/hero.png";
}
function handleImageLoad(event) {
loaded+=1;
if (loaded==4){
start();
}
}
function handleKeyDown(e) {
if(!e){ var e = window.event; }
switch(e.keycode) {
case KEYCODE_LEFT: lfHeld = true;
dir="left"; break;
case KEYCODE_RIGHT: rtHeld = true;
dir="right"; break;
case KEYCODE_SPACE: jump(); break;
}
}
function handleKeyUp(e) {
if(!e){ var e = window.event; }
switch(e.keycode) {
case KEYCODE_LEFT: lfHeld = false;
keyDn=false; hero.gotoAndStop("idle_h"); break;
case KEYCODE_RIGHT: rtHeld = false;
keyDn=false; hero.gotoAndStop("idle"); break;
}
}
function start(){
var bg = new createjs.Bitmap(bgimg);
stage.addChild(bg);
door = new createjs.Bitmap(dimg);
door.x = 131;
door.y = 384;
stage.addChild(door);
hero = new Hero(img);
hero.x = 80;
hero.y = 450;
stage.addChild(Hero);
crates = new Array();
paltforms = new Array();
for(i=0; i < platformW.length; i++){
var platform = new Platform(platformW[i],20);
platforms.push(platform);
stage.addChild(platform);
platform.x = platformX[i];
platform.y = platformY[i];
}
for(j=0; j < 5; j++){
var crate = new Crate();
crates.push(crate);
stage.addChild(crate);
resetCrates(crate);
}
key = new createjs.Bitmap(kimg);
key.x = 900;
key.y = 490;
stage.addChild(key);
Ticker.setFPS(30);
Ticker.addListener(window);
stage.update();
}
function tick() {
heroCenter = hero.y-40;
if (play){
vy+=gravity;
if (inAir){
hero.y+=vy;
}
if (vy>15){
vy=15;
}
for(i=0; i < platforms.length; i++){
if (hero.y >= platforms[i].y && hero.y<=(platforms[i].y+platforms[i].height) && hero.x>
platforms[i].x && hero.x<(platforms[i].
x+platforms[i].width)){;
hero.y=platforms[i].y;
vy=0;
jumping = false;
inAir = false;
break;
}else{
inAir = true;
}
}
for(j=0; j < crates.length; j++){
var ct = crates[j];
ct.y+=ct.speed;
ct.rotation+=3;
if (ct.y>650){
resetCrates(ct);
}
if (collisionHero (ct.x, ct.y, 20)){
gameOver();}
}
if (collisionHero (key.x+20, key.y+20,
20)){
key.visible=false;
door.visible=false;
}
if (collisionHero (door.x+20, door.y+40,
20) && key.visible==false){nextLevel();}
if (lfHeld){vx = -5;}
if (rtHeld){vx = 5;}
if(lfHeld && keyDn==false && inAir==false)
{
hero.gotoAndPlay("walk_h");
keyDn=true;
}
if(rtHeld && keyDn==false &&
inAir==false){
hero.gotoAndPlay("walk");
keyDn=true;
}
if (dir=="left" && keyDn==false &&
inAir==false){
hero.gotoAndStop("idle_h");
}
if (dir=="right" && keyDn==false &&
inAir==false){
hero.gotoAndStop("idle");
}
hero.x+=vx;
vx=vx*0.5;
if (hero.y>610){
gameOver();
}
}
stage.update();
}
function end(){
play=false;
var l = crates.length;
for (var i=0; i<l; i++){
var c = crates[i];
resetCrates(c);
}
hero.visible=false;
stage.update();
}
function nextLevel(){
gameTxt = new createjs.Text("Well Done\n\n",
"36px Arial", "#000");
gameTxt.text += "Prepare for Level 2";
gameTxt.textAlign = "center";
gameTxt.x = canvas.width / 2;
gameTxt.y = canvas.height / 4;
stage.addChild(gameTxt);
end();
}
function gameOver(){
gameTxt = new createjs.Text("Game Over\n\n",
"36px Arial", "#000");
gameTxt.text += "Click to Play Again.";
gameTxt.textAlign = "center";
gameTxt.x = canvas.width / 2;
gameTxt.y = canvas.height / 4;
stage.addChild(gameTxt);
end();
canvas.onclick = handleClick;
}
function handleClick() {
canvas.onclick = null;
hero.visible=true;
hero.x = 80;
hero.y = 450;
door.visible=true;
key.visible=true;
stage.removeChild(gameTxt);
play=true;
}
function collisionHero (xPos, yPos,
Radius){
var distX = xPos - hero.x;
var distY = yPos - heroCenter;
var distR = Radius + 20;
if (distX * distX + distY * distY <=
distR * distR){
return true;
}
}
function jump(){
if (jumping == false && inAir == false){
if (dir=="right"){
hero.gotoAndStop("jump");
}else{
hero.gotoAndStop("jump_h");
}
hero.y -=20;
vy = -25;
jumping = true;
keyDn=false;
}
}
function resetCrates(crt) {
crt.x = canvas.width * Math.random()|0;
crt.y = 0 - Math.random()*500;
crt.speed = (Math.random()*5)+8;
}
</script>
<title>Game</title>
</head>
<body onload="init();">
<canvas id="canvas" width="960px" height="600px"></canvas>
</body>
</html>
Adding the js files as listed in the header.
Platform.js:
(function(window) {
function Platform(w,h) {
this.width = w;
this.height = h;
this.initialize();
}
Platform.prototype = new createjs.Container ();
Platform.prototype.platformBody = null;
Platform.prototype.Container_initialize = Platform.prototype.initialize;
Platform.prototype.initialize = function() {
this.Container_initialize();
this.platformBody = new createjs.Shape();
this.addChild(this.platformBody);
this.makeShape();
}
Platform.prototype.makeShape = function() {
var g = this.platformBody.graphics;
g.drawRect(0,0,this.width,this.height);
}
window.Platform = Platform;
}(window));
Hero.js
(function(window) {
function Hero(imgHero) {
this.initialize(imgHero);
}
Hero.prototype = new createjs.BitmapAnimation();
Hero.prototype.Animation_initialize = Hero.prototype.initialize;
Hero.prototype.initialize = function(imgHero) {
var spriteSheet = new createjs.SpriteSheet({
images: [imgHero],
frames: {width: 60, height: 85, regX: 29, regY: 80}, animations: {
walk: [0, 19, "walk"],
idle: [20, 20],
jump: [21, 21] } });
SpriteSheetUtils
.addFlippedFrames(spriteSheet, true, false,
false);
this.Animation_initialize(spriteSheet);
this.gotoAndStop("idle");
}
window.Hero = Hero;
}(window));
Crate.js
(function(window) {
function Crate() {
this.initialize();
}
Crate.prototype = new createjs.Container();
Crate.prototype.img = new Image();
Crate.prototype.Container_initialize =
Crate.prototype.initialize;
Crate.prototype.initialize = function() {
this.Container_initialize();
var bmp = new createjs.Bitmap("img/crate.jpg");
bmp.x=-20;
bmp.y=-20;
this.addChild(bmp);}
window.Crate = Crate;
}(window));
I noticed that you try to initialize the EaselJS-objects without a namespace:
stage = new Stage(canvas);
But since 0.5.0 you have to use the createjs-namespace(or map the namespace to window before you do anything)
So what you would have to do now will ALL easelJS-classes is to add a createjs. before them when you are creating a new instance like this:
stage = new createjs.Stage(canvas);
Not sure if that's everything, but it's a start, hope this helps.
You can read more on CreateJS namepsacing here: https://github.com/CreateJS/EaselJS/blob/master/README_CREATEJS_NAMESPACE.txt
I want to draw on a HTML Canvas using a mouse (for example, draw a signature, draw a name, ...)
How would I go about implementing this?
Here is a working sample.
<html>
<script type="text/javascript">
var canvas, ctx, flag = false,
prevX = 0,
currX = 0,
prevY = 0,
currY = 0,
dot_flag = false;
var x = "black",
y = 2;
function init() {
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
canvas.addEventListener("mousemove", function (e) {
findxy('move', e)
}, false);
canvas.addEventListener("mousedown", function (e) {
findxy('down', e)
}, false);
canvas.addEventListener("mouseup", function (e) {
findxy('up', e)
}, false);
canvas.addEventListener("mouseout", function (e) {
findxy('out', e)
}, false);
}
function color(obj) {
switch (obj.id) {
case "green":
x = "green";
break;
case "blue":
x = "blue";
break;
case "red":
x = "red";
break;
case "yellow":
x = "yellow";
break;
case "orange":
x = "orange";
break;
case "black":
x = "black";
break;
case "white":
x = "white";
break;
}
if (x == "white") y = 14;
else y = 2;
}
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
}
function erase() {
var m = confirm("Want to clear");
if (m) {
ctx.clearRect(0, 0, w, h);
document.getElementById("canvasimg").style.display = "none";
}
}
function save() {
document.getElementById("canvasimg").style.border = "2px solid";
var dataURL = canvas.toDataURL();
document.getElementById("canvasimg").src = dataURL;
document.getElementById("canvasimg").style.display = "inline";
}
function findxy(res, e) {
if (res == 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
flag = true;
dot_flag = true;
if (dot_flag) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(currX, currY, 2, 2);
ctx.closePath();
dot_flag = false;
}
}
if (res == 'up' || res == "out") {
flag = false;
}
if (res == 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
draw();
}
}
}
</script>
<body onload="init()">
<canvas id="can" width="400" height="400" style="position:absolute;top:10%;left:10%;border:2px solid;"></canvas>
<div style="position:absolute;top:12%;left:43%;">Choose Color</div>
<div style="position:absolute;top:15%;left:45%;width:10px;height:10px;background:green;" id="green" onclick="color(this)"></div>
<div style="position:absolute;top:15%;left:46%;width:10px;height:10px;background:blue;" id="blue" onclick="color(this)"></div>
<div style="position:absolute;top:15%;left:47%;width:10px;height:10px;background:red;" id="red" onclick="color(this)"></div>
<div style="position:absolute;top:17%;left:45%;width:10px;height:10px;background:yellow;" id="yellow" onclick="color(this)"></div>
<div style="position:absolute;top:17%;left:46%;width:10px;height:10px;background:orange;" id="orange" onclick="color(this)"></div>
<div style="position:absolute;top:17%;left:47%;width:10px;height:10px;background:black;" id="black" onclick="color(this)"></div>
<div style="position:absolute;top:20%;left:43%;">Eraser</div>
<div style="position:absolute;top:22%;left:45%;width:15px;height:15px;background:white;border:2px solid;" id="white" onclick="color(this)"></div>
<img id="canvasimg" style="position:absolute;top:10%;left:52%;" style="display:none;">
<input type="button" value="save" id="btn" size="30" onclick="save()" style="position:absolute;top:55%;left:10%;">
<input type="button" value="clear" id="clr" size="23" onclick="erase()" style="position:absolute;top:55%;left:15%;">
</body>
</html>
I think, other examples here are too complicated. This one is simpler and JS only...
// create canvas element and append it to document body
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
// some hotfixes... ( ≖_≖)
document.body.style.margin = 0;
canvas.style.position = 'fixed';
// get canvas 2D context and set him correct size
var ctx = canvas.getContext('2d');
resize();
// last known position
var pos = { x: 0, y: 0 };
window.addEventListener('resize', resize);
document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);
// new position from mouse event
function setPosition(e) {
pos.x = e.clientX;
pos.y = e.clientY;
}
// resize canvas
function resize() {
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
}
function draw(e) {
// mouse left button must be pressed
if (e.buttons !== 1) return;
ctx.beginPath(); // begin
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = '#c0392b';
ctx.moveTo(pos.x, pos.y); // from
setPosition(e);
ctx.lineTo(pos.x, pos.y); // to
ctx.stroke(); // draw it!
}
Here's the most straightforward way to create a drawing application with canvas:
Attach a mousedown, mousemove, and mouseup event listener to the canvas DOM
on mousedown, get the mouse coordinates, and use the moveTo() method to position your drawing cursor and the beginPath() method to begin a new drawing path.
on mousemove, continuously add a new point to the path with lineTo(), and color the last segment with stroke().
on mouseup, set a flag to disable the drawing.
From there, you can add all kinds of other features like giving the user the ability to choose a line thickness, color, brush strokes, and even layers.
I was looking to use this method for signatures as well, i found a sample on http://codetheory.in/.
I've added the below code to a jsfiddle
Html:
<div id="sketch">
<canvas id="paint"></canvas>
</div>
Javascript:
(function() {
var canvas = document.querySelector('#paint');
var ctx = canvas.getContext('2d');
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
var mouse = {x: 0, y: 0};
var last_mouse = {x: 0, y: 0};
/* Mouse Capturing Work */
canvas.addEventListener('mousemove', function(e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
}, false);
/* Drawing on Paint App */
ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = 'blue';
canvas.addEventListener('mousedown', function(e) {
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', function() {
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
var onPaint = function() {
ctx.beginPath();
ctx.moveTo(last_mouse.x, last_mouse.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.closePath();
ctx.stroke();
};
}());
Here is my very simple working canvas draw 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>
I had to provide a simple example for this subject so I'll share here:
http://jsfiddle.net/Haelle/v6tfp2e1
class SignTool {
constructor() {
this.initVars()
this.initEvents()
}
initVars() {
this.canvas = $('#canvas')[0]
this.ctx = this.canvas.getContext("2d")
this.isMouseClicked = false
this.isMouseInCanvas = false
this.prevX = 0
this.currX = 0
this.prevY = 0
this.currY = 0
}
initEvents() {
$('#canvas').on("mousemove", (e) => this.onMouseMove(e))
$('#canvas').on("mousedown", (e) => this.onMouseDown(e))
$('#canvas').on("mouseup", () => this.onMouseUp())
$('#canvas').on("mouseout", () => this.onMouseOut())
$('#canvas').on("mouseenter", (e) => this.onMouseEnter(e))
}
onMouseDown(e) {
this.isMouseClicked = true
this.updateCurrentPosition(e)
}
onMouseUp() {
this.isMouseClicked = false
}
onMouseEnter(e) {
this.isMouseInCanvas = true
this.updateCurrentPosition(e)
}
onMouseOut() {
this.isMouseInCanvas = false
}
onMouseMove(e) {
if (this.isMouseClicked && this.isMouseInCanvas) {
this.updateCurrentPosition(e)
this.draw()
}
}
updateCurrentPosition(e) {
this.prevX = this.currX
this.prevY = this.currY
this.currX = e.clientX - this.canvas.offsetLeft
this.currY = e.clientY - this.canvas.offsetTop
}
draw() {
this.ctx.beginPath()
this.ctx.moveTo(this.prevX, this.prevY)
this.ctx.lineTo(this.currX, this.currY)
this.ctx.strokeStyle = "black"
this.ctx.lineWidth = 2
this.ctx.stroke()
this.ctx.closePath()
}
}
var canvas = new SignTool()
canvas {
position: absolute;
border: 2px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="500" height="300"></canvas>
A super short version, here, without position:absolute in vanilla JavaScript. The main idea is to move the canvas' context to the right coordinates and draw a line. Uncomment click handler and comment mousedown & mousemove handlers below to get a feel for how it is working.
<!DOCTYPE html>
<html>
<body>
<p style="margin: 50px">Just some padding in y direction</p>
<canvas id="myCanvas" width="300" height="300" style="background: #000; margin-left: 100px;">Your browser does not support the HTML5 canvas tag.</canvas>
<script>
const c = document.getElementById("myCanvas");
// c.addEventListener("click", penTool); // fires after mouse left btn is released
c.addEventListener("mousedown", setLastCoords); // fires before mouse left btn is released
c.addEventListener("mousemove", freeForm);
const ctx = c.getContext("2d");
function setLastCoords(e) {
const {x, y} = c.getBoundingClientRect();
lastX = e.clientX - x;
lastY = e.clientY - y;
}
function freeForm(e) {
if (e.buttons !== 1) return; // left button is not pushed yet
penTool(e);
}
function penTool(e) {
const {x, y} = c.getBoundingClientRect();
const newX = e.clientX - x;
const newY = e.clientY - y;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.moveTo(lastX, lastY);
ctx.lineTo(newX, newY);
ctx.strokeStyle = 'white';
ctx.stroke();
ctx.closePath();
lastX = newX;
lastY = newY;
}
let lastX = 0;
let lastY = 0;
</script>
</body>
</html>
It's been years since the question was asked and was answered.
For anyone who looks for a simple drawing canvas (eg, for taking the signature from the user/customer), here I am posting a more simplified jquery version of the currently accepted answer
$(document).ready(function() {
var flag, dot_flag = false,
prevX, prevY, currX, currY = 0,
color = 'black', thickness = 2;
var $canvas = $('#canvas');
var ctx = $canvas[0].getContext('2d');
$canvas.on('mousemove mousedown mouseup mouseout', function(e) {
prevX = currX;
prevY = currY;
currX = e.clientX - $canvas.offset().left;
currY = e.clientY - $canvas.offset().top;
if (e.type == 'mousedown') {
flag = true;
}
if (e.type == 'mouseup' || e.type == 'mouseout') {
flag = false;
}
if (e.type == 'mousemove') {
if (flag) {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = color;
ctx.lineWidth = thickness;
ctx.stroke();
ctx.closePath();
}
}
});
$('.canvas-clear').on('click', function(e) {
c_width = $canvas.width();
c_height = $canvas.height();
ctx.clearRect(0, 0, c_width, c_height);
$('#canvasimg').hide();
});
});
<html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<body>
<canvas id="canvas" width="400" height="400" style="position:absolute;top:10%;left:10%;border:2px solid;"></canvas>
<input type="button" value="Clear" class="canvas-clear" />
</body>
</html>
I took the first answer and Changed as per my need.
Now
Works on mobile device also.
more responsive,
no issue with scroll
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
<style>
.selected::before {
content: 'x';
height: 30px;
width: 30px;
left: 18px;
top: -8px;
font-size: 3rem;
position: absolute;
}
.select-colour{
height: 30px;
width: 30px;
}
</style>
</head>
<script type="text/javascript">
var canvas, ctx, flag = false,
prevX = 0,
currX = 0,
prevY = 0,
currY = 0,
dot_flag = false;
var x = "black",
y = 2;
function init() {
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
canvas.addEventListener("mousemove", function (e) {
findxy('move', e, '')
}, false);
canvas.addEventListener("mousedown", function (e) {
findxy('down', e, '')
}, false);
canvas.addEventListener("mouseup", function (e) {
findxy('up', e, '')
}, false);
canvas.addEventListener("mouseout", function (e) {
findxy('out', e, '')
}, false);
canvas.addEventListener("touchmove", function (e) {
findxy('move', e, 'touch')
}, false);
canvas.addEventListener("touchstart", function (e) {
findxy('down', e, 'touch')
}, false);
canvas.addEventListener("touchend", function (e) {
findxy('up', e, 'touch')
}, false);
}
function color(obj) {
x = obj.id;
if (x == "white") y = 14;
else y = 2;
var prevSelected = document.getElementsByClassName("selected");
// If it exists, remove it.
if(prevSelected.length > 0) {
prevSelected[0].classList.remove("selected");
}
document.getElementById(obj.id).classList.add("selected");
}
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
}
function erase() {
var m = confirm("Want to clear");
if (m) {
ctx.clearRect(0, 0, w, h);
document.getElementById("canvasimg").style.display = "none";
}
}
function save() {
document.getElementById("canvasimg").style.border = "2px solid";
var dataURL = canvas.toDataURL();
document.getElementById("canvasimg").src = dataURL;
document.getElementById("canvasimg").style.display = "inline";
}
function download() {
var link = document.createElement('a');
var dataURL = canvas.toDataURL();
link.href = dataURL;
link.download = 'mydrawing.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function findxy(res, e, source) {
if(source){
var clientX = e.changedTouches[0].clientX;
var clientY = e.changedTouches[0].clientY;
}else{
var clientX = e.clientX;
var clientY = e.clientY;
}
if (res == 'down') {
prevX = currX;
prevY = currY;
currX = clientX - canvas.getBoundingClientRect().left;
currY = clientY - canvas.getBoundingClientRect().top;
flag = true;
dot_flag = true;
if (dot_flag) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(currX, currY, 2, 2);
ctx.closePath();
dot_flag = false;
}
}
if (res == 'up' || res == "out") {
flag = false;
}
if (res == 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = clientX - canvas.getBoundingClientRect().left;
currY = clientY - canvas.getBoundingClientRect().top;
draw();
}
}
}
</script>
<body onload="init()">
<div style="">
<div style="float: left;">
<canvas id="can" width="600" height="500" style=" border:5px solid;"></canvas>
</div>
<div style="margin-left:20px; float:left;">
<div class="row">
<div class="col-12">Choose Color</div>
<div class="col-2 pt-3"><div class="select-colour" style="background:green;" id="green" onclick="color(this)"></div></div>
<div class="col-2 pt-3"><div class="select-colour" style="background:blue;" id="blue" onclick="color(this)"></div></div>
<div class="col-2 pt-3"><div class="select-colour" style="background:red;" id="red" onclick="color(this)"></div></div>
<div class="col-2 pt-3"><div class="select-colour" style="background:yellow;" id="yellow" onclick="color(this)"></div></div>
<div class="col-2 pt-3"><div class="select-colour" style="background:orange;" id="orange" onclick="color(this)"></div></div>
<div class="col-2 pt-3"><div class="select-colour" style="background:black;" id="black" onclick="color(this)"></div></div>
<div class="col-12 pt-3 pb-3" style="">Eraser</div>
<div class="col-2 pt-3"><div class="select-colour" style="background:white;border:2px solid;" id="white" onclick="color(this)"></div></div>
</div>
<div class="row">
<div class="col-6 col-lg-12 mt-3">
<input type="button" value="Download" id="download" size="23" onclick="download()" class="btn btn-primary">
</div>
<div class="col-6 col-lg-12 mt-3">
<input type="button" value="clear" id="clr" size="23" onclick="erase()" class="btn btn-light">
</div>
</div>
</div>
</div>
</body>
</html>
Also check this one here:
Example:
https://github.com/williammalone/Simple-HTML5-Drawing-App
Documentation:
http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/
This document includes following codes:
HTML:
<canvas id="canvas" width="490" height="220"></canvas>
JS:
context = document.getElementById('canvas').getContext("2d");
$('#canvas').mousedown(function(e){
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
paint = true;
addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
redraw();
});
$('#canvas').mouseup(function(e){
paint = false;
});
$('#canvas').mouseleave(function(e){
paint = false;
});
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
function addClick(x, y, dragging)
{
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
//Also redraw
function redraw(){
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
context.strokeStyle = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
for(var i=0; i < clickX.length; i++) {
context.beginPath();
if(clickDrag[i] && i){
context.moveTo(clickX[i-1], clickY[i-1]);
}else{
context.moveTo(clickX[i]-1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.closePath();
context.stroke();
}
}
And another awesome example
http://perfectionkills.com/exploring-canvas-drawing-techniques/
I used what 1083202 did but removed all the controlles, and implemented the change that KWILLIAMS suggested, that made it non sensitive to scrolling. I also made the canvas big, basically over my whole page 2000x1600 px, excepts for the margins. I did remove all the drawing tools and buttons, and used "blue" as the only color.
I put the JS-code in a separate file named myJS.js, and put that in a folder named "JS" localy:
I then used a Stylus on my Laptop to write on the tuchpad, witch works out a bit better then with the finger or mouse.
The document I use it for is kind of informal, for internal use, but it is nice to be able to put some pen strokes on it before making a pdf-file.
var canvas, ctx, flag = false,
prevX = 0,
currX = 0,
prevY = 0,
currY = 0,
dot_flag = false;
var x = "blue",
y = 3;
function init() {
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
canvas.addEventListener("mousemove", function (e) {
findxy('move', e)
}, false);
canvas.addEventListener("mousedown", function (e) {
findxy('down', e)
}, false);
canvas.addEventListener("mouseup", function (e) {
findxy('up', e)
}, false);
canvas.addEventListener("mouseout", function (e) {
findxy('out', e)
}, false);
}
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
}
function findxy(res, e) {
if (res == 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.getBoundingClientRect().left;
currY = e.clientY - canvas.getBoundingClientRect().top;
flag = true;
dot_flag = true;
if (dot_flag) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(currX, currY, 2, 2);
ctx.closePath();
dot_flag = false;
}
}
if (res == 'up' || res == "out") {
flag = false;
}
if (res == 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.getBoundingClientRect().left;
currY = e.clientY - canvas.getBoundingClientRect().top;
draw();
}
}
}
<html>
<body onload="init()">
<p>Below you can draw:</p>
<canvas id="can" width="750" height="1050" style="position:absolute;">Below you can write:</canvas>
</body>
Let me know if you have trouble implementing this. It uses processing.js and has features for changing colors and making the draw point larger and smaller.
<html>
<head>
<!--script librarires-->
<script type="text/javascript" src="processing.js"></script>
<script type="text/javascript" src="init.js"></script>
<!--styles -->
<style type="text/css" src="stylesheet.css">
</style>
</head>
<body>
<!--toolbox -->
<div id="draggable toolbox"></div>
<script type="application/processing">
// new script
int prevx, prevy;
int newx, newy;
boolean cliked;
color c1 = #000000;
int largeur=2;
int ps = 20;
int px = 50;
int py = 50;
void setup() {
size(500,500);
frameRate(25);
background(50);
prevx = mouseX;
prevy = mouseY;
cliked = false;
}
void draw() {
//couleur
noStroke(0);
fill(#FFFFFF);//blanc
rect(px, py, ps, ps);
fill(#000000);
rect(px, py+(ps), ps, ps);
fill(#FF0000);
rect(px, py+(ps*2), ps, ps);
fill(#00FF00);
rect(px, py+(ps*3), ps, ps);
fill(#FFFF00);
rect(px, py+(ps*4), ps, ps);
fill(#0000FF);
rect(px, py+(ps*5), ps, ps);
//largeur
fill(#FFFFFF);
rect(px, py+(ps*7), ps, ps);
fill(#FFFFFF);
rect(px, py+(ps*8), ps, ps);
stroke(#000000);
line(px+2, py+(ps*7)+(ps/2), px+(ps-2), py+(ps*7)+(ps/2));
line(px+(ps/2), py+(ps*7)+1, px+(ps/2), py+(ps*8)-1);
line(px+2, py+(ps*8)+(ps/2), px+(ps-2), py+(ps*8)+(ps/2));
if(cliked==false){
prevx = mouseX;
prevy = mouseY;
}
if(mousePressed) {
cliked = true;
newx = mouseX;
newy = mouseY;
strokeWeight(largeur);
stroke(c1);
line(prevx, prevy, newx, newy);
prevx = newx;
prevy = newy;
}else{
cliked= false;
}
}
void mouseClicked() {
if (mouseX>=px && mouseX<=(px+ps)){
//couleur
if (mouseY>=py && mouseY<=py+(ps*6)){
c1 = get(mouseX, mouseY);
}
//largeur
if (mouseY>=py+(ps*7) && mouseY<=py+(ps*8)){
largeur += 2;
}
if (mouseY>=py+(ps*8) && mouseY<=py+(ps*9)){
if (largeur>2){
largeur -= 2;
}
}
}
}
</script><canvas></canvas>
</body>
</html>
if you have background image for your canvas, you will have to make some tweaks to have it work properly because white erasing trick will hide the background.
here is a gist with the code.
<html>
<script type="text/javascript">
var canvas, canvasimg, backgroundImage, finalImg;
var mouseClicked = false;
var prevX = 0;
var currX = 0;
var prevY = 0;
var currY = 0;
var fillStyle = "black";
var globalCompositeOperation = "source-over";
var lineWidth = 2;
function init() {
var imageSrc = '/abstract-geometric-pattern_23-2147508597.jpg'
backgroundImage = new Image();
backgroundImage.src = imageSrc;
canvas = document.getElementById('can');
finalImg = document.getElementById('finalImg');
canvasimg = document.getElementById('canvasimg');
canvas.style.backgroundImage = "url('" + imageSrc + "')";
canvas.addEventListener("mousemove", handleMouseEvent);
canvas.addEventListener("mousedown", handleMouseEvent);
canvas.addEventListener("mouseup", handleMouseEvent);
canvas.addEventListener("mouseout", handleMouseEvent);
}
function getColor(btn) {
globalCompositeOperation = 'source-over';
lineWidth = 2;
switch (btn.getAttribute('data-color')) {
case "green":
fillStyle = "green";
break;
case "blue":
fillStyle = "blue";
break;
case "red":
fillStyle = "red";
break;
case "yellow":
fillStyle = "yellow";
break;
case "orange":
fillStyle = "orange";
break;
case "black":
fillStyle = "black";
break;
case "eraser":
globalCompositeOperation = 'destination-out';
fillStyle = "rgba(0,0,0,1)";
lineWidth = 14;
break;
}
}
function draw(dot) {
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.globalCompositeOperation = globalCompositeOperation;
if(dot){
ctx.fillStyle = fillStyle;
ctx.fillRect(currX, currY, 2, 2);
} else {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = fillStyle;
ctx.lineWidth = lineWidth;
ctx.stroke();
}
ctx.closePath();
}
function erase() {
if (confirm("Want to clear")) {
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
document.getElementById("canvasimg").style.display = "none";
}
}
function save() {
canvas.style.border = "2px solid";
canvasimg.width = canvas.width;
canvasimg.height = canvas.height;
var ctx2 = canvasimg.getContext("2d");
// comment next line to save the draw only
ctx2.drawImage(backgroundImage, 0, 0);
ctx2.drawImage(canvas, 0, 0);
finalImg.src = canvasimg.toDataURL();
finalImg.style.display = "inline";
}
function handleMouseEvent(e) {
if (e.type === 'mousedown') {
prevX = currX;
prevY = currY;
currX = e.offsetX;
currY = e.offsetY;
mouseClicked = true;
draw(true);
}
if (e.type === 'mouseup' || e.type === "mouseout") {
mouseClicked = false;
}
if (e.type === 'mousemove') {
if (mouseClicked) {
prevX = currX;
prevY = currY;
currX = e.offsetX;
currY = e.offsetY;
draw();
}
}
}
</script>
<body onload="init()">
<canvas id="can" width="400" height="400" style="position:absolute;top:10%;left:10%;border:2px solid;">
</canvas>
<div style="position:absolute;top:12%;left:43%;">Choose Color</div>
<div style="position:absolute;top:15%;left:45%;width:10px;height:10px;background:green;" data-color="green" onclick="getColor(this)"></div>
<div style="position:absolute;top:15%;left:46%;width:10px;height:10px;background:blue;" data-color="blue" onclick="getColor(this)"></div>
<div style="position:absolute;top:15%;left:47%;width:10px;height:10px;background:red;" data-color="red" onclick="getColor(this)"></div>
<div style="position:absolute;top:17%;left:45%;width:10px;height:10px;background:yellow;" data-color="yellow" onclick="getColor(this)"></div>
<div style="position:absolute;top:17%;left:46%;width:10px;height:10px;background:orange;" data-color="orange" onclick="getColor(this)"></div>
<div style="position:absolute;top:17%;left:47%;width:10px;height:10px;background:black;" data-color="black" onclick="getColor(this)"></div>
<div style="position:absolute;top:20%;left:43%;">Eraser</div>
<div style="position:absolute;top:22%;left:45%;width:15px;height:15px;background:white;border:2px solid;" data-color="eraser" onclick="getColor(this)"></div>
<canvas id="canvasimg" style="display:none;" ></canvas>
<img id="finalImg" style="position:absolute;top:10%;left:52%;display:none;" >
<input type="button" value="save" id="btn" size="30" onclick="save()" style="position:absolute;top:55%;left:10%;">
<input type="button" value="clear" id="clr" size="23" onclick="erase()" style="position:absolute;top:55%;left:15%;">
</body>
</html>