I am trying to change the position of a circle inside a canvas using random x and y when hitting the space bar but i am stuck to figure out how I should implement a gravity effect so when the circle change it's position it come back down to the ground in a smoothy way
my jsfiddle :http://jsfiddle.net/seekpunk/efcnM/5/
how can i modify my update function to succeed my gravity effect
function update() {
$(window).keydown(function (e) {
var spacebarHit = e.which == 32 || e.keyCode == 32 ? true : false;
if (spacebarHit) {
Bluecircle.y -=1;// RandomPos;
Draw();
}
});
}
Why not use the real-world equations of motion?
if (spacebarHit) {
var oldPos = Bluecircle.y;
var u = 50;
var g = 9.81;
var t = 0;
var handle = setInterval(function () {
t++;
Bluecircle.y = oldPos-(((u)*t-(g/2)*t*t));
console.log(Bluecircle.y);
if (Bluecircle.y>oldPos) {
Bluecircle.y = oldPos;
clearInterval(handle);
}
}, 100);
Draw();
}
DEMO
Use Newtonian integration:
function mainLoop(dt) {
circle.acc.x = 0
circle.acc.y = 9.81 // positive is down
circle.vel.x += circle.acc.x * t
circle.vel.y += circle.acc.y * t
circle.pos.x += circle.vel.x * t
circle.pos.y += circle.vel.y * t
// check if the bottom of the screen has been hit, and
// clamp circle.poa to he within the world
// do bounces by flipping the sign of components of circle.vel
}
function jump() {
circle.vy = -20; // or something
}
Related
This question already has answers here:
move element with keypress (multiple)
(3 answers)
Closed 3 years ago.
I made a pretty basic demo, you press an arrow key and the square moves in that direction. One problem: when I first press the key, the square moves a little, pauses, then continues moving. How do I get rid of or work around the pause?
Code:
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext('2d');
var p1 = document.getElementById("p1");
var keys = [];
var x = 25
var y = 25
document.addEventListener("keydown", function(e) {
keys[e.keyCode] = true;
update();
});
document.addEventListener("keyup", function(e) {
keys[e.keyCode] = false;
update();
});
function update() {
ctx.clearRect(0, 0, 400, 400)
if(keys[40] == true) {
y += 5
}
if(keys[38] == true) {
y -= 5
}
if(keys[39] == true) {
x += 5
}
if(keys[37] == true) {
x -= 5
}
ctx.fillRect(x, y, 100, 100)
console.log(keys);
p1.innerText = "";
for (i = 0; i < keys.length; i++) {
if (keys[i]) {
p1.innerText += i + " | ";
}
}
}
<canvas id='canvas' width='400' height='400'></canvas>
<p id='p1'>testing</p>
This happens because the keydown event is continuously fired at different intervals in different browsers.
Instead of relying on the browser to send keydown from time to time, it's better to have your own update loop running at 60fps, using requestAnimationFrame and each frame moving the box based on the keys pressed.
var canvas = document.getElementById("canvas")
var ctx = canvas.getContext('2d');
var p1 = document.getElementById("p1");
var keys = [];
var x = 25
var y = 25
document.addEventListener("keydown", function(e) {
e.preventDefault(); // make sure this doesn't scroll the window
keys[e.keyCode] = true;
});
document.addEventListener("keyup", function(e) {
keys[e.keyCode] = false;
});
function update() {
// Tell the browser to run again update when it is "free",
// preferably at 60fps (actually your monitor's refresh rate)
requestAnimationFrame(update);
ctx.clearRect(0, 0, 400, 400)
if(keys[40] == true) {
y += 5
}
if(keys[38] == true) {
y -= 5
}
if(keys[39] == true) {
x += 5
}
if(keys[37] == true) {
x -= 5
}
ctx.fillRect(x, y, 100, 100)
p1.innerText = "";
for (i = 0; i < keys.length; i++) {
if (keys[i]) {
p1.innerText += i + " | ";
}
}
}
update(); // Start running the loop at 60fps
<canvas id='canvas' width='400' height='400'></canvas>
<p id='p1'>testing</p>
Note that the distance the box moves in a given amount of time depends on your framerate, so if the browser can't keep up at 60fps and only runs at 30fps, update will be only called half of the times, so the box will only move half of the distance it would otherwise move at 60fps. To learn more about animations and game update loops I recommend to read this fix your timestep article.
I'm making a game, and I'd like to know how to make a character move more smoothly. The character can already move, but it moves really choppy; when you click the arrow key, it instantly appears 10 pixels ahead. I'd like it to move smoothly so it doesn't just "appear" 10 pixels ahead of itself.
Here is the Code:
document.onkeydown = checkKey;
var canvas;
var ctx;
var up;
var down;
var left;
var right;
var bobX = 200;
var bobY = 200;
var bobWidth = 30;
var bobHeight = 30;
window.onload = function() {
canvas = document.getElementById("gameCanvas");
ctx = canvas.getContext("2d");
var fps = 200; // frames per second
setInterval(function() {
updateAll();
drawAll();
}, 1000/fps)
};
var drawAll = function() {
// draw background
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// draw bob
ctx.fillStyle = "red";
ctx.fillRect(bobX, bobY, bobWidth, bobHeight);
};
var updateAll = function() {
if (up == true) {
up = false;
}
if (down == true) {
bobY += 1;
down = false;
}
if (left == true) {
bobX -= 1;
left = false;
}
if (right == true) {
bobX += 1;
right = false;
}
};
function checkKey(e) {
e = e || window.event;
if (e.keyCode == '38') {
up = true;
}
else if (e.keyCode == '40') {
down = true;
}
else if (e.keyCode == '37') {
left = true;
}
else if (e.keyCode == '39') {
right = true;
}
}
I tried doing moving it by one pixel every keypress, but it moves very slowly when I do that.
Your screen has maximum refreshrate, usually 60 fps. Some screens can get up to 120fps, but that's a rather rare case.
So what is happening here:
var fps = 200; // frames per second
setInterval(function() {
updateAll();
drawAll();
}, 1000/fps)
};
The canvas gets redrawn and the position gets updated at a rate which your screen can't catch up with. You simply can't see that your character only moves 1 pixel instead of 10 pixel.
Solution would be to use requestAnimationFrame instead. Which invokes when the screen refreshes:
function animate() {
requestAnimationFrame(animate);
updateAll();
drawAll();
};
animate();
I am currently developing a Javascript game (almost everything is based on a tutorial yet, so I am not worried of sharing the code).
The problem is, I can't get the character to jump after pressing the Space button. Please, can someone look at the code and help me?
// EDIT: Sorry for lack of information I provided. The thing is - code is written, the game is in the state, that the character is animated (=is running) and the backgrounds are moving. Yesterday, I tried to implement some basic controls, such as jump by pressing spacebar. The thing is, the player won't jump at all, and browser console is not giving me any error statements.
Character is defined as Player on line 5. and 321. in the code provided below.
The jumping is defined in the following examples:
Pressing the Space button
var KEY_CODES = {
32: 'space'
};
var KEY_STATUS = {};
for (var code in KEY_CODES) {
if (KEY_CODES.hasOwnProperty(code)) {
KEY_STATUS[KEY_CODES[code]] = false;
}
}
document.onkeydown = function(e) {
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if (KEY_CODES[keyCode]) {
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = true;
}
};
document.onkeyup = function(e) {
var keyCode = (e.keyCode) ? e.keyCode : e.charCode;
if (KEY_CODES[keyCode]) {
e.preventDefault();
KEY_STATUS[KEY_CODES[keyCode]] = false;
}
};
Other jump information (please, read the comments in the code)
this.update = function() {
// jump, if the characted is NOT currently jumping or falling
if (KEY_STATUS.space && this.dy === 0 && !this.isJumping) {
this.isJumping = true;
this.dy = this.jumpDy;
jumpCounter = 12;
assetLoader.sounds.jump.play();
}
// longer jump if the space bar is pressed down longer
if (KEY_STATUS.space && jumpCounter) {
this.dy = this.jumpDy;
}
jumpCounter = Math.max(jumpCounter-1, 0);
this.advance();
// gravity
if (this.isFalling || this.isJumping) {
this.dy += this.gravity;
}
// change animation is-falling
if (this.dy > 0) {
this.anim = this.fallAnim;
}
// change animation is-jumping
else if (this.dy < 0) {
this.anim = this.jumpAnim;
}
else {
this.anim = this.walkAnim;
}
this.anim.update();
};
/**
* Update the Sprite's position by the player's speed
*/
this.update = function() {
this.dx = -player.speed;
this.advance();
};
/**
* Draw the current player's frame
*/
this.draw = function() {
this.anim.draw(this.x, this.y);
};
}
Player.prototype = Object.create(Vector.prototype);
Everything seems just fine to me, but the player just won't move. :(
Any help?
If you are curious about the full code, go here: http://pastebin.com/DHZKhBMT
EDIT2:
Thank you very much for your replies so far.
I have moved the RequestAnimFrame to the end of the function - will keep that in mind, thanks.
I have also implemented the simple jumping script Ashish provided above, but the character is still not jumping.
This is what it looks like now:
/** JUMP KEYS DEFINITION **/
$(document).keypress(function(e){
if(e.which==32){
$('Player.prototype').css({'top':"0px"});
}
setTimeout(function(){
$('Player.prototype').css({'top':"200px"});
},350);
});
/** DEFINING CHARACTER **/
function Player(x, y) {
this.dy = 0;
this.gravity = 1;
this.speed = 6;
this.jumpDy = -10;
this.isJumping = false;
this.width = 60;
this.height = 96;
this.sheet = new SpriteSheet('imgs/normal_walk.png', this.width, this.height);
this.walkAnim = new Animation(this.sheet, 4, 0, 11);
this.jumpAnim = new Animation(this.sheet, 4, 3, 3);
this.fallAnim = new Animation(this.sheet, 4, 3, 3);
this.anim = this.walkAnim;
Vector.call(this, x, y, 0, this.dy);
var jumpCounter = 0; // Maximalna dlzka drzania tlacidla skakania
}
Player.prototype = Object.create(Vector.prototype);
Where am I wrong?
I've tried in http://jsfiddle.net/Ykge9/1/
and you have an infinite loop in animate, the requestAnimFrame should be at the end of the function:
/**
* Loop cykly hry
*/
function animate() {
background.draw();
for (i = 0; i < ground.length; i++) {
ground[i].x -= player.speed;
ctx.drawImage(assetLoader.imgs.grass, ground[i].x, ground[i].y+250);
}
if (ground[0].x <= -platformWidth) {
ground.shift();
ground.push({'x': ground[ground.length-1].x + platformWidth, 'y': platformHeight});
}
player.anim.update();
player.anim.draw(64, 260);
requestAnimFrame( animate );
}
I have created fixed city having few path.
On that I want to make specific path where I can move characters from start to and point.
Here is the canvas map
When I tried this http://jsfiddle.net/nathggns/HG752/light/ example code move on map Iy just show white background instead of canvas city map.
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
// Grab our context
var context = canvas.getContext('2d');
// Make sure we have a valid defintion of requestAnimationFrame
var requestAnimationFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function(callback) {
return setTimeout(callback, 16);
};
// Let's define our square
var square = {
'x': 50,
'y': 50,
'width': 10,
'height': 10,
'fill': '#000000'
};
var render = function() {
// Clear the canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// Draw the square
context.beginPath();
context.rect(square.x, square.y, square.width, square.height);
context.fillStyle = square.fill;
context.fill();
// Redraw
requestAnimationFrame(render);
};
// Start the redrawing process
render();
var animate = function(prop, val, duration) {
// The calculations required for the step function
var start = new Date().getTime();
var end = start + duration;
var current = square[prop];
var distance = val - current;
var step = function() {
// Get our current progres
var timestamp = new Date().getTime();
var progress = Math.min((duration - (end - timestamp)) / duration, 1);
// Update the square's property
square[prop] = current + (distance * progress);
// If the animation hasn't finished, repeat the step.
if (progress < 1) requestAnimationFrame(step);
};
// Start the animation
return step();
};
animate('x', 0, 1000);
setTimeout(function() {
animate('y', 0, 1000);
setTimeout(function() {
animate('x', 50, 1000);
animate('y', 50, 1000);
}, 1000);
}, 1000);
var meta = function(e) {
// Set some initial variables
var distance = 100;
var prop = 'x';
var mult = 1;
// Just return false if the key isn't an arrow key
if (e.which < 37 || e.which > 40) {
return false;
};
// If we're going left or up, we want to set the multiplier to -1
if (e.which === 37 || e.which === 38) {
mult = -1;
}
// If we're going up or down, we want to change the property we will be animating.
if (e.which === 38 || e.which === 40) {
prop = 'y';
};
return [prop, mult * distance];
};
document.body.addEventListener('keydown', function(e) {
var info = meta(e);
if (info) {
e.preventDefault();
animate(info[0], square[info[0]] + info[1], 1000);
};
});
document.body.addEventListener('keyup', function(e) {
var info = meta(e);
if (info) {
e.preventDefault();
animate(info[0], square[info[0]], 1000);
};
});
};
Thanks in advance !
i will not give you full code, but will show you the way. Do not forget to use several layers. It is 100x faster. http://jsfiddle.net/HG752/7/
Also think about having digital smarter version of map, that you can check on every move. Like matrix where 1 block is 10x10 pixels having true/false. Also on redraw do just minimal things. For example calculating var img_back and var imgData on each redraw is big mistake. But this is just example :)
var canvas = document.getElementById('canvas');
var canvas_back = document.getElementById('canvas_back');
...
var img_back = canvas_back.getContext('2d').getImageData(0, 0, W, H);
var imgData = img_back.data;
var x = (Math.round(square.x) + Math.round(square.y)*W) * 4;
//get background color where i am
document.getElementById('log').innerHTML = imgData[x]+'+'+imgData[x+1]+'+'+imgData[x+2];
We're working with the HTML5 canvas, displaying lots of images at one time.
This is working pretty well but recently we've had a problem with chrome.
When drawing images on to a canvas you seem to reach a certain point where the performance degrades very quickly.
It's not a slow effect, it seems that you go right from 60fps to 2-4fps.
Here's some reproduction code:
// Helpers
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random
function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
// http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })();
// https://github.com/mrdoob/stats.js
var Stats = function () { var e = Date.now(), t = e; var n = 0, r = Infinity, i = 0; var s = 0, o = Infinity, u = 0; var a = 0, f = 0; var l = document.createElement("div"); l.id = "stats"; l.addEventListener("mousedown", function (e) { e.preventDefault(); y(++f % 2) }, false); l.style.cssText = "width:80px;opacity:0.9;cursor:pointer"; var c = document.createElement("div"); c.id = "fps"; c.style.cssText = "padding:0 0 3px 3px;text-align:left;background-color:#002"; l.appendChild(c); var h = document.createElement("div"); h.id = "fpsText"; h.style.cssText = "color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; h.innerHTML = "FPS"; c.appendChild(h); var p = document.createElement("div"); p.id = "fpsGraph"; p.style.cssText = "position:relative;width:74px;height:30px;background-color:#0ff"; c.appendChild(p); while (p.children.length < 74) { var d = document.createElement("span"); d.style.cssText = "width:1px;height:30px;float:left;background-color:#113"; p.appendChild(d) } var v = document.createElement("div"); v.id = "ms"; v.style.cssText = "padding:0 0 3px 3px;text-align:left;background-color:#020;display:none"; l.appendChild(v); var m = document.createElement("div"); m.id = "msText"; m.style.cssText = "color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; m.innerHTML = "MS"; v.appendChild(m); var g = document.createElement("div"); g.id = "msGraph"; g.style.cssText = "position:relative;width:74px;height:30px;background-color:#0f0"; v.appendChild(g); while (g.children.length < 74) { var d = document.createElement("span"); d.style.cssText = "width:1px;height:30px;float:left;background-color:#131"; g.appendChild(d) } var y = function (e) { f = e; switch (f) { case 0: c.style.display = "block"; v.style.display = "none"; break; case 1: c.style.display = "none"; v.style.display = "block"; break } }; var b = function (e, t) { var n = e.appendChild(e.firstChild); n.style.height = t + "px" }; return { REVISION: 11, domElement: l, setMode: y, begin: function () { e = Date.now() }, end: function () { var f = Date.now(); n = f - e; r = Math.min(r, n); i = Math.max(i, n); m.textContent = n + " MS (" + r + "-" + i + ")"; b(g, Math.min(30, 30 - n / 200 * 30)); a++; if (f > t + 1e3) { s = Math.round(a * 1e3 / (f - t)); o = Math.min(o, s); u = Math.max(u, s); h.textContent = s + " FPS (" + o + "-" + u + ")"; b(p, Math.min(30, 30 - s / 100 * 30)); t = f; a = 0 } return f }, update: function () { e = this.end() } } }
// Firefox events suck
function getOffsetXY(eventArgs) { return { X: eventArgs.offsetX == undefined ? eventArgs.layerX : eventArgs.offsetX, Y: eventArgs.offsetY == undefined ? eventArgs.layerY : eventArgs.offsetY }; }
function getWheelDelta(eventArgs) { if (!eventArgs) eventArgs = event; var w = eventArgs.wheelDelta; var d = eventArgs.detail; if (d) { if (w) { return w / d / 40 * d > 0 ? 1 : -1; } else { return -d / 3; } } else { return w / 120; } }
// Reproduction Code
var stats = new Stats();
document.body.appendChild(stats.domElement);
var masterCanvas = document.getElementById('canvas');
var masterContext = masterCanvas.getContext('2d');
var viewOffsetX = 0;
var viewOffsetY = 0;
var viewScaleFactor = 1;
var viewMinScaleFactor = 0.1;
var viewMaxScaleFactor = 10;
var mouseWheelSensitivity = 10; //Fudge Factor
var isMouseDown = false;
var lastMouseCoords = null;
var imageDimensionPixelCount = 25;
var paddingPixelCount = 2;
var canvasDimensionImageCount = 50;
var totalImageCount = Math.pow(canvasDimensionImageCount, 2);
var images = null;
function init() {
images = createLocalImages(totalImageCount, imageDimensionPixelCount);
initInteraction();
renderLoop();
}
function initInteraction() {
var handleMouseDown = function (eventArgs) {
isMouseDown = true;
var offsetXY = getOffsetXY(eventArgs);
lastMouseCoords = [
offsetXY.X,
offsetXY.Y
];
};
var handleMouseUp = function (eventArgs) {
isMouseDown = false;
lastMouseCoords = null;
}
var handleMouseMove = function (eventArgs) {
if (isMouseDown) {
var offsetXY = getOffsetXY(eventArgs);
var panX = offsetXY.X - lastMouseCoords[0];
var panY = offsetXY.Y - lastMouseCoords[1];
pan(panX, panY);
lastMouseCoords = [
offsetXY.X,
offsetXY.Y
];
}
};
var handleMouseWheel = function (eventArgs) {
var mouseX = eventArgs.pageX - masterCanvas.offsetLeft;
var mouseY = eventArgs.pageY - masterCanvas.offsetTop;
var zoom = 1 + (getWheelDelta(eventArgs) / mouseWheelSensitivity);
zoomAboutPoint(mouseX, mouseY, zoom);
if (eventArgs.preventDefault !== undefined) {
eventArgs.preventDefault();
} else {
return false;
}
}
masterCanvas.addEventListener("mousedown", handleMouseDown, false);
masterCanvas.addEventListener("mouseup", handleMouseUp, false);
masterCanvas.addEventListener("mousemove", handleMouseMove, false);
masterCanvas.addEventListener("mousewheel", handleMouseWheel, false);
masterCanvas.addEventListener("DOMMouseScroll", handleMouseWheel, false);
}
function pan(panX, panY) {
masterContext.translate(panX / viewScaleFactor, panY / viewScaleFactor);
viewOffsetX -= panX / viewScaleFactor;
viewOffsetY -= panY / viewScaleFactor;
}
function zoomAboutPoint(zoomX, zoomY, zoomFactor) {
var newCanvasScale = viewScaleFactor * zoomFactor;
if (newCanvasScale < viewMinScaleFactor) {
zoomFactor = viewMinScaleFactor / viewScaleFactor;
} else if (newCanvasScale > viewMaxScaleFactor) {
zoomFactor = viewMaxScaleFactor / viewScaleFactor;
}
masterContext.translate(viewOffsetX, viewOffsetY);
masterContext.scale(zoomFactor, zoomFactor);
viewOffsetX = ((zoomX / viewScaleFactor) + viewOffsetX) - (zoomX / (viewScaleFactor * zoomFactor));
viewOffsetY = ((zoomY / viewScaleFactor) + viewOffsetY) - (zoomY / (viewScaleFactor * zoomFactor));
viewScaleFactor *= zoomFactor;
masterContext.translate(-viewOffsetX, -viewOffsetY);
}
function renderLoop() {
clearCanvas();
renderCanvas();
stats.update();
requestAnimFrame(renderLoop);
}
function clearCanvas() {
masterContext.clearRect(viewOffsetX, viewOffsetY, masterCanvas.width / viewScaleFactor, masterCanvas.height / viewScaleFactor);
}
function renderCanvas() {
for (var imageY = 0; imageY < canvasDimensionImageCount; imageY++) {
for (var imageX = 0; imageX < canvasDimensionImageCount; imageX++) {
var x = imageX * (imageDimensionPixelCount + paddingPixelCount);
var y = imageY * (imageDimensionPixelCount + paddingPixelCount);
var imageIndex = (imageY * canvasDimensionImageCount) + imageX;
var image = images[imageIndex];
masterContext.drawImage(image, x, y, imageDimensionPixelCount, imageDimensionPixelCount);
}
}
}
function createLocalImages(imageCount, imageDimension) {
var tempCanvas = document.createElement('canvas');
tempCanvas.width = imageDimension;
tempCanvas.height = imageDimension;
var tempContext = tempCanvas.getContext('2d');
var images = new Array();
for (var imageIndex = 0; imageIndex < imageCount; imageIndex++) {
tempContext.clearRect(0, 0, imageDimension, imageDimension);
tempContext.fillStyle = "rgb(" + getRandomInt(0, 255) + ", " + getRandomInt(0, 255) + ", " + getRandomInt(0, 255) + ")";
tempContext.fillRect(0, 0, imageDimension, imageDimension);
var image = new Image();
image.src = tempCanvas.toDataURL('image/png');
images.push(image);
}
return images;
}
// Get this party started
init();
And a jsfiddle link for your interactive pleasure:
http://jsfiddle.net/BtyL6/14/
This is drawing 50px x 50px images in a 50 x 50 (2500) grid on the canvas. I've also quickly tried with 25px x 25px and 50 x 50 (2500) images.
We have other local examples that deal with bigger images and larger numbers of images and the other browser start to struggle with these at higher values.
As a quick test I jacked up the code in the js fiddle to 100px x 100px and 100 x 100 (10000) images and that was still running at 16fps when fully zoomed out. (Note: I had to lower the viewMinScaleFactor to 0.01 to fit it all in when zoomed out.)
Chrome on the other hand seems to hit some kind of limit and the FPS drops from 60 to 2-4.
Here's some info about what we've tried and the results:
We've tried using setinterval rather than requestAnimationFrame.
If you load 10 images and draw them 250 times each rather than 2500 images drawn once each then the problem goes away. This seems to indicate that chrome is hitting some kind of limit/trigger as to how much data it's storing about the rendering.
We have culling (not rendering images outside of the visual range) in our more complex examples and while this helps it's not a solution as we need to be able to show all the images at once.
We have the images only being rendered if there have been changes in our local code, against this helps (when nothing changes, obviously) but it isn't a full solution because the canvas should be interactive.
In the example code we're creating the images using a canvas, but the code can also be run hitting a web service to provide the images and the same behaviour (slowness) will be seen.
We've found it very hard to even search for this issue, most results are from a couple of years ago and woefully out of date.
If any more information would be useful then please ask!
EDIT: Changed js fiddle URL to reflect the same code as in the question. The code itself didn't actually change, just the formatting. But I want to be consistent.
EDIT: Updated jsfiddle and and code with css to prevent selection and call requestAnim after the render loop is done.
In Canary this code freezes it on my computer. As to why this happens in Chrome the simple answer is that it uses a different implementation than f.ex. FF. In-depth detail I don't know, but there is obviously room for optimizing the implementation in this area.
I can give some tip however on how you can optimize the given code to make it run in Chrome as well :-)
There are several things here:
You are storing each block of colors as images. This seem to have a huge performance impact on Canary / Chrome.
You are calling requestAnimationFrame at the beginning of the loop
You are clearing and rendering even if there are no changes
Try to (addressing the points):
If you only need solid blocks of colors, draw them directly using fillRect() instead and keep the color indexes in an array (instead of images). Even if you draw them to an off-screen canvas you will only have to do one draw to main canvas instead of multiple image draw operations.
Move requestAnimationFrame to the end of the code block to avoid stacking.
Use dirty flag to prevent unnecessary rendering:
I modified the code a bit - I modified it to use solid colors to demonstrate where the performance impact is in Chrome / Canary.
I set a dirty flag in global scope as true (to render the initial scene) which is set to true each time the mouse move occur:
//global
var isDirty = true;
//mouse move handler
var handleMouseMove = function (eventArgs) {
// other code
isDirty = true;
// other code
};
//render loop
function renderLoop() {
if (isDirty) {
clearCanvas();
renderCanvas();
}
stats.update();
requestAnimFrame(renderLoop);
}
//in renderCanvas at the end:
function renderCanvas() {
// other code
isDirty = false;
}
You will of course need to check for caveats for the isDirty flag elsewhere and also introduce more criteria if it's cleared at the wrong moment. I would store the old position of the mouse and only (in the mouse move) if it changed set the dirty flag - I didn't modify this part though.
As you can see you will be able to run this in Chrome and in FF at a higher FPS.
I also assume (I didn't test) that you can optimize the clearCanvas() function by only drawing the padding/gaps instead of clearing the whole canvas. But that need to be tested.
Added a CSS-rule to prevent the canvas to be selected when using the mouse:
For further optimizing in cases such as this, which is event driven, you don't actually need an animation loop at all. You can just call the redraw when the coords or mouse-wheel changes.
Modification:
http://jsfiddle.net/BtyL6/10/
This was a legitimate bug in chrome.
https://code.google.com/p/chromium/issues/detail?id=247912
It has now been fixed and should be in a chrome mainline release soon.