I wrote a quick program to bounce a ball on the screen. Everything works, but the image is prone to flickering and is not smooth.
I suspect that the image flickers because the velocity is significant at the bottom of the screen.
I was wondering if anybody had any ideas on how to interpolate the motion of the ball to reduce the flicker.
Called to update the position
this.step = function()
{
thegame.myface.y = thegame.myface.y + thegame.myface.vSpeed;
if (thegame.myface.y > thegame.height)
{
thegame.myface.vSpeed = -thegame.myface.vSpeed;
}
else
{
thegame.myface.vSpeed = thegame.myface.vSpeed + 1;
}
}
},
Called to redraw
draw: function()
{
//clears the canvas
thegame.ctx.clearRect(0,0,thegame.width,thegame.height);
//draw the objects
thegame.ctx.drawImage(thegame.imgFace,this.x-this.width/2,this.y-this.height/2);
return;
},
Call framework in index.html
<script type="text/javascript">
thegame.init(450,450);
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
(function()
{
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x)
{
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelRequestAnimationFrame = window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
{
var f = function(callback, element)
{
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16-(currTime-lastTime));
var id = window.setTimeout(function()
{
callback(currTime+timeToCall);
}, timeToCall);
lastTime = currTime+timeToCall;
return id;
};
window.requestAnimationFrame = f;
}
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id)
{
clearTimeout(id);
};
}());
(function gameloop()
{
thegame.update();
requestAnimationFrame(gameloop);
thegame.draw();
})();
</script>
edit definition for thegame
init: function(width, height)
{
var canvas = $("<canvas width='"+width+"' height='"+height+"'></canvas>");
canvas.appendTo("body");
thegame.ctx = canvas.get(0).getContext("2d");
thegame.width = width;
thegame.height = height;
thegame.imgFace = new Image();
thegame.imgFace.src = "face.png";
thegame.myface = new thegame.makeFace(width/2,height/2);
},
It's about visual perception. First find out at what rate the game loop is called by the browser via requestanimationframe. If it's Chrome its task manager will help. If not, calc the rate by yourself with timestamps. It should be at least 60 times per second. If the browser is running at this rate and the movement is still not smooth, the speed is simply too high for that rate.
However, you have options to trick the perception of motion. One is to make the image smaller (simple), another is motion blur (complicated). To do the latter, you basically run the game hidden at double speed and draw two blended frames onto the visible canvas. Or at same speed and a bit simpler, keep track of the last two images and draw both with 50% alpha at the canvas. If you want more background info follow the discussion why latest hobbit movie was filmed at 48 instead of the usual 24 frames per second.
If it appears the image is horizontally chopped/cut in half then the browser is not properly synchronized to the monitor. In this case make sure the vertical sync (vsync) isn't overridden somewhere by the system or display options.
How large is your canvas? Not sure if it will solve your flicker issue, but an additional optimization you can try is to modify your clearRect() call to only clear the region which is dirty, before each update.
Ex: thegame.ctx.clearRect(this.x-this.width/2, this.y-this.height/2, thegame.imgFace.width, thegame.imgFace.height);
Related
Here's the site in question: https://marks-groovy-project-2fa056.webflow.io/
I want to rotate each letter by 90 degrees on their Y axis while scroll is active, and return them back to their original position as soon as scrolling stops.
Visualization: in top view, a letter should rotate 90deg counter-clockwise when scroll is activated, which will render the letter invisible in front view ( the viewport) while scrolling, and then turn back 90 degrees clockwise when scrolling ends, so that each letter is visible again.
Method: used skew.js and slightly modified it:
skew.js is applied to an entire section. I want to apply it to every instance of a span with id="letter-animation". I've appropriately renamed the constant and referenced the #.
const speed is a remnant from skew.js. I haven't yet figured out how to rewrite it. It expresses the amount of skew as a function of the difference in newPixel/oldPixel. Which I don't want. My rotation needs to be 90deg every time. Once in, once out.
letter.style.transform = "rotateY(45deg)" used to be "rotateY(" + speed + "deg)" in the old script. (technically it was "skewY", not "rotateY" but you get my point). const speed would then be replaced with whatever new constant is appropriate as mentioned in point 2.
I've set up this codepen to isolate the script in question. https://codepen.io/mhedinger/pen/yLJaLmp
const letter = document.querySelector("#letter-animation")
let currentPixel = window.pageYOffset
const looper = function(){
const newPixel = window.pageYOffset
const diff = newPixel - currentPixel
const speed = diff * 11
letter.style.transform = "rotateY(45deg)"
currentPixel = newPixel
requestAnimationFrame(looper)
}
looper()
Here's the tutorial that explains how skew.js works: https://www.superhi.com/video/skew-on-scroll-effect
And here's an example of it working, according to the video tutorial above: https://codepen.io/emgiust/pen/rdOJwQ
const section = document.querySelector("section");
let currentPixel = window.pageYOffset
//looper keeps running and keeps track of where the new pixel is
const looper = function () {
const newPixel = window.pageYOffset;
const diff = newPixel - currentPixel
const speed = diff * 0.35;
section.style.transform = "skewY(" + speed + "deg)"
currentPixel = newPixel;
requestAnimationFrame(looper)
}
looper();
Can anybody help me get this working? So far, the main issue seems to be that they're spans and not sections, but it could also just be my complete absence of understanding of javascript.
I'm hoping to control the transition speed, curve, and delay via css, but if it has to be controlled in JS, i'd also appreciate some advice on how to do this.
Thank you everyone in advance for trying to help.
Cheers,
Mark
NOTE: there is another script running (fullpage.js) which simulates a swiping experience during scroll. deactivate it temporarily to get a better view of what’s happening during scroll while you’re checking things out/setting things up. End-product, a letter animation that synchronously rotates each letter by 90 degrees (rendering the words invisible) for the duration of the swipe/scroll, and returning them to normal once the section snaps into place.
In case anybody is still interested in this, I have figured it out in the meantime.
const letter = document.getElementsByTagName("SPAN");
var timer = null;
window.addEventListener('scroll', function() {
if(timer !== null) {clearTimeout(timer)}
timer = setTimeout(function() {
var i;
for (i = 0; i < letter.length; i++) {
letter[i].style.transform = "rotateY(0deg)";
};
}, 150);
var i;
for (i = 0; i < letter.length; i++) {
letter[i].style.transform = "rotateY(90deg)";
};
}, false);
https://codepen.io/mhedinger/pen/yLJaLmp
EDIT:
I have improved this script for best-practice purposes. This makes it more stable and versatile because it can now be used together with plugins like fullpage.js. Additionally, if desired, the transforms can now also be called manually by referencing the appropriate function.
let letter = document.getElementsByTagName("SPAN");
var timer = null;
var rotateLetter = function() {
Array.from(letter).forEach(function(letter) {
letter.style.transform = "rotateY(90deg)";
});
};
var resetLetter = function() {
Array.from(letter).forEach(function(letter) {
letter.style.transform = "rotateY(0deg)";
});
};
window.addEventListener('scroll', function() {
if(timer !== null) {clearTimeout(timer)}
timer = setTimeout(function() {resetLetter()}, 125);
rotateLetter()
}, false);
https://codepen.io/mhedinger/pen/dypmKZd
I have a problem in mobile browsers.
I maked a canvas chart without any js library or frameworks, but its animations is very very slow in mobile
I use this function for requestAnimationFrame:
(function () {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
},
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}());
requestAnimationFrame(myFunc);
function myFunc(){
//any code
.
.
requestAnimationFrame(myFunc);
})
What should I do?
First off, you didn't post the relevant code. How you are drawing to the canvas is almost certainly your problem. There are a few tips for making that better:
Draw to an offscreen canvas and copy the image data over all at once.
Combine the above with multiple canvases, only draw layers that have changed e.g. your graph layer may have changed but not the labels/legend.
Draw to a smaller canvas and scale it up.
To be honest though, mobile browsers are way, way slower than their desktop counterparts for a variety of reasons (heat and power consumption being two rather large ones). If you want smooth animations you will have to either write some high-performance rendering code (likely in WebGL) or use someone elses (e.g. pixi.js).
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.
My newest Hobby Project is a very simple Jump'n'Run Game using JavaScript. I already wrote some code (with the help of a tutorial at lostdecadegames) and read everything about the GameLoop.
var start = true;
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 1200;
canvas.height = 480;
document.body.appendChild(canvas);
var jumping = false;
var gravity = 1.5;
var pressed = true;
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "background.png";
// Hero image
var heroReady = false;
var heroImage = new Image();
heroImage.onload = function () {
heroReady = true;
};
heroImage.src = "hero.png";
// Monster image
var monsterReady = false;
var monsterImage = new Image();
monsterImage.onload = function () {
monsterReady = true;
};
monsterImage.src = "monster.png";
// Game objects
var hero = {
speed_x: 50,
speed_y_up: 50,
speed_y_down: 50, // movement in pixels per second
velocity_x: 50,
velocity_y: 50
};
// Handle keyboard controls
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
// Update game objects
var update = function (modifier) {
if(38 in keysDown) { // Player holding up
jumping = true;
//hero.y -= hero.speed_y_up * modifier;
}
if (40 in keysDown) { // Player holding down
hero.y += hero.speed_y_down * modifier;
}
if (37 in keysDown) { // Player holding left
hero.x -= hero.speed_x * modifier;
}
if (39 in keysDown) { // Player holding right
hero.x += hero.speed_x * modifier;
}
};
// Draw everything
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
if (heroReady) {
if(hero.y > 0 && hero.y < 480 && hero.x <= -32)
{
hero.x = hero.x + 1232;
ctx.drawImage(heroImage, hero.x, hero.y);
}
else if(hero.y > 0 && hero.y < 480 && hero.x >= 1200)
{
hero.x = hero.x - 1232;
ctx.drawImage(heroImage, hero.x, hero.y);
}
else if(jumping)
{
ctx.drawImage(heroImage, hero.x, hero.y-100);
jumping = false;
}
else ctx.drawImage(heroImage, hero.x, hero.y);
}
if (monsterReady) {
ctx.drawImage(monsterImage, monster.x, monster.y);
}
};
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - then;
update(delta / 500);
render();
then = now;
};
// Starting the game!
reset();
var then = Date.now();
setInterval(main, 1); // Execute as fast as possible
As you can see, I already added a fix gravity var and some speed vars. The Hero moves very smooth, so this is no problem.
I have 2 problems with the jump-Animation:
The Hero stays in the air, when the Up-Key is keep being pressed. I tried to fix this with some boolean vars, but I couldn't figure it out how to get the Hero down again.
Right now, I implemented a "dirty hack" which causes the Hero to be repainted 50px higher, but I want a smooth Jump, so that the Hero gets slower while going up and speeds up while falling. I looked up so many Tutorials and so much Example Code, but I'm too stupid to figure it out, how I get my desired Animation.
Hope you guys can give me some advice for my problem (I'm not asking for the final code, I just need some tips).
It's hard to understand exactly what the if statements inside of if (heroReady) are doing because the numbers don't mean anything to me, but it seems to me like your problem is in there.
First of all, it seems to me like jumping should the first condition checked. If one of the first conditions is true, then it doesn't matter whether or not he's jumping. I can't easily tell when each condition is true, though, so I'm going to assume that when the player is holding up,
else if(jumping)
{
ctx.drawImage(heroImage, hero.x, hero.y-100);
jumping = false;
}
gets executed like normal.
Now, assuming that, I think your issue is that jumping is determined solely by whether or not the player is holding up, because as soon as jumping is true, it becomes false. This is incorrect.
Jumping should be set to true when the player presses the up key, but it should be set to false when they remove it. It should be set to false when the animation hits the ground.
Another issue that you have is the fact that you aren't actually using the hero's attributes to render its jumping location, you're simply offsetting it. Perhaps that is just your workaround until the problem is solved, but it makes it hard to tell when the character hits the ground, because you can't start lower the character (increasing the y value) after they jump, since you never raised them by decreasing the y value.
So how do we fix this?
Here are my recommendations. You might find more elegant ways to do it by the time you're done due to refactoring, but the way you have it set up right now I think it will work fine:
Set jumping as soon as they press up, like you're doing, but only if jumping == false, because presumably your hero can't do mid-air jumps.
Immediately after you set jumping (and inside the same if statement), update their velocity.
In your update section, add another if for whether or not the player is jumping, regardless of whether or not they are pressing any keys. If they are, decrease their momentum based on gravity. Then, add a check for if their momentum is the opposite of how much you increase it when they start jumping. In other words, check if they are moving down at exactly the same rate they were moving up when they started jump. This happens at exactly the y-coordinate that they began the jump from. (This is more reliable that just checking their position, because it will work from multiple y-locations.) The alternative would be to store a variable with the y-coordinate they were at when they jumped. Either way, if their jump has ended, set jumping to false.
Since you're updating their coordinates based on jumping, in your render function, you can eliminate any jumping logic and just draw the image based on the coordinates.
Does that help at all?
I've got an animation on my canvas where I have some images (drawn using drawImage()). For the sake of simplicity, let's say there's only two images. These images are following a circular path in faux-3d space such that sometimes one image overlaps the other and other times the second image overlaps the first. These images are also scaled as they move "closer" or "further" from the viewer.
Each of these images is an object with the following (simplified) code:
function slide_object() {
this.x = 0.0; // x = position along path (in radians)
this.xpos = 0.0; // x position on canvas
this.ypos = 0.0; // y position on canvas
this.scale = 0.0;
this.name = ""; // a string to be displayed when slide is moused over
this.imgx = 0.0; // width of original image
this.imgy = 0.0; // height of original image
this.init = function (abspath, startx, name) { // startx = path offset (in radians)
this.name = name;
this.x = (startx % (Math.PI * 2));
var slide_image = new Image();
slide_image.src = abspath;
this.img = slide_image;
calcObjPositions(0, this); // calculate's the image's position, then draws it
};
this.getDims = function () { // called by calcObjPositions when animation is started
this.imgx = this.img.width / 2;
this.imgy = this.img.height / 2;
}
}
Each of these objects is stored in an array called slides.
In order to overlap the images appropriately, the drawObjs function first sorts the slides array in order of slides[i].scale from smallest to largest, then draws the images starting with slides[0].
On $(document).ready() I run an init function that, among other things, adds an event listener to the canvas:
canvas = document.getElementById(canvas_id);
canvas.addEventListener('mousemove', mouse_handler, false);
The purpose of this handler is to determine where the mouse is and whether the mouse is over one of the slides (which will modify a <div> on the page via jQuery).
Here's my problem -- I'm trying to figure out how to determine which slide the mouse is over at any given time. Essentially, I need code to fill in the following logic (most likely in the mouse_handler() function):
// if (mouse is over a slide) {
// slideName = get .name of moused-over slide;
// } else {
// slideName = "";
// }
// $("#slideName").html(slideName);
Any thoughts?
Looks like I just needed some sleep before I could figure this one out.
I've got everything I need to determine the size of the image and it's placement on the canvas.
I added the following function to my slide_object:
this.getBounds = function () {
var bounds = new Array();
bounds[0] = Math.ceil(this.xpos); // xmin
bounds[1] = bounds[0] + Math.ceil(this.imgx * this.scale); // xmax
bounds[2] = Math.ceil(this.ypos); // ymin
bounds[3] = bounds[2] + Math.ceil(this.imgy * this.scale); // ymax
return bounds;
};
Then in my mouse_handler function, I get the index of the currently moused-over slide from a function I call isWithinBounds() which takes a mouse x and mouse y position:
function isWithinBounds(mx,my) {
var index = -1;
for ( var i in slides ) {
bounds = slides[i].getBounds();
if ((mx >= bounds[0] && mx <= bounds[1]) && (my >= bounds[2] && my <= bounds[3])) {
index = i;
}
}
return index;
}
Since slides is sorted in order of scale, smallest to largest, each iteration will either replace or preserve the value of index. If there's more than one image occupying a space, the top-most slide's index gets returned.
The only problem now is to figure out how to make the code more efficient. Chrome runs the animation without any lag. Firefox has some and I haven't even thought about implementing excanvas.js yet for IE users. For browsers lacking canvas support, the canvas object is simply hidden with display:none.