I am attempting to animate an emoticon that was previously drawn in canvas. I am attempting to do a draw and clear using frames following a tutorial but am not getting results. I have 6 frames of the emoticon coded and am unsure how to include this within the code. This is what I have so far:
<!DOCTYPE html>
<html>
<head>
<title>Adding Animation</title>
<style>
canvas {
border: 3px #CCC solid;
}
</style>
</head>
<body>
<div id="container">
<canvas id="myCanvas" height="1200" width="900"></canvas>
</div>
<script>
var mainCanvas = document.querySelector("#myCanvas");
var mainContext = mainCanvas.getContext("2d");
var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;
function drawCircle() {
mainContext.clearRect(0, 0, canvasWidth, canvasHeight);
// color in the background
mainContext.fillStyle = "#EEEEEE";
mainContext.fillRect(0, 0, canvasWidth, canvasHeight);
// draw the circle
ctx.beginPath();
ctx.strokeStyle = "000000";
ctx.lineWidth = 5;
ctx.fillStyle = "yellow";
ctx.arc(600, 450, 150, 0, Math.PI * 2, true);
ctx.stroke();
ctx.closePath();
ctx.fill();
//The smile
ctx.beginPath();
ctxstrokeStyle = "black";
ctx.lineWidth = 2;
ctx.arc(600, 475, 75, .1 * Math.PI, Math.PI * .9, false)
ctx.stroke();
ctx.closePath();
//The eyes
//Left
ctx.save();
ctx.scale(0.65, 1);
ctx.beginPath();
ctx.arc(850, 405, 40, 0 * Math.PI, Math.PI * 2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
//Right
ctx.save();
ctx.scale(0.65, 1);
ctx.beginPath();
ctx.arc(1000,405,40, 0*Math.PI, Math.PI*2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore()
}
drawCircle();
</script>
</body>
</html>
I am unsure if I am even on the right track as I have a difficult time with animation. Does anyone have any suggestions they can give me guidance on?
You have 2 names for the context: mainContext & ctx.
Change it to a single name and your face is "smiley" ! :-)
...
To animate:
Use a requestAnimationFrame loop to change the scaleY value in scale(scaleX,scaleY) over time.
Here's annotated code and a Demo:
var mainCanvas = document.querySelector("#myCanvas");
var ctx = mainCanvas.getContext("2d");
var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;
ctx.translate(-425,-275);
drawCircle(1);
// global var to hold pct the left eye is open
// 1==fully open, 0==fully closed
var scaley=1;
var direction=-1;
// request 1 animate() loop
requestAnimationFrame(animate);
function animate(time){
// draw smiley with the specified eye openness
drawCircle(scaley);
scaley+=.02*direction;
if(scaley<0){
scaley=0;
direction=1;
}
if(scaley>1){
scaley=1;
direction=-1;
}
requestAnimationFrame(animate);
}
function drawCircle(scaleY) {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// color in the background
ctx.fillStyle = "#EEEEEE";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// draw the circle
ctx.beginPath();
ctx.strokeStyle = "000000";
ctx.lineWidth = 5;
ctx.fillStyle = "yellow";
ctx.arc(600, 450, 150, 0, Math.PI * 2, true);
ctx.stroke();
ctx.closePath();
ctx.fill();
//The smile
ctx.beginPath();
ctxstrokeStyle = "black";
ctx.lineWidth = 2;
ctx.arc(600, 475, 75, .1 * Math.PI, Math.PI * .9, false)
ctx.stroke();
//The eyes
//Left
ctx.save();
// move the [0,0] origin to the left eye's centerpoint
ctx.translate(550,405);
// close the left eye by the specified scaleY
ctx.scale(0.65, scaleY);
ctx.beginPath();
// draw the left eye (arc) at 0,0 because
// we translated the origin to [550,405] earlier
ctx.arc(0, 0, 40, 0 * Math.PI, Math.PI * 2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
//Right
ctx.save();
ctx.scale(0.65, 1);
ctx.beginPath();
ctx.arc(1000,405,40, 0*Math.PI, Math.PI*2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore()
}
<canvas id="myCanvas" height="1200" width="900"></canvas>
You never declared a ctx variable.
Change all your mainContext by ctx and it should be working fine.
Related
I'm trying to modify some getElementById code I found on the internet to write a function that uses a switch/case statement so that I can draw different shapes using the same function. Basically, I want to create a tiny state machine for shape drawing commands. JavaScript is still new to me, so I'm probably missing something glaringly obvious. Cheers!
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
<script src="draw_shapes.js"></script>
</head>
<body onload="draw01();">
<canvas id="circle" width="150" height="150"></canvas>
</body>
</html>
draw_shapes.js
function draw01() {
var canvas = document.getElementById('circle');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
var X = canvas.width / 2;
var Y = canvas.height / 2;
var R = 45;
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 3;
ctx.strokeStyle = '#000000';
ctx.stroke();
}
}
function draw02() {
const canvas = document.getElementById(canvas.id);
const ctx = canvas.getContext('2d');
switch (canvas.id) {
case "circle":
var X = canvas.width / 2;
var Y = canvas.height / 2;
var R = 45;
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 3;
ctx.strokeStyle = '#000000';
ctx.stroke();
break;
}
}
Second iteration. Still not quite working right, but I've got the setup more as I was envisioning.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
<script src="draw_shapes_02.js"></script>
</head>
<body onload="draw(CIRCLE);">
</body>
</html>
draw_shapes_02.js
//shape enumeration (an object with constant variables with immutable property values)
const shapes = {
CIRCLE: "circle",
RECTANGLE: "rectangle",
TRIANGLE: "triangle",
HOUSE: "house",
DEFAULT:"default",
}
Object.freeze(shapes);
// shape default setting and error
let shape = shapes.DEFAULT;
if (!shape) {
throw new Error("Shape is not defined");
}
// shape state machine
function draw(shapes) {
var canvas = document.getElementById("shape");
var ctx = canvas.getContext('2d');
switch ("shape") {
case shapes.CIRCLE:
ctx.beginPath();
ctx.fillStyle = "black";
ctx.arc(150, 60, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
break;
case shapes.RECTANGLE:
ctx.beginPath();
ctx.fillStyle = "green";
ctx.rect(100, 130, 100, 50);
ctx.fill();
ctx.closePath();
break;
case shapes.TRIANGLE:
ctx.beginPath();
ctx.fillStyle = "yellow";
ctx.moveTo(150, 200);
ctx.lineTo(200, 250);
ctx.lineTo(100, 250);
ctx.lineTo(150, 200);
ctx.fill();
ctx.closePath();
break;
case shapes.HOUSE:
ctx.lineWidth = 10;
ctx.strokeRect(75, 140, 150, 110);
ctx.fillRect(130, 190, 40, 60);
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();
break;
case shapes.DEFAULT:
break;
}
}
I have simplified a bit but you can complicate again as you wish :)
function draw(shape) {
var canvas = document.getElementById("CIRCLE");
var ctx = canvas.getContext('2d');
console.log(shape);
switch (shape) {
case "CIRCLE":
ctx.beginPath();
ctx.fillStyle = "black";
ctx.arc(150, 60, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
break;
case "RECTANGLE":
ctx.beginPath();
ctx.fillStyle = "green";
ctx.rect(100, 130, 100, 50);
ctx.fill();
ctx.closePath();
break;
case "TRIANGLE":
ctx.beginPath();
ctx.fillStyle = "yellow";
ctx.moveTo(150, 200);
ctx.lineTo(200, 250);
ctx.lineTo(100, 250);
ctx.lineTo(150, 200);
ctx.fill();
ctx.closePath();
break;
case "HOUSE":
ctx.lineWidth = 10;
ctx.strokeRect(75, 140, 150, 110);
ctx.fillRect(130, 190, 40, 60);
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();
break;
default:
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,75);
break;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
</head>
<body onload="draw('HOUSE');">
<canvas width="240" height="297" style="border:1px solid #d3d3d3;" id="CIRCLE"></canvas>
</body>
</html>
//Selectors
canvas = document.querySelector('.canvas');
canvas.setAttribute("tabindex", 0);
canvas.focus();
pointerImg = document.querySelector('.pointer');
//Variables
const pi = Math.PI;
const c = canvas.getContext('2d');
var grid = {
'x':60,
'y':20,
'size':20,
}
var pointerValues = {
37: {'x':-1, 'y':0},
38: {'x':0, 'y':-1},
39: {'x':1, 'y':0},
40: {'x':0, 'y':1},
}
//Event Listeners
window.addEventListener('resize', function() {
//init();
})
var cells = [];
for(let i=0;i<grid.x;i++) {
cells[i] = new Array(grid.y);
}
//Functions
function init() {
resizeGrid();
initCells();
}
init();
function update(){
requestAnimationFrame(update)
}
update();
function Cell(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.color = 'white';
this.update = function() {
this.draw();
}
this.draw = function() {
c.beginPath();
c.rect(this.x*this.size, this.y*this.size, this.size, this.size);
c.strokeStyle = 'black';
c.stroke();
c.fillStyle=this.color; c.fill();
c.closePath();
}
this.resetCell = function() {
c.clearRect(this.x, this.y, this.size, this.size);
this.update();
}
}
function resizeGrid() {
let windowX = window.innerWidth;
windowX -= windowX*0.1;
grid.size = windowX/grid.x;
let canvasX = windowX, canvasY = grid.y * grid.size;
canvas.width = canvasX;
canvas.height = canvasY;
}
function initCells() {
cells = [];
for(let i=0;i<grid.x;i++) {
cells[i] = new Array(grid.y);
}
for(let i=0;i<grid.x;i++) {
for(let j=0;j<grid.y;j++) {
cells[i][j] = new Cell(i, j, grid.size);
cells[i][j].update();
}
}
}
<html>
<head>
<link rel="stylesheet" href="assets/css/main.css">
<div style="display:none;">
<img class='pointer' src="assets/img/pointer.svg">
</div>
<div class='nav'>
</div>
</head>
<body>
<div class="content">
<canvas class='canvas'></canvas>
</div>
<script src='assets/js/canvas.js'></script>
</body>
</html>
function Cell(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.color = 'white';
this.update = function () {
this.draw();
}
this.draw = function () {
c.beginPath();
c.rect(this.x * this.size, this.y * this.size, this.size, this.size);
c.strokeStyle = 'black';
c.stroke();
c.fillStyle = this.color;
c.fill();
c.closePath();
}
this.resetCell = function () {
c.clearRect(this.x, this.y, this.size, this.size);
this.update();
}
This is my code for a single cell in a grid. After I draw it a single time, when I call resetCell(), it draws the cell just a little bit bigger.
In this picture, I call Update for the very first cell and it becomes a tiny bit bigger than the rest.
I've tried pretty much everything but nothing seems to work.
Also can someone recommend better way to draw and control grid.
I need it to demonstrate BFS algorithm.
You were tricked by the thickness of the border
The border has a thickness, which is easy to forget. If you draw a box which is X wide, its left and right border are in addition to the width of X.
So if you do NOT fill the interior, you get this nice appearance (left), but if you FILL, you get this ugly appearance (right).
When you draw a grid of these squares, each one covers over the right-hand and bottom sides of previous squares, so that it is not obvious what is happening.
Unless you redraw one that is not the last of the list, as I have done here (bottom-middle). Then it becomes obvious that something is wrong.
Here is the code to reproduce the above figures, and below I recommend a solution.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// With NO fill, it looks fine
ctx.beginPath();
ctx.rect(40, 40, 40, 40);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.rect(80, 80, 40, 40);
ctx.stroke();
ctx.closePath();
// With white fill, the inner half of each box gets covered up by the white fill.
ctx.beginPath();
ctx.rect(140, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(180, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
// Make Grid. This part looks OK initially.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.rect(240, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(240, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(280, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(280, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(320, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(320, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
// Redraw one: this will look messed up.
ctx.beginPath();
ctx.rect(280, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="400" height="150" ></canvas>
</body>
</html>
Solution 1. Don't fill the interiors
This is the neatest solution, if you can get away with it, and it works regardless of any (non-zero) border thickness.
Solution 2. Shrink the width to allow for border thickness
This is complicated because you need to manually set the border thickness, and realise that it is in addition to the numerical values you specify for the rectangle's size.
Look at this messy business at CSS-tricks, even when they are trying to make borders interesting! https://css-tricks.com/almanac/properties/b/border/
For one of my application i am creating circular time range selector widget.I have created the clock dial using html canvas.I need help to select time range by touch and drag on the dial as shown in the imageCircular clock dial. When clicked on the "Add" button, the previous selection should be cleared. Next time i should be able to set new range. this continues. The range value should be stored in a variable. It should work on the mobile device. Your help would be appreciated. http://codepen.io/harish_s/pen/ggpoBq
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var radius = canvas.height / 2;
ctx.translate(radius, radius);
radius = 180;
drawClock();
function drawClock() {
drawFace(ctx, radius);
drawNumbers(ctx, radius);
}
function drawFace(ctx, radius) {
var grad;
ctx.beginPath();
ctx.strokeStyle = "rgb(207,207,207)";
ctx.arc(0, 0, radius, 0, 2*Math.PI);
ctx.lineWidth = 60;
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, 150, 0, 2*Math.PI);
ctx.fillStyle = "rgb(243,244,244)";
ctx.fill();
ctx.beginPath();
ctx.arc(0, 0, 50, 0, 2*Math.PI);
ctx.fillStyle = '#333';
ctx.fill();
ctx.font = '25pt Calibri';
ctx.fillStyle = 'white';
ctx.fillText('SET', -25, 10);
}
function drawNumbers(ctx, radius) {
var ang;
var num;
ctx.font = radius*0.20 + "px arial";
ctx.fillStyle = '#000';
ctx.textBaseline="middle";
ctx.textAlign="center";
for(num = 1; num < 13; num++){
ang = num * Math.PI / 6;
ctx.rotate(ang);
ctx.translate(0, -radius*1);
ctx.rotate(-ang);
ctx.fillText(num.toString(), 0, 0);
ctx.rotate(ang);
ctx.translate(0, radius*1);
ctx.rotate(-ang);
}
}
#canvas{
position: absolute;
width: 50%;
left: 35%;
top:5%;
}
<canvas id="canvas" width="1000" height="500">
</canvas>
The problem is demonstrated in the image below
In other words, how to exactly compute the x and y coordinates for every frame?
ctx.arc(x,y,radius,0,pi*2,false)
because after incrementing one coordinate, I have a problem how to compute the other
I tried this but doesn't work
var step=2,x=100,y=100,r=50,coordinates=[[x,y-r]];
for(var i=1;i <r;i+=step){
bx=x;
x+=step;
y=y-Math.sqrt(Math.pow(r,2)-Math.pow(bx-x,2))
coordinates[i]=[x,y];
}
a jsfiddle will be appreciated.
var canvas = document.querySelector("#c");
var ctx = canvas.getContext("2d");
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var speed = 10; // Lower is faster
function animate(t){
ctx.save();
ctx.clearRect (0 , 0 ,canvas.width ,canvas.height );
ctx.translate(canvas.width/2, canvas.height/2);
// First circle
ctx.beginPath();
ctx.arc(0, 0, 5, 0, 2 * Math.PI, false);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.closePath();
// rotate + move along
ctx.rotate((t/speed)/100);
ctx.translate(100,0);
// Orbiting cirle
ctx.beginPath();
ctx.arc(0, 0, 10, 0, 2 * Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
ctx.closePath();
ctx.restore();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
canvas {
border: 1px solid black;
}
<canvas id="c" width="512" height="512"></canvas>
I am read tons of documentation about save/restore canvas states, but still confused with next example.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// save default state
ctx.save();
// draw new arc with new settings
ctx.lineWidth = 2;
ctx.fillStyle = '#bfb';
ctx.strokeStyle = '#999';
ctx.beginPath();
ctx.arc(50, 50, 10, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.stroke();
// restore default state
ctx.restore();
// save default state again
ctx.save();
// draw line with new settings
ctx.lineWidth = 4;
ctx.strokeStyle = '#000';
ctx.moveTo(50, 50);
ctx.lineTo(100, 100);
ctx.stroke();
// restore default state
ctx.restore();
// save default state third time
ctx.save();
// draw round circle with new settings
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = '#999';
ctx.arc(100, 100, 10, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = '#bfb';
ctx.fill();
ctx.stroke();
// restore default state
ctx.restore();
}
draw();
My logic in code comments, but result absolutely not expected. First circle has a settings from line. Circles should have different style from line.
I am not good at canvas just yet but with some basic learning I think
You are missing ctx.beginPath(); before starting to draw path.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// save default state
ctx.save();
// draw new arc with new settings
ctx.lineWidth = 2;
ctx.fillStyle = '#bfb';
ctx.strokeStyle = '#999';
ctx.beginPath();
ctx.arc(50, 50, 10, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
ctx.stroke();
// restore default state
ctx.restore();
// save default state again
ctx.save();
// draw line with new settings
ctx.beginPath();
ctx.lineWidth = 4;
ctx.strokeStyle = '#000';
ctx.moveTo(50, 50);
ctx.lineTo(100, 100);
ctx.stroke();
// restore default state
ctx.restore();
// save default state third time
ctx.save();
// draw round circle with new settings
ctx.beginPath(); /* ** THIS is missing in your code ** */
ctx.lineWidth = 2;
ctx.strokeStyle = '#999';
ctx.arc(100, 100, 10, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = '#bfb';
ctx.fill();
ctx.stroke();
// restore default state
ctx.restore();
}
draw();
DEMO
SOURCE