I'm trying to change image position using JavaScript using my code but for some reason it doesn't work. Can someone explain the reason.
var walk, isWaveSpawned = true;
var walkers = [];
function start()
{
walk = document.getElementById("walk");
draw(); //Animation function
}
function draw()
{
if(isWaveSpawned) //Generate a wave of 5 "walkers"
{
isWaveSpawned = false;
for(var i = 0; i < 5; i++
walkers.push(new createWalker());
}
for(var o = 0; o < walkers.length; o++) //Add 1px to x position after each frame
{
walkers[o].x += walkers[o].speed;
walkers[o].image.style.left = walkers[o].x;
walkers[o].image.style.top = walkers[o].y;
}
requestAnimationFrame(draw);
}
function createWalker()
{
this.x = 0;
this.y = 100;
this.speed = 1;
this.image = walk.cloneNode(false); //Possible cause of issue
}
<!DOCTYPE html>
<html>
<body onload="start()">
<img id="walk" src="https://i.imgur.com/ArYIIjU.gif">
</body>
</html>
My GIF image is visible in top left corner but doesn't move.
P.S. Added a HTML/JS snippet but it outputs some errors while in my end these errors are not seen.
First let's modify the way you're cloning the gif - get rid of this line:
this.image = walk.cloneNode(false);
and insert this:
this.image = document.createElement("img");
This will create a fresh empty HTML image element.
Now assign it's .src property the source of your gif:
this.image.src=document.getElementById("walk").src;
and set the CSS position property to absolute:
this.image.style="position:absolute;";
finally add this new image element to the body using:
document.body.appendChild(this.image);
If you hit run you will still not see any movement because there's still a little fix to do!
Find this line:
walkers[o].image.style.left = walkers[o].x;
and change it to this:
walkers[o].image.style.left = walkers[o].x + "px";
var walk, isWaveSpawned = true;
var walkers = [];
function start() {
walk = document.getElementById("walk");
draw(); //Animation function
}
function draw() {
if (isWaveSpawned) //Generate a wave of 5 "walkers"
{
isWaveSpawned = false;
for (var i = 0; i < 5; i++)
walkers.push(new createWalker());
}
for (var o = 0; o < walkers.length; o++) //Add 1px to x position after each frame
{
walkers[o].x += walkers[o].speed;
walkers[o].image.style.left = walkers[o].x + "px";
walkers[o].image.style.top = walkers[o].y + "px";
}
requestAnimationFrame(draw);
}
function createWalker() {
this.x = 0;
this.y = 100;
this.speed = 1;
this.image = document.createElement("img");
this.image.src = document.getElementById("walk").src;
this.image.style = "position:absolute;";
document.body.appendChild(this.image);
}
start();
<body>
<img id="walk" src="https://i.imgur.com/ArYIIjU.gif">
</body>
Related
i am getting frames from gif using Libgif.
and then i am appending those frames in the div with Id = frames.
then i am taking those frames and trying to add each frames one after the other in canvas to make a spritesheet.
in the end i am getting an image in canvas but instead of getting different frames i am getting same image in the spritesheet.
Please help me find the issue.
I had taken canvas width 10000 assuming a gif wont have frames more than 100.
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
ctx.clearRect(0, 0, ctx.width, ctx.height);
ctx.beginPath();
var imageGiF = "";
var total = 0;
let canvasWidth = 0;
let canvasHeight = 0;
$('div.gifimage img').each(function(idx, img_tag) {
var total = 0;
if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
var rub = new SuperGif({
gif: img_tag,
progressbar_height: 0
});
rub.load(function() {
for (let i = 0; i < rub.get_length(); i++) {
total += 1;
rub.move_to(i);
// var canvas = cloneCanvas(rub.get_canvas());
var canvas = rub.get_canvas().toDataURL("image/png");
img = $('<img id = "gifframe' + i + '"src= "' + canvas + '" class= frameimages>');
$("#frames").append(img);
}
var frameimages = document.getElementById("frames").querySelectorAll(".frameimages");
var totalimages = frameimages.length;
x = 0;
y = 0;
for (let i = 0; i < frameimages.length; i++) {
img = document.getElementById("gifframe" + i + "");
img.onload = function() {
ctx.drawImage(img, i * 100, 0, 100, 100);
total++;
console.log(total);
}
}
totalwidth = (total) * 100;
c.width = totalwidth;
c.height = 100;
setTimeout(() => {
imageGiF = c.toDataURL("image/png");
console.log(imageGiF);
// addBgimg(imageGiF)
}, 10);
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/buzzfeed/libgif-js/master/libgif.js"></script>
<div class="gifimage" id="placehere">
<img src="https://media1.giphy.com/media/bzUwzbxcvJ3XQlcnoi/giphy.gif" alt="">
</div>
<div id="frames" class="classGIF"></div>
<canvas id='myCanvas' width="10000" height="300"></canvas>
You were looping through the images, using img in your event handler.
However, this variable img in the outer scope was overridden by every loop, until it was finished looping through everything, then img was stuck on the last frame added.
Then when the event handler triggered, it added the last frame in every instance, because that was the value of img at that point. The loop was done before the images could load.
By adding it to it's own scope by wrapping it in a function, the variable is preserved.
I also modified your code to store the DOM img elements in an array, so you don't need expensive DOM lookups which makes your code a tad bit faster.
I added comments in the code to explain my changes.
c = document.getElementById("myCanvas");
ctx = c.getContext("2d");
ctx.clearRect(0, 0, ctx.width, ctx.height);
ctx.beginPath();
var imageGiF = "";
var total = 0;
let canvasWidth = 0;
let canvasHeight = 0;
$('div.gifimage img').each(function(idx, img_tag) {
var total = 0;
if (/^.+\.gif$/.test($(img_tag).prop("src"))) {
var rub = new SuperGif({
gif: img_tag,
progressbar_height: 0
});
rub.load(function() {
// An array for the image references
let images = [];
// Keep the reference to save on expensive DOM lookups every iteration.
let frames = $("#frames");
for (let i = 0; i < rub.get_length(); i++) {
total += 1;
rub.move_to(i);
// var canvas = cloneCanvas(rub.get_canvas());
var canvas = rub.get_canvas().toDataURL("image/png");
img = $('<img id = "gifframe' + i + '"src= "' + canvas + '" class="frameimages">');
// Use the reference to append the image.
frames.append(img);
// Add image to images array with the current index as the array index.
// Use the jQuery get method to get the actual DOM element.
images[i] = img.get(0);
}
var frameimages = document.getElementById("frames").querySelectorAll(".frameimages");
var totalimages = frameimages.length;
x = 0;
y = 0;
// Loop through all the images in the image array
// Using a scope so the reference to img won't be overridden.
images.forEach((img, index) => {
img.onload = () => {
ctx.drawImage(img, index * 100, 0, 100, 100);
total++;
console.log(total);
}
})
totalwidth = (total) * 100;
c.width = totalwidth;
c.height = 100;
setTimeout(() => {
imageGiF = c.toDataURL("image/png");
console.log(imageGiF);
// addBgimg(imageGiF)
}, 10);
});
}
});
#frames { display:none;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/buzzfeed/libgif-js/master/libgif.js"></script>
<div class="gifimage" id="placehere">
<img src="https://media1.giphy.com/media/bzUwzbxcvJ3XQlcnoi/giphy.gif" alt="">
</div>
<div id="frames" class="classGIF"></div>
<canvas id='myCanvas' width="10000" height="300"></canvas>
I'm loading image URLs from a json file. I have everything working fine, except displaying the actual images.
It's a simple clik carousel. Hit the click and it moves index onto the next one. I want to make sure the images display at the same time, obviously but it's not working (images are referenced, but don't display).
Anyone know what I am doing wrong?
var w = window.innerWidth;
var h = window.innerHeight;
var xPos = w/2;
var yPos = h/2;
var index = 0;
var imageData;
var imgList = [];
var indexMax;
function preload() {
loadJSON("image_search_result.json", resultLoaded);
}
function resultLoaded(data) {
imageData = data;
indexMax = imageData.items.length;
for (i = 0; i < indexMax; i++) {
imgList.push(imageData.items[i]['link']);
}
}
function mouseReleased() {
index = index + 1;
if (index == indexMax){
index = index - indexMax;
}
}
function setup() {
createCanvas(w,h);
}
function draw() {
background(0);
image(loadImage(imgList[index]),xPos,yPos,w,h);
text(index,20,60); // index counter
text(imgList[index],80,60); // image list number
textSize(20);
fill(255);
}
I think the problem is that you're trying to upload images every call to the drawing function. As you wrote the code, even if the images to be uploaded will always be the same, p5.js will reload them from scratch. You should load them before starting the program. As I did below:
var w = window.innerWidth;
var h = window.innerHeight;
var xPos = w / 2;
var yPos = h / 2;
var index = 0;
var imageData;
var imgList = [];
var indexMax;
function preload() {
loadJSON("img.json", resultLoaded);
}
function resultLoaded(data) {
imageData = data;
indexMax = imageData.items.length;
for (i = 0; i < indexMax; i++) {
url = imageData.items[i]["link"];
imgList.push(loadImage(url));
}
}
function mouseReleased() {
index = index + 1;
if (index == indexMax) {
index = index - indexMax;
}
}
function setup() {
createCanvas(w, h);
}
function draw() {
background(0);
image(imgList[index], xPos, yPos, w, h);
text(index, 20, 60);
textSize(20);
fill(255);
}
P.S. #George Profenza gave the same answer while I was writing the code. Sorry
Finally I got this to work by adding a callback in loadImage. I was unaware you could do this (hadn't seen it referenced in any documentation), but it works very efficiently.
function draw() {
nextImg = loadImage(imgList[index], imageLoaded);
text(index,20,60); // index counter
text(imgList[index],80,60); // image list number
textSize(20);
fill(255);
}
function imageLoaded() {
background(0);
image(nextImg,xPos,yPos,w,h);
imageMode(CENTER);
}
So I'm making a game in JavaScript and I'm trying to place "Sprites" I've defined inside an array.
This is what my Sprite object looks like
function Sprite(imgsrc)
{
this.x = 0;
this.y = 0;
this.velocity_xr = 0;
this.velocity_xl = 0;
this.velocity_yu = 0;
this.velocity_yd = 0;
this.velocity_x = 0;
this.velocity_y = 0;
this.IMG = new Image();
this.IMG.src = imgsrc;
this.visible = false;
}
So I've done this:
var buttons = [];
var b = new Sprite("https://i.imgur.com/qDH38qs.png");
buttons.push(b):
And then tried to draw it using this line, with no success.
ctx.drawImage(bullets[0].IMG,300,300);
However, this works:
ctx.drawImage(b.IMG,300,300);
What am I missing?
I try to make a game with HTML Canvas but I've a problem.
When I call my 'init' function with 'window.onload', my program doesn't load any images. The canvas has been created, but images aren't there, and when I submit my function in chrome console, images appears... So I want to load my images and execute the function when it's done.
Thanks for help !
var requestAnimId;
// Initialisation
function init() {
var tilemapCtx = new Canvas('tilemap', 800, 600, 1);
var tilemap = new Tilemap("sand", tilemapCtx);
requestAnimId = window.requestAnimationFrame(update);
}
// Boucle de rafraîchissement
function update() {
requestAnimId = window.requestAnimationFrame(update);
}
window.onload = init;
Here is Tilemap :
var Tilemap = function(map, canvas) {
for (var y = 0; y < 10; y++) {
for (var x = 0; x < 10; x++) {
// Création de la tile
this.image = new Image();
// Définition de l'image
switch(tilemaps[map][y][x]) {
case " ":
this.image.src = "assets/sand.png";
break;
case "#":
this.image.src = "assets/wall.png";
break;
}
// Affichage de l'image
//canvas.drawImage(this.image, tileX, tileY, tilesetWidth, tilesetHeight, x, y, 32, 32);
canvas.drawImage(this.image, 32, 32);
}
}
}
and Canvas :
var Canvas = function(name, width, height, zIndex) {
this.canvas = window.document.createElement("canvas");
this.canvas.id = name;
this.canvas.style.position = "absolute";
this.canvas.width = width;
this.canvas.height = height;
this.canvas.style.zIndex = zIndex;
document.body.appendChild(this.canvas);
return this.canvas.getContext('2d');
}
Notice that for your tile you only use two images. You use a double loop that will load 100 images.
One problem is you are creating 100 images when in the end you only need two.
Now the reason why your images don't get drawn is because they haven't been loaded. You need to give time for your images to load before you try drawing them.
You need to create an array list of images and make sure they are loaded before you call your init()
This will ensure all your images have been loaded before init() is called.
var len = pics.length;
var loadCounter = 0;
for(var i = 0; i < len; i++) {
$(document.createElement(img)).attr('src', pics[i]).load(function() {
alert(pics[i] + 'is loaded');
loadCounter++;
if(loadCounter === len) {
alert("all are loaded");
}
});
}
see the news scroller on the top of this site
http://track.dc.gov/Agency/DH0
Any idea what library/functions this site uses to implment such a smooth scroller?
They have a very nicely formatted block of code you can study. Open your favorite JS debugger when you visit the site, wait for everything to get moving, and then press "Break All" or the equivalent in your debugger. You'll see something like the following:
Dashboard.UI.EndlessLine = function() {
var me = this;
me.jq = $(me);
me.classNames = { CONTAINER: "uiEndless", VIEW: "uiEndlessView", CANVAS: "uiEndlessCanvas", TILE: "uiEndlessTile" };
var canvas = null;
var view = null;
var tiles = null;
var x = 0;
var xx = 0;
var canvasWidth = 0;
var step = 1;
var delay = 40;
me.initialize = function(container, data, handler) {
required(container, "container");
required(data, "data");
required(handler, "handler");
container.addClass(me.classNames.CONTAINER);
view = newDiv(me.classNames.VIEW);
canvas = newDiv(me.classNames.CANVAS);
view.append(canvas);
container.append(view);
x = 0;
xx = 0;
canvasWidth = 0;
tiles = me.populateTiles(data, handler);
container.click(function() {
if (me.started()) me.stop(); else me.start();
});
};
me._resize = function(size) {
};
var moveId = 0;
me.start = function() {
me.stop();
me.tick();
}
me.stop = function() {
if (moveId > 0) clearTimeout(moveId);
moveId = 0;
}
me.started = function() {
return moveId > 0;
};
me.tick = function() {
var tile = tiles.current();
var width = tile.calculatedWidth;
if (x < width - step) {
x += step;
} else {
x = 0;
tile.css("left", canvasWidth + "px");
if (tiles.advance()) {
xx = 0;
canvasWidth = 0;
do {
current = tiles.current();
width = current.calculatedWidth;
current[0].style.left = canvasWidth + "px";
canvasWidth += width;
} while (!tiles.advance());
} else {
canvasWidth += width;
}
}
canvas[0].style.left = -(xx) + "px";
xx += step;
moveId = setTimeout(me.tick, delay);
}
me.populateTiles = function(data, handler) {
var tiles = new Dashboard.Core.List();
var viewWidth = view.contentWidth();
var maxHeight = 0;
each(data, function() {
var tile = newDiv(me.classNames.TILE);
handler.call(this, tile);
tile.css({ left: canvasWidth + "px", top: 0 });
canvas.append(tile);
var width = tile.outerWidth();
var height = tile.outerHeight();
if (maxHeight < height) maxHeight = height;
tile.calculatedWidth = width;
canvasWidth += width; // getting width may only be done after the element is attached to DOM
tiles.append(tile);
view.height(height);
});
return tiles.createCycle();
}
}
I'm impressed -- everything looks professional and nicely namespaced.
Update: If you want an explanation of how it works, focus on the tick method defined above. Glossing over all the details (cause I haven't really studied it myself), it calculates a step size, moves the message element to the left by the some amount, and schedules the next tick call for 40 milliseconds in the future.
jQuery enthusiast, Remy Sharp, has his own Marquee Plugin that you can implement pretty easily. You can gather deeper details of it on his blog or by visiting the demo page.
For Mootools users, there's Mooquee.
You can also view the actual code for this example online at http://track.dc.gov/Resource/Script/ - do a search for "uiEndless" to find the target-scripting.