How can I make an image fall off the screen? - javascript

I have this code to allow me to place a random image next to the user's cursor using javascript and styled using CSS. I would like images to fall down off the page after a few seconds, my first thought was to animate position but apparently that's not possible?
How could I achieve this? Here is my javascript and CSS code
Javascript
<script>
var myPix = new Array("/img/portfolio/30day.jpg", "/img/portfolio/animationposter.jpg","/img/portfolio/beetle.jpg","/img/portfolio/board.jpg","/img/portfolio/cyanotype.jpg","/img/portfolio/dissent.jpg")
document.addEventListener("click", showCoords);
function showCoords(event)
{
var randomNum = Math.floor(Math.random() * myPix.length);
var yourImage = document.createElement("img");
yourImage.src = myPix[randomNum] ;
yourImage.classList.add("mystyle");
yourImage.style.cssText = " width:360px;height:auto;position:fixed;top:" + event.clientY + "px;left:" + event.clientX + "px;";
document.body.appendChild(yourImage);
}
jQuery.fn.reverse = [].reverse;
</script>
CSS
.mystyle {
border-radius: 20px;
box-shadow: 0px 0px 10px 0 rgba(0, 0, 0, 0.1);
z-index: -2;
width: 360px;
height: auto;
position: fixed;
}

First create an images array so you can easily access all your images. Whenever you create an image, push it to the array:
var images = [];
function showCoords(event)
{
var randomNum = Math.floor(Math.random() * myPix.length);
var yourImage = document.createElement("img");
yourImage.src = myPix[randomNum] ;
yourImage.classList.add("mystyle");
yourImage.style.cssText = " width:360px;height:auto;position:fixed;top:" + event.clientY + "px;left:" + event.clientX + "px;";
images.push([yourImage,0,0]); // this line is where we add the image.
//In the same sub-array, put a number, 0, to store the image's age, and a velocity, 0, to make the physics look good. These will be used later.
document.body.appendChild(yourImage);
}
In order to animate your images, you need to set up some kind of animate loop and call it in a setInterval:
animate = function(){}
setInterval(animate,5); // choose whatever interval you want. Here, it is called every 5 milliseconds
Inside the animate function, we need to add the logic to change every image's position:
animate = function(){
for(image of images){ // loop over all elements of our array
image[1] += 1; //increase the age
if(image[1] > 400){ //if old enough, fall
image[2] += 0.1; //accelerate, tweak this value to change how strong gravity is
currentTop = parseFloat(image[0].style.top.replace("px","")); // get the current y position
currentTop += image[2]; //move
newTop = String(currentTop) + "px"; //change back to string
image[0].style.top = newTop; //set the attribute
if(newTop > document.documentElement.clientHeight){ //if off-screen, remove it
document.body.removeChild(image[0]);
images.splice(images.indexOf(image),1); //remove from array
}
}
}
}
Now you should be good to go. I tested this out in Chrome and it worked for the simple case where I just had one image on screen; hopefully I haven't made any typos writing it up here. To change the speed, either change the acceleration value or change the time in the setInterval. Hope this helps!
Edit: here is a working jsFiddle. I had to use spans instead of images because I don't have your exact image files, but everything else is the same.

Related

javascript - How to insert an img several times into a div using a loop

Hello I am trying to insert an img several times into a div, each time my img will have different properties. My Html code will have a div with an example id called ID_DIV.
I will have a style section into the html code like this img position absolute.
I will preset the variable NumberOfTimes according to the number of img I need into the div.
But I can't manage to make the loop work. Every time I refresh my browser my img appears in a different position, but it wont appear a "NumberOfTImes" times.
I have edited my code into this. But the loop wont work anyways. Still without knowing what it is.
function generateImg(){
var numberOfTimes=prompt("how many times?");
var div_id1=document.getElementById("div_id1");
do{
var oImg=document.createElement("img"); // Creates an oimg node
oImg.setAttribute('src', 's0me_s0urce'); // sets the source for img file
div_id1.appendChild(oImg); // append the img to #id Div.
document.querySelector('img').style.top = Math.floor(Math.random()*401)+'px';
document.querySelector('img').style.left = Math.floor(Math.random()*401)+'px';
numberOfTimes--;
}while(numberOfTimes>0);
}
Please Help. I can't find where is the mistake in this logic.
you need to declare "i" outside of the loop otherwise it will not decrement and will only ever be "NumberOfTimes - 1" and therefore your loop will loop repeadeteldy with the image in the same location. This is also assuming that "NumberOfTimes" is in in the same scope to be used in the first place. Note that I have moved the location of "i" and added a decrement to it in the loop.
function RandomPositionImgs(){
var i=NumberOfTimes;
do{
var oImg=document.createElement("img"); // Creates an oimg node
oImg.setAttribute('src', 'some_source'); // sets the source for img file
document.getElementById("ID_DIV").appendChild(oImg); // append the img to leftSide Div.
document.querySelector('img').style.top = Math.floor(Math.random()*401)+'px'; // random positioning
document.querySelector('img').style.left = Math.floor(Math.random()*401)+'px'; // random positioning
i--;
}while(i>0);
}
Thank you all for your answers you made me realize what I was doing wrong. This is the correct code I did after your comments, its working perfect.
function generateImgs(){
var numberOfTimes=prompt("how many times?");
for(i=0;i<numberOfFaces;i++){
var oImg=document.createElement("img"); // Creates an oimg node
oImg.setAttribute('src', 'valid source'); // sets the source for img file
document.getElementById("div id").appendChild(oImg); // append the img to Div.
oImg.style.top = Math.floor(Math.random()*401)+'px';
oImg.style.left = Math.floor(Math.random()*401)+'px';
}
}
My answer is obsolete now. Anyway, here is a way to deal with overlapping images:
var img, i, x, y;
var size = 48, w = 5, h = 3;
var grid = new Array(w * h).map(x => false);
var n = 5; // n > grid.length => infinite loop
var src = 'https://i.stack.imgur.com/6gwfY.gif?s=48&g=1';
var ct = document.getElementById('ct');
ct.style.width = size * w + 'px';
ct.style.height = size * h + 'px';
while (n-- > 0) {
i = next(), x = i % w, y = (i - x) / w;
img = document.createElement('img');
img.setAttribute('src', src);
img.style.left = size * x + 'px';
img.style.top = size * y + 'px';
ct.appendChild(img);
}
function next () {
var l = grid.length;
var i = Math.floor(Math.random() * l);
// as long as this position is not free, try the next one
while (grid[i]) i = (i + 1) % l;
return grid[i] = true, i;
}
img{position:absolute}
#ct{background:#eee}
#ct{position:relative}
<div id="ct"></div>

Add class to canvas element HTML5 & CreateJS

I am generating 5 circles with a for loop in a canvas and I want to give them a class so I can control them with jquery, but I am doing something wrong. Can you guys figure out what's happening?
var stage;
var quantity = 6,
width = 60,
height = 60,
circles = [];
function init(){
stage = new createjs.Stage("myCanvas");
stage.width = 500;
stage.height = 600;
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", onTick);
setupGame();
}
function setupGame() {
for(var i = 0; i < quantity; i++) {
var circle = document.createElement("img");
circle.setAttribute('src', 'images/circles/circle'+i+'.png');
circle.className = "circle";
circle.style.position = "absolute";
circle.style.left = Math.floor((Math.random() * 100)) + "%";
circle.style.top = Math.floor((Math.random() * 100)) + "%";
circle.style.width = width + "px";
circle.style.height = height + "px";
document.body.appendChild(circle);
circles.push(circle);
}
}
function onTick(e){
stage.update(e);
}
NEW VERSION. With the help from JonnyD, I now have a functional loop. The only problem is that the images get appended to the body, and not to my stage. I have tried stage.appendChild(circle), but it's not working.
Here is a link to an online source so you guys can check it out = LINK
A lot is wrong with your code.
You are trying to add properties to strings within an array which is not possible. Properties are added to objects using dot or bracket notation..
Dot notation
foo.bar.baz
Square bracket notation
foo['bar']['baz']
What I think you want to do is create five circles on the 'screen' or more technically correct DOM (https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) at random positions with set H&W of 60px with classnames of myClass..
I have rewritten your code for you, you can remove the style javascript lines and add them in the CSS if you wish.. All you were really doing wrong was attempting to add properties to array values, wrong technique for the code and missing off .style before width, height. Note You add className's and width and height attributes to DOM elements only.
You can now access the individual circles through a for loop and the circles array or by using the nth-child selector with CSS. e.g .circle:nth-child(1) {animation/transition}
var quantity = 5,
width = 60,
height = 60
circles = [];
function setUp() {
for(var i = 0; i < quantity; i++) {
var circle = document.createElement("div");
circle.className = "circle";
circle.style.position = "absolute";
circle.style.left = Math.floor((Math.random() * 100)) + "%";
circle.style.top = Math.floor((Math.random() * 100)) + "%";
circle.style.backgroundColor = "black";
circle.style.width = width + "px";
circle.style.height = height + "px";
circles.push(circle);
document.body.appendChild(circle);
}
}
setUp();
.circle {
border-radius: 50%;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
}
I didn't see you were using CreateJS.. in that case using the notation like so is okay..
var circle = new createjs.Shape();
circle.graphics.beginFill("DeepSkyBlue").drawCircle(0, 0, 50);
circle.x = 100;
circle.y = 100;
stage.addChild(circle);
ensure that you update the stage as well.
stage.update();
I realize this question has been answered, but since I clicked on this for trying to find out how to add a class to a canvas object in jquery, I'll post how to do that.
var thing = canvas_object;
$('body').append(thing);
var canvas = $('canvas');
canvas.addClass('test');
Things inside canvas are not in DOM, but elements in Scalable Vector Graphics images are, and can be manipulated this way.
Try using SVG if convenient. svg.js is a lightweight library to manipulate SVG.

Animating array of divs; only the final element is modified

This is baffling me slightly at the moment.
I'm a fairly experienced programmer who is looking to throw a bit of a surprise for my fiancee on the countdown page for our wedding. The desired effect essentially simulated confetti falling behind the main page. My setup is as follows:
I have an array of divs in javascript. Each div is absolutely positioned on the page. I have an interval set that updates every 50 or so milliseconds. On each tick, I run a for loop over my array of divs, and do calculations and update their positions.
Problem
The problem I am having, however, is that even though I can clearly see that all divs are created, stored into the array, and modified (in this case, each has a slight randomized rotation) during the initialization phase, once the tick to update their position starts, only the div stored in whatever the last slot of the array is gets its position updated. I even tried hard-coding each div by index for testing purposes, but it is strictly the last element that will actually update its position. Even upon printing the current "left" and "top" style values for each div before and after the calculations shows that the value has been changed, but no visible change is noticeable. Here's my basic code:
Javascript
var container; // the div that will have all the other divs attached to it
var conf = new Array(); // the array of divs to modify
var rots = new Array(); // the rotation of each div
var vels = new Array(); // the velocity of each div
var xPos = new Array(); // the current x value of position in pixels
var yPos = new Array(); // the current y value of position in pixels
var xVels = new Array(); // the x portion of that velocity
var yVels = new Array(); // the y portion of that velocity
function initialize() // this is attached to the onload event on my <body> tag
{
container = document.getElementById("conf_container");
confettiInit(); // creates, initializes, and displays each div
setInterval(updateConfetti, 42);
}
function confettiInit()
{
screenHeight = window.innerHeight;
screenWidth = window.innerWidth;
for (var i = 0; i < 25; i ++) // create 25 confetti flakes
{
// append a div in the container with a unique ID
container.innerHTML += "<div class='conf' id='conf_" + i + "'></div>";
// store the element in an array
conf[i] = document.getElementById("conf_" + i);
// ensure confetti is in the background, and each z index is unique
conf[i].style.zIndex = -i - 1;
xPos[i] = window.innerWidth * Math.random(); // calculate random x position
conf[i].style.left = xPos[i] + "px"; // set x position of div
yPos[i] = -40; // y position above screen
conf[i].style.top = yPos[i] + "px"; // set y position of div
// calculate the random amount of rotation (-30 to 30 degrees)
rots[i] = Math.random() * 60*(Math.PI/180) - 30*(Math.PI/180);
// set rotation of div
conf[i].style.webkitTransform = "rotate(" + -rots[i] + "rad)";
vels[i] = Math.random() * 3 + 2; // calculate random velocity (2-5)
xVels[i] = vels[i] * Math.sin(rots[i]); // get x portion of velocity
yVels[i] = vels[i] * Math.cos(rots[i]); // get y portion of velocity
}
}
function updateConfetti()
{
for (var i = 0; i < 25; i ++)
{
xPos[i] += xVels[i]; // calculate new x position
yPos[i] += yVels[i]; // calculate new y position
conf[i].style.left = xPos[i] + "px"; // set div's x position
conf[i].style.top = yPos[i] + "px"; // set div's y position
// if the confetti piece leaves the viewing area...
if (xPos[i] < -50 ||
xPos[i] > window.screenWidth + 10 ||
yPos[i] > window.screenHeight + 10)
{
// send it back to the top and reinitialize it
xPos[i] = window.innerWidth * Math.random();
conf[i].style.left = xPos[i] + "px";
yPos[i] = -40;
conf[i].style.top = yPos[i] + "px";
rots[i] = Math.random() * 60*(Math.PI/180) - 30*(Math.PI/180);
conf[i].style.webkitTransform = "rotate(" + -rots[i] + "rad)";
vels[i] = Math.random() * 3 + 2;
xVels[i] = vels[i] * Math.sin(rots[i]);
yVels[i] = vels[i] * Math.cos(rots[i]);
}
}
}
CSS
div.conf
{
height: 29px;
width: 50px;
background-image: url("images/ConfettiYellow.gif");
position: absolute;
}
#conf_container
{
left: 0px;
top: 0px;
width: 100%;
height: 100%;
position: absolute;
overflow: hidden;
}
It's something wrong with your divs creation. It start works if you do it in conventional way, like this:
var d = document.createElement("div");
d.className = "conf";
container.appendChild(d);
conf[i] = d;

Can you use new Date().getMilliseconds() (or a different alternative) as a timer (like setTimeout or setInterval), and if so, how?

I have two setTimeout functions: one makes a div pop up randomly on the y axis, and one moves it vertically. These to need to be perfectly in sync, and setTimeout just doesn't cut it: the div has to reset its x position as soon as its y position becomes greater than the window height, before resetting to 0 to start anew. Something like that. I'm thinking of somehow integrating getMilliseconds, but any alternative will do.
Here's the current Javascript:
var width = window.outerWidth;
var height = window.outerHeight;
var h2 = height * 5;
//left
function LR() {
setTimeout(function() {
var left = [];
var one = 1;
do {
one++;
left.push(one);
}
while (one <= width);
var random = Math.floor(Math.random() * left.length);
document.getElementById("test").style.left = random + "px";
LR();
}, h2);
}
LR();
//top
function TB() {
setTimeout(function() {
var one = document.getElementById("test").offsetTop;
one++;
document.getElementById("test").style.top = one + "px";
if (one == height) {
one == 0;
document.getElementById("test").style.top = 0;
}
TB();
}, 2);
}
TB();
If you'd like to look at my full code, and/or preview, I've set up a JSFiddle: http://jsfiddle.net/JqVb9/. Thanks in advance.
Yes, for animations you always should check the current time - setTimeout/.Interval are absolutely unreliable and tend to drift.
To do that, just use Date.now() and make your animation a mathematical function to get the position from time (easiest: linear movement).
As #JanDvorak already pointed out in the comments, you should consider performing all movements from the same timer. This will make the animation smoother as you don't change the DOM / styling too often - each frame is usually rendered only once. Also, this will ensure synchronity.

Why are my particles drifting? ( aka Is Math.random() broken or is it my algorithm? )

Using Javascript I'm crudely simulating Brownian motion of particles, but for some reason I don't understand my particles are drifting up and to the left.
The algorithm is pretty straight forward. Each particle is a div and I simply add or subtract a random number from each div's top and left position each round.
I read up on Math.random() a little, and I've tried to use a function that returns a random number from min to max inclussive:
// Returns a random integer between min and max
// Using Math.round() will give you a non-uniform distribution!
function ran(min, max)
{
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Here is the function for the movement of the particles:
var x, y, $elie, pos, nowX, nowY, i, $that;
function moveIt()
{
$("div.spec").each(function(i, v) {
x = ran(-5, 5);
y = ran(-5, 5);
$elie = $(v);
pos = $elie.position();
nowX = pos.left;
nowY = pos.top;
// The min and abs are to keep the particles within a box
// The drift occurs even if I remove min and abs
$elie.css("left", Math.min(Math.abs(nowX + x), 515));
$elie.css("top", Math.min(Math.abs(nowY + y), 515));
});
}
And here is how the particles are initially set up an the setInterval started.
$(function() {
$("body").append("<div/>").attr("id","box");
$elie = $("<div/>").attr("class","spec");
// Note that math random is inclussive for 0 and exclussive for Max
for (i = 0; i < 25; ++i)
{
$that = $elie.clone();
$that.css("top", ran(0, 495));
$that.css("left", ran(0, 495));
$("#box").append($that);
}
timer = setInterval(moveIt, 60);
$("input").toggle(function() {
clearInterval(timer);
this.value = " Start ";
}, function() {
timer = setInterval(moveIt, 60);
this.value = " Stop ";
});
});
My problem is that using the min and max from above ( -5, 5 ), all the particles drift up and to the left very fast.
jsFiddle example of drift (-5, 5)
Example of drift even with the removal of .min() and .abs().
To counteract this, I have to use a min and max of -1, 5.
jsFiddle example of no drift (-1, 5)
Here is the CSS for the div all the particles are contained in:
#box {
width:500px;
height:500px;
border:2px #000 solid;
position: relative; }
Here is the default CSS for each particle:
div.spec {
width:5px;
height:5px;
background-color:#00DDAA;
position:absolute; }
What is going on? Why does a min and max of -5 and 5 cause an upward and leftward drift?
A test of the random function ran() doesn't seem to show such a persistent negative drift.
jsFiddle example of testing ran()
The ran() function was taken from the MDC Math.random() page.
Your mistake is to use
pos = $elie.position();
rather than
pos = $elie.offset();
This wouldn't have made a difference had they been added to parent div, but your elements aren't properly added to a parent div, they're appended directly to the document body. So your other mistake is this:
$("body").append("<div/>").attr("id","box");
If you want the div to have id of 'box', the line should read:
$box = $("<div/>").attr("id","box");
$("body").append($box)
Otherwise you're actually giving "body" the id of "box"
EDIT:
The most efficient way to append the div would be the following (as noted by this post):
$(document.createElement('div')).appendTo('body').attr('id', 'box')
Instead of using .position(), try .offset() instead. Looks like it works.
Position.
Offset.
It works this way because you're setting the absolute 'left' and 'top' values in CSS. Instead, you can use this Example:
$elie.css("margin-left", nowX + x);
$elie.css("margin-top", nowY + y);

Categories