Move a canvas image back and forth using setInterval - javascript

I'm trying to get an image in a canvas to move back and forth using setInterval. I've been struggling find the solution with no avail. Can anyone figure out the problem?
Here's my code:
...
var img = new Image;
var url = ["sprite-right.png","sprite-left.png"];
var ctx2;
for (i=0; i<3; i++) {
img.src = url[i];
}
img.onload = function() {
start();
}
function start() {
var canv2 = document.getElementById("canvas2");
ctx2 = canv2.getContext("2d");
var x = 50, y = 100;
var direction = "right";
var interval = setInterval(function() {
if (direction == "right") {
img.src=url[0];
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx2.drawImage(img, x, y);
x++;
if (x>690) {
direction = "left";
} // end if
} else if (direction == "left") {
img.src=url[1];
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx2.drawImage(img, x, y);
x--;
if (x<50) {
direction = "right";
} // end if
} // end if
},1); // end set interval
} // end function

Your code is infinite-looping.
(Working plunkr)
Everytime your Image loads, it runs the 'start' function; however, that function changes the image url, so it loads again, which runs the 'start' function, etc.
I got your code running by making multiple Images instead of using just one; this way, the Image isn't constantly reloading.
Notable changes were the addition of the 'img2' variable and reordering the code; everything else was fine the way it was.
var img = new Image(),
img2 = new Image();
var url = ["https://www.google.com/images/srpr/logo11w.png","http://upload.wikimedia.org/wikipedia/commons/thumb/8/85/Smiley.svg/220px-Smiley.svg.png"];
var ctx2;
function start() {
console.log('whirrr');
var canv2 = document.getElementById("canvas2");
ctx2 = canv2.getContext("2d");
var x = 50, y = 100;
var direction = "right";
var interval = setInterval(function() {
if (direction == "right") {
//img.src=url[0];
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx2.drawImage(img, x, y);
x++;
if (x>690) {
direction = "left";
} // end if
} else if (direction == "left") {
//img.src=url[1];
ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
ctx2.drawImage(img2, x, y);
x--;
if (x<50) {
direction = "right";
} // end if
} // end if
},1); // end set interval
} // end function
img.onload = function() {
start();
}
//for (i=0; i<3; i++) {
// img.src = url[i];
//}
img.src = url[0];
img2.src = url[1];

Related

Make Animated Sprite follow cursor on Mousedown with Javascript

This is my first posted question, so be gentle lol. I am trying to make a little game that will display different animated sprites on a canvas element that is laid over a user's webcam feed. Right now I am just working with one sprite to get the functionality right. I only want the sprite to appear and follow the mouse when left-click is held down. Right now the sprite appears on mousedown and animates, but disappears if I move the mouse instead of following. I only want it to disappear on mouseup. I have looked all over for the answer, but haven't been able to find one. Here is the js I have:
'use strict';
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const errorMsgElement = document.querySelector('span#errorMsg');
canvas.width = window.innerWidth;
canvas.height =window.innerHeight;
const constraints = {
// audio: true,
video: {
// width: 1280, height: 720
width: window.innerWidth,
height: window.innerHeight
}
};
// Access webcam
async function init() {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
handleSuccess(stream);
} catch (e) {
errorMsgElement.innerHTML = `navigator.getUserMedia error:${e.toString()}`;
}
}
// Success
function handleSuccess(stream) {
window.stream = stream;
video.srcObject = stream;
}
// Load init
init();
//Code for drawing sprite on Canvas
var image = new Image();
image.src = "Starsprite.png";
var ctx = canvas.getContext("2d");
let magic = false;
var spriteWidth = 187;
var spriteHeight = 60;
var rows = 1;
var cols = 3;
var width = spriteWidth/cols;
var height = spriteHeight/rows;
var curFrame = 0;
var frameCount = 3;
let x =0;
let y =0;
var srcX=0;
var srcY=0;
function startPosition(e){
magic =true;
x =e.clientX;
y =e.clientY;
}
function movePosition(e){
ctx.clearRect(x,y,width,height);
x =e.clientX;
y =e.clientY;
}
function endPosition(){
magic =false;
ctx.clearRect(x,y,width,height);
}
canvas.addEventListener("mousedown", startPosition);
canvas.addEventListener("mousemove",movePosition);
canvas.addEventListener("mouseup", endPosition);
canvas.addEventListener("mousedown",draw);
function updateFrame(){
curFrame = ++curFrame % frameCount;
srcX = curFrame * width;
}
function draw(){
if (!magic) return;
ctx.clearRect(x,y,width,height);
updateFrame();
ctx.drawImage(image,srcX,srcY,width,height, x,y,width,height);
console.log(x + ", " + y);
}
setInterval(draw,200);
The movePosition function does update the x & y coordinates, but the sprite doesn't show while the mouse is moving. I tried creating an event listener to run the draw function on mousemove, but it messes up the animation because it fires every time the browser updates the mouse position. Any ideas on tackling this problem?
Ok, so I have worked some more on it and animated the movement instead of trying to call the function over again. I based this on instructions for animating keystroke inputs. It works fairly well, but if I move the mouse very quickly to a new position it doesn't follow it all the way.
//Code for drawing on Canvas
var image = new Image();
image.src = "Starsprite.png";
var ctx = canvas.getContext("2d");
let magic = false;
var spriteWidth = 187;
var spriteHeight = 60;
var rows = 1;
var cols = 3;
var width = spriteWidth/cols;
var height = spriteHeight/rows;
var curFrame = 0;
var frameCount = 3;
let x =0;
let y =0;
let mousex =0;
let mousey =0;
var stillx =0;
var stilly =0;
var srcX=0;
var srcY=0;
var left =false;
var right =false;
var up = false;
var down = false;
var speed = 60;
var lift = 30;
var buffer = 100;
function startPosition(e){
magic =true;
x =e.clientX;
y =e.clientY;
}
function moveDirection(e){
stillx = x + buffer;
stilly = y + buffer;
mousex = e.clientX;
mousey = e.clientY;
if(mousex>stillx){
left = false;
right = true;
}
if(mousex<stillx){
left = true;
right = false;
}
if(mousey>stilly){
down = true;
up = false;
}
if(mousey<stilly){
down = false;
up = true;
}
}
function endPosition(){
magic =false;
ctx.clearRect(x,y,width,height);
}
canvas.addEventListener("mousedown", startPosition);
canvas.addEventListener("mousemove",moveDirection);
canvas.addEventListener("mouseup", endPosition);
canvas.addEventListener("mousedown",draw);
function moveSprite(){
if(right && x<canvas.width-width){
x+=speed;
// x+=speed;
right =false;
}
if(left && x>0){
x-=speed;
left=false;
}
if(down && y<canvas.height-height){
y+=lift;
down=false;
}
if(up && y>0){
y-=lift;
up=false;
}
}
function updateFrame(){
curFrame = ++curFrame % frameCount;
srcX = curFrame * width;
moveSprite();
}
function draw(){
if (!magic) return;
ctx.clearRect(x,y,width,height);
updateFrame();
ctx.drawImage(image,srcX,srcY,width,height, x,y,width,height);
}
setInterval(draw,200);
If anyone has a better solution please let me know!

Moving multiple object together in canvas with respect to the current position

I have a canvas element with multiple images being displayed. I am trying to achieve is that when I click on a button it should move 200 position in X coordinates. I managed to move the second image, but the first image is not moving. And the images must move with respective to their current position. I am here by attaching my javascript for the same.
$(window).on('load', function () {
imageContent(200);
});
function imageContent(x) {
var posX = x;
var imgArray = ['http://via.placeholder.com/200x200?text=first', 'http://via.placeholder.com/200x200?text=second'];
$.each(imgArray, function (i, l) {
var img = new Image();
img.onload = function () {
var x = 0;
myCanvas(img, i * posX);
};
img.src = l;
});
}
function myCanvas(img, x) {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var last_ts = -1
var speed = 0.1
function renderScene() {
ctx.beginPath();
ctx.drawImage(img, x, 0);
}
function fly(ts) {
if (last_ts > 0) {
x += speed * (ts - last_ts)
}
last_ts = ts
if (x < 200) {
imageContent(x);
requestAnimationFrame(fly);
}
}
renderScene();
$('#movebutton').click(function () {
x = 0;
requestAnimationFrame(fly);
});
}
Here is the codepen. It would be greatful if anyone could help me.
Edit: Modified to achieve side by side movement
I modified your code a little, and removed jQuery. See if it is the effect you are trying to achieve:
var imgArr = [],
c = document.getElementById("myCanvas"),
ctx = c.getContext("2d"),
last_ts = -1,
speed = 0.1,
x = 0
window.onload = function() {
[
'http://via.placeholder.com/200x200?text=first',
'http://via.placeholder.com/200x200?text=second'
].forEach(function(obj, idx) {
var img = new Image()
img.onload = function() {
drawImage(img, idx * 200, 0)
}
img.src = obj
imgArr.push(img)
})
}
function drawImages(x) {
ctx.clearRect(0, 0, c.width, c.height)
imgArr.forEach(function(obj, idx) {
drawImage(obj, x + idx*200, 0)
})
}
function drawImage(img, x, y) {
ctx.beginPath()
ctx.drawImage(img, x, y)
}
function fly(ts) {
if (last_ts > 0) {
x += speed * (ts - last_ts)
}
last_ts = ts
if (x < 200) {
drawImages(x)
requestAnimationFrame(fly)
}
}
document
.getElementById('movebutton')
.addEventListener('click', function() {
x = 0
fly()
}, false)
canvas {
border: 1px solid red;
}
<canvas id="myCanvas" width="960" height="600"></canvas>
<button id="movebutton">Move</button>

Canvas game, where to write code for background?

The Problem
I have been creating a game, I have got to a stage where I want to see what it looks like with a mockup background I have created.
The Question
Where about in my code should I place this code as the place it currently is doesnt show the background.
I want this background on the canvas, the dimensions are correct.
The Code
var game = create_game();
game.init();
function create_game() {
debugger;
var level = 1;
var projectiles_per_level = 1;
var min_speed_per_level = 1;
var max_speed_per_level = 2;
var last_projectile_time = 0;
var next_projectile_time = 0;
var width = 600;
var height = 500;
var delay = 1000;
var item_width = 30;
var item_height = 30;
var total_projectiles = 0;
var projectile_img = new Image();
var projectile_w = 30;
var projectile_h = 30;
var player_img = new Image();
var background_img = new Image();
var c, ctx;
var projectiles = [];
var player = {
x: 200,
y: 400,
score: 0
};
function init() {
projectile_img.src = "projectile.png";
player_img.src = "player.png";
background_img.src = "background.png";
background_img.onload = function(){
context.drawImage(background_img, 0, 0);
}
level = 1;
total_projectiles = 0;
projectiles = [];
c = document.getElementById("c");
ctx = c.getContext("2d");
ctx.fillStyle = "#410b11";
ctx.fillRect(0, 0, 500, 600);
c.addEventListener("mousemove", function (e) {
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
player.x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - player_img.width / 2;
}, false);
setupProjectiles();
requestAnimationFrame(tick);
}
function setupProjectiles() {
var max_projectiles = level * projectiles_per_level;
while (projectiles.length < max_projectiles) {
initProjectile(projectiles.length);
}
}
function initProjectile(index) {
var max_speed = max_speed_per_level * level;
var min_speed = min_speed_per_level * level;
projectiles[index] = {
x: Math.round(Math.random() * (width - 2 * projectile_w)) + projectile_w,
y: -projectile_h,
v: Math.round(Math.random() * (max_speed - min_speed)) + min_speed,
delay: Date.now() + Math.random() * delay
}
total_projectiles++;
}
function collision(projectile) {
if (projectile.y + projectile_img.height < player.y + 20) {
return false;
}
if (projectile.y > player.y + 74) {
return false;
}
if (projectile.x + projectile_img.width < player.x + 20) {
return false;
}
if (projectile.x > player.x + 177) {
return false;
}
return true;
}
function maybeIncreaseDifficulty() {
level = Math.max(1, Math.ceil(player.score / 10));
setupProjectiles();
}
function tick() {
var i;
var projectile;
var dateNow = Date.now();
c.width = c.width;
for (i = 0; i < projectiles.length; i++) {
projectile = projectiles[i];
if (dateNow > projectile.delay) {
projectile.y += projectile.v;
if (collision(projectile)) {
initProjectile(i);
player.score++;
} else if (projectile.y > height) {
initProjectile(i);
} else {
ctx.drawImage(projectile_img, projectile.x, projectile.y);
}
}
}
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "#410b11";
ctx.fillText(player.score, c.width - 50, 50);
ctx.fillText("Level: " + level, 20, 50);
ctx.drawImage(player_img, player.x, player.y);
maybeIncreaseDifficulty();
requestAnimationFrame(tick);
ctx.drawImage(background_img, 0, backgroundY);
}
return {
init: init
};
}
As already pointed out in a comment, here more precisely:
First of all, the background picture must be rendered first in every animation frame.
However, the picture didn't show up at all. This is due to the fact that variable was used (backgroundY), which is never declared somewhere.
This should actually printed to the console as an error "backgroundY" is not defined.
Whenever an the property src of an image object is set to a value, it takes some time until it's loaded. So in many cases, it's necessary to indicate the moment, when it's finished loading by the onload callback.
In this case, however, it's not necessary. The tick / animation loop function will just draw nothing (an empty image object) until it's loaded. After it's loaded it will continue to draw the loaded image every frame.
If the background is really important, meaning, the app should only start, when it's there, of course, one can only start the whole game / animation from within the img.onload handler.
You must draw:
the background first
the player later
level/score info last
Background < Player < UI < You Looking
The drawing order is from back to top (painters algorithm)
Also note that for performance reasons if you background never changes you could draw it in another 'static' canvas under the game canvas.
Otherwise the background will be drawn above/over the player and hide it.

HTML5 canvas scroll-based animation engaging and disengaging

I'm trying to achieve this effect using this tutorial to dynamically add on images from a collection to a canvas to give the appearance of animation as you scroll past the canvas. There are three divs on the page top to bottom: div_1 is a full page static image, div_2 is the canvas, and div_3 is a full-page static image. After scrolling past div_1, the desired behavior is:
- Once div_1 is out of view, the scrolling action of the mouse/trackpad would cease scrolling down the page
- pause over div_2/canvas
- the mouse/trackpad would begin cycling through all of the images (displayed via the canvas) in the collection until the last image is displayed
- the scrolling action would resume to continue down the page to div_3.
I cannot figure out how to engage/disengage the mouseWheel event that I am tying into; from the very top of the page it's (understandably) tied into cycling the images, but I can't figure out a way to trigger it once div_1 is out of view and then disengage it once the scroll-based animation has completed.
Any help is greatly appreciated.
html.erb
<body>
<div class="div_1">
<!-- Full screen image to scroll past -->
</div>
<div class="div_2">
<canvas id="background" width="1280" height="720"></canvas>
</div>
<div class="div_3">
<!-- Full screen image to scroll to once animation is complete -->
</div>
</body>
Javascript
var totalImages = IMAGE_URLS.length;
var images = new Array();
for(var i = 0; i < totalImages; i++) {
var img = new Image;
img.src = IMAGE_URLS[i];
images.push(img);
}
var currentLocation = 0;
var canv;
var context;
$(document).ready(function(){
canv = document.getElementById('background');
context = canv.getContext('2d');
mouseWheel();
// See above for where this gets called
});
var mouseWheel = function() {
window.addEventListener('mousewheel', function(e) {
e.preventDefault(); // No scroll
// The following equation will return either a 1 for scroll down
// or -1 for a scroll up
var delta = Math.max(-1, Math.min(1, e.wheelDelta));
// This code mostly keeps us from going too far in either direction
if(delta == -1) currentLocation += .5;
if(delta == 1) currentLocation -= .5`;
if(currentLocation < 0) currentLocation = 0;
if(currentLocation > images.length)
currentLocation = images.length;
// See below for the details of this function
setImage(currentLocation);
});
}
var setImage = function(newLocation) {
// drawImage takes 5 arguments: image, x, y, width, height
context.drawImage(images[newLocation], 0, 0, 1280, 720);
}
jsFiddle : https://jsfiddle.net/jvLk0vhp/1/
javascript :
var images = new Array();
var currentLocation = 0;
var totalImages = 7;
for (var i = 0; i < totalImages; i++) {
var img = new Image;
switch (i) {
case 0:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/mewtwo.png";
break;
case 1:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/keldeo-ordinary.png";
break;
case 2:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/darkrai.png";
break;
case 3:
img.src = "http://floatzel.net/pokemon/black-white/sprites/images/5.png";
break;
case 4:
img.src = "http://vignette1.wikia.nocookie.net/capx/images/0/03/001.png/revision/latest?cb=20140322003659";
break;
case 5:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/absol.png";
break;
case 6:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/dewgong.png";
break;
case 7:
img.src = "http://orig05.deviantart.net/e770/f/2013/008/c/6/froakie_by_baconboy914-d5qvrjo.gif";
break;
}
images.push(img);
}
var c = document.getElementById("background");
var ctx = c.getContext("2d");
var mouseWheel = function () {
window.addEventListener('mousewheel', function (e) {
e.preventDefault(); // No scroll
// The following equation will return either a 1 for scroll down
// or -1 for a scroll up
var delta = Math.max(-1, Math.min(1, e.wheelDelta));
// This code mostly keeps us from going too far in either direction
if (delta == -1) currentLocation += 1;
if (delta == 1) currentLocation -= 1;
if (currentLocation < 0) currentLocation = 0;
if (currentLocation >= (totalImages - 1)) currentLocation = (totalImages - 1);
console.log("Current location " + currentLocation);
// See below for the details of this function
setImage(currentLocation);
});
}
var setImage = function (newLocation) {
// drawImage takes 5 arguments: image, x, y, width, height
ctx.fillRect(0, 0, c.width, c.height);
ctx.drawImage(images[newLocation], 0, 0, 150, 150);
}
images[0].onload = function () {
ctx.fillRect(0, 0, c.width, c.height);
ctx.drawImage(images[currentLocation], 0, 0, 150, 150);
mouseWheel();
};
I have just used a canvas to achieve the expected output, if you still want to use a div for the first and last check my second answer below
jsfiddle : https://jsfiddle.net/jvLk0vhp/2/
javascript (also using divs)
var images = new Array();
var currentLocation = 0;
var totalImages = 7;
var div1 = document.getElementById("id_1");
var div2 = document.getElementById("id_2");
var div3 = document.getElementById("id_3");
div2.style.display = "none";
div3.style.display = "none";
for (var i = 0; i < totalImages; i++) {
var img = new Image;
switch (i) {
case 1:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/keldeo-ordinary.png";
break;
case 2:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/darkrai.png";
break;
case 3:
img.src = "http://floatzel.net/pokemon/black-white/sprites/images/5.png";
break;
case 4:
img.src = "http://vignette1.wikia.nocookie.net/capx/images/0/03/001.png/revision/latest?cb=20140322003659";
break;
case 5:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/absol.png";
break;
case 6:
img.src = "http://img.pokemondb.net/sprites/black-white/normal/dewgong.png";
break;
}
images.push(img);
}
var c = document.getElementById("background");
var ctx = c.getContext("2d");
var mouseWheel = function () {
window.addEventListener('mousewheel', function (e) {
e.preventDefault(); // No scroll
// The following equation will return either a 1 for scroll down
// or -1 for a scroll up
var delta = Math.max(-1, Math.min(1, e.wheelDelta));
// This code mostly keeps us from going too far in either direction
if (delta == -1) currentLocation += 1;
if (delta == 1) currentLocation -= 1;
if (currentLocation < 0) currentLocation = 0;
if (currentLocation >= (totalImages - 1)) currentLocation = (totalImages - 1);
console.log("Current location " + currentLocation);
// See below for the details of this function
setImage(currentLocation);
});
}
var setImage = function (newLocation) {
// drawImage takes 5 arguments: image, x, y, width, height
if (newLocation == 0) {
div1.style.display = "block";
div2.style.display = "none";
div3.style.display = "none";
} else if (newLocation == (totalImages - 1)) {
div1.style.display = "none";
div2.style.display = "none";
div3.style.display = "block";
} else {
div1.style.display = "none";
div2.style.display = "block";
div3.style.display = "none";
ctx.fillRect(0, 0, c.width, c.height);
ctx.drawImage(images[newLocation], 0, 0, 150, 150);
}
}
ctx.fillRect(0, 0, c.width, c.height);
ctx.drawImage(images[currentLocation], 0, 0, 150, 150);
mouseWheel();

HTML5 Canvas blinking on drawing

I'm beginning with an isometric game, and my canvas is blinking(Not in IE) when draws all the parts of the ground. When I set fps to 20 or less, the blinking stops. How can I solve that? Any ideas?
var camerax = 300, cameray = 100;
var fps = 60;
function draw() {
clearCanvas();
drawGround();
}
function drawGround() {
var img = new Image();
img.onload = function() {
var width = img.width;
var height = img.height;
for (var x = 0; x < 3; x++) {
for (var y = 3; y >= 0; y--) {
mx = (x-y)*height + camerax;
my = (x+y)*height/2 + cameray;
ctx.drawImage(img, mx, my);
}
}
}
img.src = "ground.png";
}
var loop = setInterval(function() {
update();
draw();
}, 1000/fps);
Right now you're reloading the image every frame and unless the onload callback fires within the 16ms of the frame you're going to see a blank canvas.
You should only need to call the new Image, img.onload sequence once, to preload your images. The onload callback would then kick off your first frame, and the draw calls are free to use the image in memory.
Something like:
var camerax = 300, cameray = 100;
var fps = 60;
var img;
var loop;
function init() {
img = new Image();
img.onload = function() {
loop = setInterval(function() {
update();
draw();
}, 1000/fps);
};
img.src = "ground.png";
}
function draw() {
clearCanvas();
drawGround();
}
function drawGround() {
var width = img.width;
var height = img.height;
for (var x = 0; x < 3; x++) {
for (var y = 3; y >= 0; y--) {
mx = (x-y)*height + camerax;
my = (x+y)*height/2 + cameray;
ctx.drawImage(img, mx, my);
}
}
}
}
Of course, it gets more complex once you're waiting for multiple images to preload since you need to start the loop only once all of them are done.
Nice tip, freejosh! Thanks! My screen now is not blinking and the code result was that:
var canvas = document.getElementById("game");
var ctx = canvas.getContext("2d");
var camerax = 300, cameray = 100;
var fps = 60;
var img;
var loop;
function update() {
}
function draw() {
clearCanvas();
drawGround();
}
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function drawGround() {
var width = img.width;
var height = img.height;
for (var x = 0; x < 3; x++) {
for (var y = 3; y >= 0; y--) {
mx = (x-y)*height + camerax;
my = (x+y)*height/2 + cameray;
ctx.drawImage(img, mx, my);
}
}
}
function init() {
img = new Image();
img.onload = function() {
drawGround();
};
img.src = "ground.png";
}
function keyListener(e){
e = e || window.event
if(e.keyCode==37){
camerax--;
}
else if(e.keyCode==39){
camerax++;
}
else if(e.keyCode==38){
cameray--;
}
else if(e.keyCode==40){
cameray++;
}
}
window.onkeypress = function(e) {
keyListener(e);
}
init();
var loop = setInterval(function() {
update();
draw();
}, 1000/fps);

Categories

Resources