I am trying to animate a square fading into a canvas element. When I use setInterval for my animation, everything works fine, but if I try to use setTimeout, everything falls apart. Here is my code:
http://jsbin.com/OyiRIVa/1/edit
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
/*class canvasUiElement*/
function canvasUiElement() {}
canvasUiElement.prototype = {
canvas: document.getElementById("canvas"),
context: canvas.getContext("2d")
}
/* ---------------------*/
function rectangle(x,y,length,width){
this.x = x;
this.y = y;
this.opacity = 0 ;
this.length = length;
this.width = width;
}
rectangle.prototype = new canvasUiElement();
rectangle.prototype.renderSelf = function(){
this.context.clearRect(this.x,this.y,this.length,this.width);
this.context.fillStyle = "rgba(0,0,255,".concat(this.opacity.toString().concat(")"));
this.context.fillRect(this.x,this.y,this.length,this.width);
}
rectangle.prototype.drawFrame = function(){
this.opacity += .01;
this.renderSelf();
x = this.drawFrame;
setTimeout(function(){x()}, 5);
}
rect = new rectangle(20,10,50,50);
rect.drawFrame();
/*window.setInterval(function() {
rect.drawFrame();
}, 1); */
The problem probably lies in the this keyword in drawFrame. When the setTimeout fires, this in side is no longer rect.
The solution is using apply or call.
f.apply(self) bind the this keyword in function f to the first argument f.
So change this way:
rectangle.prototype.drawFrame = function draw(){
var self = this;
this.opacity += 0.005;
this.renderSelf();
if (this.opacity < 1) {
requestAnimationFrame(function(){
draw.call(self)
});
}
};
check out this JSBin. http://jsbin.com/OwaHALUF/4/edit
========================
edited upon a valid comment:
x in the original code is not semantic and misses var declaration. Fixed.
prefer requestAnimationFrame to setTimeout
stop call drawFrame if opacity >= 1 (useful if requestAnimationFrame is not availlable)
prefer named function expression over re-assignment. It reduces closure overhead.(This overhead may be not neglected if the animation lasts long enough). And more concise code is bonus.
Related
Ive made a Flappy bird Clone. It works fine when i play in normal screen mode. (1920×1080). However, when i play in potrait mode, my Array is getting a 'undefined' Error.
I tought theres a problem, since i use windowHeight and windowWidth.
Iam doing it in the p5 framework.
this.hits = function (bird) {
if (bird.y < this.top || bird.y > windowHeight - this.bottom) {
if (bird.x > this.x && bird.x < this.x + this.w) {
return true;
}
}
return false;
}
Here are the used variables: (not actual code)
Bird:
bird.y = windowHeight/2;
bird.x = windowWidth*0.2;
Pipes:
var pipeSpace = random(250,600);
var centerY = random(0,windowHeight/3.5);
pipes.top = centerY - pipeSpace/2;
pipes.bottom = windowHeight - centerY - (centerY + pipeSpace/2);
pipes.x = windowWidth*1.5; (this is the x position(starting position) of the pipes)
this.w = 40; (this is the width of the pipes)
This function gets called every frame, to see if a collision is happening, but after i pass the first "pipe", the game breaks and says the error said above.
Edit: the Error: pipes[i] is undefined
this is the part where it occurs:
Theres actually more code, but it has nothing to do with my problem.
var pipes = [];
function setup() {
bird = new Bird();
pipes.push(new Pipe());
}
function draw(){
for (var i = pipes.length-1; i >= 0; i--){
if(pipes[i].hits(bird)){
gameOver = true;
}
}
}
Ok so the red blocks dangers[] should fall down smoothly down the canvas, but they skip and act weirdly. Does it have anything to do with the for loop that show(); them?
Here's the code:
var dangers = [];
function setup() {
createCanvas(1060, 480);
createDanger();
}
var x = 0;
function createDanger() {
var randomWidth = (floor(random(980)) * 80) % 980;
dangers[x] = new Danger(randomWidth, -80);
dangers.forEach(function(obj) {
setInterval(function() {
obj.fall();
}, 1000);
});
x++;
console.log(dangers);
}
function draw() {
background(0);
for (danger of dangers) {
danger.show();
}
}
setInterval(createDanger, 3000)
//Danger
function Danger (x, y) {
this.x = x;
this.y = y;
var size = 80;
this.fall = function () {
this.y += size;
}
this.update = function () {
}
this.show = function () {
fill(255,0,0);
noStroke();
rect(this.x, this.y, size, size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>
<meta charset="utf-8" />
And if you have any other suggestions to my code, feel free to help. Thanks.
Here's an example of smoother performance.
var dangers = [];
function setup() {
background(0);
createCanvas(1060, 480);
createDanger();
}
function createDanger() {
var randomWidth = (floor(random(980)) * 80) % 980;
dangers.push(new Danger(randomWidth, -80));
}
var lastRenderTime;
function update() {
var now = Date.now();
if (lastRenderTime) {
var elapsed = (now - lastRenderTime) / 1000;
dangers.forEach(function(obj) {
obj.fall(elapsed);
});
}
dangers = dangers.filter(d => d.y < 400);
lastRenderTime = now;
}
function draw() {
update();
background(0);
for (danger of dangers) {
danger.show();
}
}
setInterval(function () {
createDanger();
}, 500)
//Danger
function Danger (x, y) {
this.x = x;
this.y = y;
this.speed = 40 + random(50); // pixels per second
var size = 80;
this.fall = function (time) {
this.y += this.speed * time;
}
this.update = function () {
}
this.show = function () {
fill(255,0,0);
noStroke();
rect(this.x, this.y, size, size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>
<meta charset="utf-8" />
This is close to the original, except that I did a couple of things differently:
I got rid of the idea of setting an update function for each object when you create a new one. That just doesn't make sense.
I updated the position of each object on every frame. I did this by adding an update function that is called from the draw function. That guarantees that the animation is up to date before each frame is rendered.
I used an absolute real-time clock to adjust the amount of time that passes per frame. I keep track of this with a lastRenderTime variable and subtract now minus lastRenderTime to compute the elapsed time. The elapsed time is passed through to the update function as a parameter.
I used a more physics-based approach to updating the position of the object.
I defined the speed of each object in pixels per second. The position is updated, not by a constant amount each frame, but instead by a variable amount, according to the actual amount of time that has elapsed since the last frame of animation. This ensures that the object moves consistently in real-time regardless of the performance of the machine rendering it. So this.y += size becomes this.y += this.speed * time.
I changed the array access to use push which is a more language-agnostic description of the operation you are trying to perform rather than relying on JavaScript's quirky "extend the length of the array when you write off the end of it" behaviour.
I added a filter function to remove expired objects after they hit the bottom of the window to ensure the array doesn't grow without bound and consume resources over time.
I increased the frequency with which the objects are created from once every 3000 milliseconds to once every 500 milliseconds. Perhaps only because I'm a little impatient. :)
I also decided to choose a random speed for each object, just for a little visual variety and to make it clear that each object has its own speed.
I'm working on a small retro-style side-scrolling space shooter game (or, that's the theory anyway) and I've recently moved over to using IIFEs for managing my separate 'classes'.
However, most of the examples I've seen tend to use var when declaring variables, E.g, var x = 0. I'm wondering though, is it possible to use this.x = 0 and if so, are there any benefits or drawbacks?
I've tried googling it, and can't find much on the subject, which leads me to think it's a non-issue.
My classes are as follows;
var Player = function () {
// ------------------------------------------------------------------------------------------------
// PLAYER VARIABLES
// ------------------------------------------------------------------------------------------------
var w = 50;
var h = 50;
var x = 0;
var y = 0;
var color = 'white';
var projectiles = [];
// ------------------------------------------------------------------------------------------------
// BIND EVENTS TO THE GLOBAL CANVAS
// ------------------------------------------------------------------------------------------------
Canvas.bindEvent('mousemove', function(e){
y = (e.pageY - Canvas.element.getBoundingClientRect().top) - (h / 2);
});
Canvas.bindEvent('click', function(e){
createProjectile(50, (y + (h / 2)) - 10);
});
// ------------------------------------------------------------------------------------------------
// FUNCTIONS
// ------------------------------------------------------------------------------------------------
var createProjectile = function(x, y){
projectiles.push({
x: x,
y: y
})
};
var update = function(){
for(var p = projectiles.length - 1; p >= 0; p--){
projectiles[p].x += 10;
if(projectiles[p].x > Canvas.element.width)projectiles.splice(p, 1);
}
};
var render = function () {
Canvas.context.fillStyle = color;
Canvas.context.fillRect(x, y, w, h);
console.log(projectiles.length);
for(var p = 0; p < projectiles.length; p++){
Canvas.context.fillStyle = 'red';
Canvas.context.fillRect(projectiles[p].x, projectiles[p].y, 20, 20);
}
};
// ------------------------------------------------------------------------------------------------
// Exposed Variables and Functions
// ------------------------------------------------------------------------------------------------
return{
update: update,
render: render
}
}();
are there any benefits or drawbacks?
The drawbacks are that in strict mode, you will get a runtime error (because this is undefined).
In non-strict mode, this will refer to window, so this.x = ... creates a global variable (which is what you want to avoid with the IIFE in the first place I guess).
There are no benefits.
Trying to animate dots(imageData 1x1) on a javascript canvas to make a starfield.
The strange thing is when those dots move at a speed higher than 1, there is like a flickering or anything else showing not a dot but a line.
here is a fiddle to show the strangeness: http://jsfiddle.net/xp6xd8q1/1/
function clearCanvas() {
ctx.fillStyle = '#000000';
ctx.fillRect(0,0,w,h);
}
function stars() {
this.manyStars = [];
this.addStars = function(nb) {
var i,x,y;
for(i=0;i<nb;i++) {
x = Math.floor(Math.random() * w);
y = Math.floor(Math.random() * h);
this.manyStars.push({x: x,y: y,s: 5}); // dot speed is s
}
}
this.move = function() {
var i,l;
for(i=0,l = this.manyStars.length;i<l;i++) {
this.manyStars[i].x = this.manyStars[i].x - this.manyStars[i].s;
if(this.manyStars[i].x < 0) {
this.manyStars[i].x = w + this.manyStars[i].x;
}
}
}
this.drawStars = function() {
var i,l;
for(i=0,l = this.manyStars.length;i<l;i++) {
ctx.putImageData(dot,this.manyStars[i].x,this.manyStars[i].y);
}
}
}
function run() {
clearCanvas();
s.move();
s.drawStars();
window.requestAnimationFrame(run);
}
//
window.requestAnimationFrame = window.requestAnimationFrame||window.mozRequestAnimationFrame ||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame;
var cv = document.createElement('canvas');
var w = window.innerWidth, h = window.innerHeight;
cv.width = w;
cv.height = h;
var ctx = cv.getContext('2d');
document.body.appendChild(cv);
//
var dot = ctx.createImageData(1,1);
dot.data = [255,255,255,255];
s = new stars();
s.addStars(10);
window.requestAnimationFrame(run);
Any idea on this is very welcomed !
I see it too. The dots appear to be stretched when they move. I screenshotted the canvas at several speeds. The dots really are just 1x1 pixel.
I believe you may be experiencing Display Motion Blur. It's a result of how displays work, and also because the vision cells in your eye take a bit of time to readjust when light changes.
There's not really much you can do about that, except try to hide it. It becomes less and less apparent the larger your moving objects are, and the slower they move.
It also becomes less apparent when the display refresh rate increases. See this example. Since you can't control the users' monitor's refresh rates, this doesn't really help you.
I've seen a couple answers on how to delay the animation in CSS, but I was hoping to do it in Javascript.
I have an animation that activates as soon as the page loads. The website is a single page and I want the animation to activate when clients scroll to that section. I heard that you could do this by activating the animation X pixels from the bottom of the viewport.
Thanks
Here is a little overkill but it should give you all the concepts you could want for what you're trying to achieve.
Basically, you test that the point you want to start your animation from is inside the rectangle of the screen, and use a flag to prevent the animation repeating itself for each scroll after you're in the region.
// teach JavaScript about coördinates
function CoOrdinate(x, y) {
this.x = x;
this.y = y;
}
CoOrdinate.prototype.inside = function (A, D) {
if (this.x < A.x || D.x < this.x) return false;
if (this.y < A.y || D.y < this.y) return false;
return true;
}
// write a factory for the flag + action test
function actionFactory(hotspot, action) {
var flag_done = false,
test = function (topleft, bottomright) {
if (flag_done) return; // cancel if we've done it before
if (hotspot.inside(topleft, bottomright)) {
flag_done = true;
action();
}
};
return test;
}
// create your action with the factory
var someAction = actionFactory(
new CoOrdinate(0, 100), // the coördinate to be in the screen
function () {console.log('foo')} // pass animation invocation here
);
// so far none of this is getting invoked, so
// make the stuff all work when the page is scrolled
window.addEventListener('scroll', function () {
var x = window.pageXOffset,
y = window.pageYOffset,
h = window.screen.availHeight,
w = window.screen.availWidth;
someAction(
new CoOrdinate(x , y ),
new CoOrdinate(x + w, y + h)
);
});
The above example just logs "foo" if the coördinate [0, 100] is in the screen.