I'm learning javascript, so a coworker is having me do basic animation in the browser. I just finished my code, using the canvas element to display images to the screen, but nothing happens when I run the code. My debug statements don't get printed to the console, nor do any errors. Can anyone see a problem with my code?
<html>
<body>
<canvas id="console" width="600" height="600"></canvas>
<script type="text/javascript">
(function() {
console.log("at top of script");
var ballImage = new Image();
ballImage.src = "http://upload.wikimedia.org/wikipedia/en/e/ec/Soccer_ball.svg";
var time_stamp;
var balls[];
var BALL_COUNT = 2;
function requestAnimFrame (cb) {
function fallback (cb){
window.setTimeout(cb, 10);
}
var func = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
fallback;
func (cb);
}
function update() {
var ts = Date.now();
var time_elapsed = ts - (time_stamp || ts);
time_stamp = ts;
canvas = document.getElementById("canvas");
// update ball positions and detect wall collisions
balls.forEach(function (ball){
canvas.clearRect( ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
ball.update(time_elapsed);
canvas.drawImage( ball.image, ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
});
}
function programRun() {
update();
requestAnimationFrame(programRun);
}
/*
CLASS: ball
*/
function Ball() {
// all the code and functions for the ball class....
}
ballImage.onload = function() {
// fill up the array containing the ball objects
for (var i = 0; i < BALL_COUNT; ++i){
balls.push (new Ball ("ball" + i));
}
// start running the program
programRun();
};
console.log("got to end of script");
}) ();
<script>
</body>
</html>
I'm almost certain it's some basic thing I messed up, because I didn't get any errors back from running the code. Any help is appreciated :)
This line caused it..
var balls[];
correct it by replacing it with:
var balls = [];
you should see the logs then.
Update:
I created a fiddle with the code above and revised some fragments of your code to conform to a version that may suit your needs in writing your own version of the ball animation.
The following are the problems that I saw in your code:
Of course the array declaration mentioned above var balls[]; to var balls = [];
Don't forget to declare variables with the var keyword. I changed
time_stamp = ts;
canvas = document.getElementById("canvas");
to
var time_stamp = ts;
var canvas = document.getElementById("canvas");
3. Your html code suggests that the ID of the canvas is id="console" but your javascript tries to get an element with id="canvas".
Changed
<canvas id="console" width="600" height="600"></canvas>
to
<canvas id="canvas" width="600" height="600"></canvas>
Inside your ball.each() iteration uses the canvas element to invoke methods that are supposed to be within the CanvasRenderingContext2D.
Add something after your var canvas declaration.
var ctx = canvas.getContext('2d');
use the ctx variable to manipulate the canvas itself. This should be your function update() definition.
function update() {
var ts = Date.now();
var time_elapsed = ts - (time_stamp || ts);
var time_stamp = ts;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
// update ball positions and detect wall collisions
balls.forEach(function (ball){
ctx.clearRect( ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
ball.update();
ctx.drawImage( ballImage, ball.xLocation, ball.yLocation, ball.scaleWidth, ball.scaleHeight );
});
}
UPDATE:
Always use var when declaring variables. If you want to use them as a variable that can be accessed from different scopes(inner scopes) then you can do it by declaring them a scope higher.
Something like this:
function BallAnimation() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
function updateBall() {
ctx.fillRect(.....);
...
}
function Ball() {
this.canvas = canvas;
}
}
In such a manner you can still obtain the reference of the variables that you want to share from other scopes. Read this article to know more why vars are important.
Related
I'm programming a game
I have the next settings:
HTML game.html
<canvas id="canvas" width="288" height="512"></canvas>
<script src="game.js"></script>
JAVASCRIPT game.js
var document;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//load images
var pipeNorth = new Image();
pipeNorth.src = "images/pipeNorth.png";
function draw(){
/*code */
alert(pipeNorth.height);
requestAnimationFrame(draw);
}
draw();
PROBLEM
If I set an alert displaying the image height, it shows 0 during the first two or three alerts, then it starts showing the real height.
Why does this happen? How do I fix this? I think it means the image is not fully loaded when the game begins?
From my experience this happens because the document rendering takes some time. My technique for this case is a time delay:
function draw(){
var delay = 200;//Your delay in milliseconds
setTimeout(function(){
/*code */
alert(pipeNorth.height);
requestAnimationFrame(draw);
},delay);
}
Is it usable for you?
Yes you are right the image is not fully loaded when the game begins.to fix this you simply have to use the "load" event :
var document;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//load images
var pipeNorth = new Image();
pipeNorth.src = "images/pipeNorth.png";
function draw(){
/*code */
alert(pipeNorth.height);
requestAnimationFrame(draw);
}
pipeNorth.addEventListener("load",function(){
//this function will execute when the image is fully loaded
draw();
});
I have a Image and I want to paint on top of it some square. But I keep getting undefined function on getContext('2d'). I must add that the var img is an image which is already loaded on the page, I'm trying to interpret it as a canvas since I am using another script which lets to select areas on the image and if I use a canvas the script will not work. So in the case where I can not interpret a image as a canvas what would you suggest?
Js function
function drawInput(dx1,dy1,dx2,dy2) {
var img = document.getElementById('home:tempImg');
var canvas = img;
console.log(canvas);
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, dx1, dy1,dx2-dx1,dy2-dy1);
};
imageObj.src = 'images/selected.png';
}
HTML
<h:body>
<h:form onsubmit="#{getComponents.getAllComponents()}" id="home">
<div>
<p:graphicImage id="tempImg" rendered="true" value="#{imageView.selectedImg}">
</p:graphicImage>
</div>
</h:form>
</h:body>
<script type="text/javascript">
var myJSONStr ='{ "area" : #{areaInputView.areaListString}}';
console.log(myJSONStr);
var json = JSON.parse(myJSONStr);
//console.log(json.area[1].x1);
for(var i=0;myJSONStr.length >i ; i++) {
console.log(json.area[i].x1, json.area[i].y1, json.area[i].x2, json.area[i].y2);
drawInput(json.area[i].x1, json.area[i].y1, json.area[i].x2, json.area[i].y2);
};
</script>
</html>
By leveraging custom JS 'classes' (of the type app.Image in this case), we can build a pretty neat wrapper that allows us to draw images dynamically based on other instances we have kept track of.
Check out this Codepen for a working example.
window.app = window.app || {};
app.Canvas = document.getElementById('myCanvas').getContext('2d');
app.Image = function(source, x, y) {
this.x = x;
this.y = y;
this.data = new Image();
this.data.addEventListener('load', this.draw.bind(this));
this.data.src = source;
};
app.Image.prototype.draw = function() {
app.Canvas.drawImage(this.data, this.x, this.y);
};
app.Image.prototype.getImageBounds = function() {
return {
height: this.data.height,
width: this.data.width
}
};
var myImage = new app.Image('http://placehold.it/250x250', 10, 10);
var mySecondImage = new app.Image('http://placesheen.com/100/100', myImage.x + 10, myImage.y + 10);
The most important line is the last one. Notice how we're initializing a new app.Image with x and y coordinates relative to the first image. By using a custom wrapper class that keeps track of this information, we're able to easily access the necessary data that we need in a variety of circumstances.
I hope this helps to get you on the right track! Let me know if you have any questions about the code, and I'll be glad to explain.
I have been around this problem for days now.I have written my code in constructor pattern for the first time.I want to extend the height of 10 bezier lines in transition.I have tried kineticjs(i failed),tried Setinterval(creates jerk in animation).So I finally resorted to requestAnimationFrame.But because of this constructor pattern,I am totally confused where to include it and what changes are to be made.
This is what I have done so far---JSFIDDLE
So basically I will be extending my endY and cpY1 and cpy2 in transition.Onmouseover of canvas the height of all bezier lines must increase in transition giving it an animation like feel.
JAVASCRIPT:
//for entire code please have a look at the fiddle.this is just 10% of my code
//for simplification purpose,you can use 3 instances instead of 9!!!
(function() {
hair = function() {
return this;
};
hair.prototype={
draw_hair:function(a,b,c,d,e,f,g,h){
var sx =136+a;//start position of curve.used in moveTo(sx,sy)
var sy =235+b;
var cp1x=136+c;//control point 1
var cp1y=222+d;
var cp2x=136+e;//control point 2
var cp2y=222+f;
var endx=136+g;//end points
var endy=210+h;
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.strokeStyle="grey";
context.lineWidth="8";
context.beginPath();
context.moveTo(sx,sy);
context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,endx,endy);
context.lineCap = 'round';
context.stroke();
}
};
})();
Here is the answer you wanted about growing hair.
This also has the info you wanted about how to create a hair "object".
Code and a Fiddle: http://jsfiddle.net/m1erickson/8K825/
<!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; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
window.requestAnimationFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
// the Hair "object"
// Hair is a containing object that hosts a user-specified # of hairs
var Hair = (function () {
// constructor
function Hair(x,y,width,height,haircount) {
this.x=x;
this.y=y;
this.width=width;
this.height=height;
this.right=this.x+this.width;
this.bottom=this.y+this.height;
this.hairCount=haircount;
this.startX=x+20;
this.startY=y+height-3; //235;
this.hairHeight=25;
this.hairGrowth=0;
this.lastEndX=[];
for(var i=0;i<haircount;i++){
this.lastEndX[i]= x+20+(i*15);
}
}
// grows the hair
// works by changing the Y value of the end & control points
Hair.prototype.grow = function(increment){
this.hairGrowth+=increment;
return(this.hairGrowth);
}
// draw all the hairs
Hair.prototype.draw = function(mouseX){
// clear this object's space on the canvas
// and set its styles
ctx.clearRect(this.x,this.y,this.width,this.height);
ctx.beginPath();
ctx.strokeStyle="grey";
ctx.lineWidth=7;
ctx.lineCap = 'round';
ctx.beginPath();
for(var i=0;i<this.hairCount;i++){
// straight hair
var sx=cp1x=cp2x= this.startX+(i*15);
var sy= this.startY;
var cp1y = cp2y = (this.startY-(this.hairHeight+this.hairGrowth)/2);
var endy = this.startY-this.hairHeight-this.hairGrowth;
var endx = this.lastEndX[i];
// create bend, if any
if(Math.abs(mouseX-sx)<=10){
endx = sx+(mouseX-sx)*1.1;
this.lastEndX[i]=endx;
};
// draw this curve
ctx.moveTo(sx,sy);
ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,endx,endy);
}
// stroke
ctx.stroke();
// temp outline
ctx.lineWidth=1;
ctx.beginPath();
ctx.rect(this.x,this.y,this.width,this.height);
ctx.stroke();
}
//
return Hair;
})();
var direction=1;
var fps = 3;
function animate() {
setTimeout(function() {
// change hair length
var hairLength=hair.grow(direction);
if(hairLength<1 || hairLength>10){ direction=(-direction); }
// draw
hair.draw();
// request next frame
requestAnimationFrame(animate);
}, 1000 / fps);
}
function handleMouseMove(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
$("#movelog").html("Move: "+ mouseX + " / " + mouseY);
// Put your mousemove stuff here
if(mouseX>=hair.x && mouseX<=hair.right && mouseY>=hair.y && mouseY<=hair.bottom){
hair.draw(mouseX);
}
}
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#grow").click(function(e){ animate(); });
// create a new patch of hair
var hair=new Hair(25,50,150,50,8);
hair.draw(225);
}); // end $(function(){});
</script>
</head>
<body>
<p id="movelog">Move</p>
<canvas id="canvas" width=300 height=200></canvas><br>
<button id="grow">Grow Hair</button>
</body>
</html>
[ Added: explanation of “Class” and instantiating an object from a class ]
var Hair=(function(){ …; return Hair; })() creates a Hair “class”.
var hair = new Hair(…) creates an actual, usable Hair “object”.
Think of the Hair-class as a template (or blueprint or cookie-cutter). All the code inside Hair-class only defines the properties and methods of the class. You don’t actually call any of the code in the Hair class. You just use it as a template to create actual hair objects.
You can use the Hair class to create as many actual hair objects as you need—it’s reusable. The act of creating a hair object is known as “instantiating the Hair-class”.
BTW, javascript does not actually have classes, so this is just a pseudo-class. But that’s a whole other explanation!
You ask: What’s the use of direction=0.25?
The direction variable is used to incrementally increase the height of hair when hair is “growing” during animation. The .25 tells the control/end points of the hair to go up by .25 pixels per animation frame.
You ask: What’s the significance of callback function and that settimeout?
setTimeout is wrapping requestAnimationFrame so that the animation occurs at a fixed frame-per-second.
RequestAnimationFrame (RAF) does a great job of optimizing performance, but you can’t control the frames-per-second with RAF alone. If you wrap RAF inside setTimeout, you can control the frames-per-second. For example, setTimeout(anyFunction,1000/fps) will trigger anyFunction about 3 times a second when fps==3. See this nice article on RAF+setTimeout: http://creativejs.com/resources/requestanimationframe/
As you discovered, RAF without setTimeout will still work, but RAF will try to grow your hair as quickly as possible, rather than with a fps interval.
I get the error Uncaught TypeError: Cannot read property 'getContext' of null and the important parts in files are... I am wondering since game.js is in a directory below, it cannot find canvas? What should I do?
./index.html:
<canvas id="canvas" width="640" height="480"></canvas>
./javascript/game.js:
var Grid = function(width, height) {
...
this.draw = function() {
var canvas = document.getElementById("canvas");
if(canvas.getContext) {
var context = canvas.getContext("2d");
for(var i = 0; i < width; i++) {
for(var j = 0; j < height; j++) {
if(isLive(i, j)) {
context.fillStyle = "lightblue";
}
else {
context.fillStyle = "yellowgreen";
}
context.fillRect(i*15, j*15, 14, 14);
}
}
}
}
}
I guess the problem is your js runs before the html is loaded.
If you are using jquery, you can use the document ready function to wrap your code:
$(function() {
var Grid = function(width, height) {
// codes...
}
});
Or simply put your js after the <canvas>.
Put your JavaScript code after your tag <canvas></canvas>
You don't have to include JQuery.
In the index.html:
<canvas id="canvas" width="640" height="480"></canvas><script src="javascript/game.js">
This should work without JQuery...
Edit: You should put the script tag IN the body tag...
You should put javascript tag in your html file.
because browser load your webpage according to html flow, you should put your javascript file<script src="javascript/game.js"> after the <canvas>element tag. otherwise,if you put your javascript in the header of html.Browser load script first but it doesn't find the canvas. So your canvas doesn't work.
Write code in this manner ...
<canvas id="canvas" width="640" height="480"></canvas>
<script>
var Grid = function(width, height) {
...
this.draw = function() {
var canvas = document.getElementById("canvas");
if(canvas.getContext) {
var context = canvas.getContext("2d");
for(var i = 0; i < width; i++) {
for(var j = 0; j < height; j++) {
if(isLive(i, j)) {
context.fillStyle = "lightblue";
}
else {
context.fillStyle = "yellowgreen";
}
context.fillRect(i*15, j*15, 14, 14);
}
}
}
}
}
First write canvas tag and then write script tag. And write script tag in body.
You just need to put<script src='./javascript/game.js'></script> after your <canvas>.
Because the browser don't find your javascript file before the canvas
I assume you have your JS file declared inside the <head> tag so it keeps it consistent, like standard, then in your JS make sure the canvas initialization is after the page is loaded:
window.onload = function () {
var myCanvas = document.getElementById('canvas');
var ctx = myCanvas.getContext('2d');
}
There is no need to use jQuery just to initialize a canvas, it's very evident most of the programmers all around the world use it unnecessarily and the accepted answer is a probe of that.
This might seem like overkill, but if in another case you were trying to load a canvas from js (like I am doing), you could use a setInterval function and an if statement to constantly check if the canvas has loaded.
//set up the interval
var thisInterval = setInterval(function{
//this if statment checks if the id "thisCanvas" is linked to something
if(document.getElementById("thisCanvas") != null){
//do what you want
//clearInterval() will remove the interval if you have given your interval a name.
clearInterval(thisInterval)
}
//the 500 means that you will loop through this every 500 milliseconds (1/2 a second)
},500)
(In this example the canvas I am trying to load has an id of "thisCanvas")
Here's my code. I created a VIBGOYR pattern on the canvas and want to change the radial gradient of the system after a time interval so that it looks animated.
function activate(index){
canvas =document.getElementById("exp");
context = canvas.getContext('2d');
//Start.
draw(index);
//Functions.
function draw(index){
drawGradiantSq(index);
var t=setTimeout(doTimer,1000);
}
function doTimer(){
draw(index+10);
}
function drawGradiantSq(fi){
console.log(fi);
var g1 = context.createRadialGradient(0,300,0,500,300,fi);
colors = ["violet","indigo","blue","green","orange","yellow","red"];
var x= 1/12;
for (var i=0;i<colors.length;i++){
g1.addColorStop(i*x,colors[i]);
}
context.fillStyle = g1;
context.fillRect(0,0,500,600);
var g2 = context.createRadialGradient(1000,300,0,500,300,fi);
for (var i=0;i<colors.length;i++){
g2.addColorStop(i*x,colors[i]);
}
context.fillStyle = g2;
context.fillRect(500,0,1000,600);
}
}
But the output i'm getting only changes the gradient only once although the timer seems to be working fine.
The HTML file:
<body>
<canvas id="exp" width="1000px" height="600px"></canvas>
<button onclick="activate(index+=10);">Paint</button>
</body>
The variable index is a global variable set to 0.
Try using setInterval instead of setTimeout.
setTimeout is only triggered once:
'setInterval' vs 'setTimeout'