Object constructor - managing loop animation - javascript

I am doing a website wich has a lot of animations managed by JavaScript, when i started i just defined a function and some variables for the animation and repeat the process, like this. And a think is not the good way.
//BRIGHT ANIMATION
var frameWidth1 = 386;
var frameHeight1 = 100;
var spriteWidth1 = 20067;
var spriteHeight1 = 100;
var spriteElement1 = document.getElementById("bright");
var curPx1 = 0;
var ti1;
function animateSpriteB() {
spriteElement1.style.backgroundPosition = "-" + curPx1 + 'px 0px';
curPx1 = curPx1 + frameWidth1;
if (curPx1 >= spriteWidth1) {
curPx1 = 0;
}
ti1 = setTimeout(animateSpriteB, 70);
}
animateSpriteB();
// PAPIRO ANIMATION
var frameWidth = 56;
var frameHeight = 218;
var spriteWidth = 2016;
var spriteHeight = 218;
var spriteElement = document.getElementById("roll-off");
var curPx = 0;
var ti;
function animateSprite() {
spriteElement.style.backgroundPosition = "-" + curPx + 'px 0px';
curPx = curPx + frameWidth;
ti = setTimeout(animateSprite, 27.7);
if (curPx === spriteWidth) {
clearTimeout(ti);
}
}
function slideMask(){
var mask = $("#paper-mask");
var paper = $("#paper");
mask.animate({
width: 450
},{
duration: 1000,
complete: function(){
$("#paper-content").fadeIn();
}
});
}
var ti = setTimeout(function(){
animateSprite();
slideMask();
}, 3000);
So know, I decided to use a constructor to re use the same code and manage all the animations in the website. i came with Something like this:
// CONSTRUCTOR WHO MANAGE THE ANIMATIONS FOR THE WEBSITE
function SpriteAnimation(frameWidth, spriteWidth, spriteElement, isLoop){
this.frameWidth = frameWidth;
this.spriteWidth = spriteWidth;
this.spriteElement = spriteElement;
this.isLoop = isLoop;
this.curPx = 0;
this.ti;
}
SpriteAnimation.prototype.start = function(){
var selector = document.getElementById(this.spriteElement);
selector.style.backgroundPosition = "-" + this.curPx + "px 0px";
this.curPx = this.curPx + this.frameWidth;
this.ti = setTimeout(this.start, 2000);
if (this.curPx === this.spriteWidth){
clearTimeout(this.ti);
}
this.start();
}
var letter = new SpriteAnimation(935.4, 17774, "letter", true);
letter.start();
I am having problems in performance, every time i run the code my browser just crash i also think im not doing good the loop. So here comes my question: how can i do to manage the animations with an object constructor in wich i can pass parameters like if it is loop animation and the sprite parameters?... I appreciate the help you guys can bring me :)
#Tibos Your code has been from great help for me i just spent almost 4 hours trying to achieve this, and then yo came out and make it really easy, this is how my code looks now, i added another parameter: frame rate. so every animation can have a different frame rate. Also modified a bit the if statement because the animation was running untill the sprite dissapear and i need them to stay in the last frame, let me know if this is the correct form.
// CONSTRUCTOR WHO MANAGE THE ANIMATIONS FOR THE WEBSITE
function SpriteAnimation(frameWidth, spriteWidth, spriteElement, shouldLoop, frameRate){
this.frameWidth = frameWidth;
this.spriteWidth = spriteWidth;
this.selector = document.getElementById(spriteElement);
this.shouldLoop = shouldLoop ;
this.curPx = 0;
this.frameRate = frameRate;
this.ti;
}
SpriteAnimation.prototype.start = function(){
this.selector.style.backgroundPosition = "-" + this.curPx + "px 0px";
this.curPx += this.frameWidth;
if (this.curPx < (this.spriteWidth - this.frameWidth)){
setTimeout(this.start.bind(this), this.frameRate);
} else if (this.shouldLoop) {
this.curPx = 0;
this.start();
}
};
var letter = new SpriteAnimation(935.4, 17774, "letter", true, 60);
letter.start();

You have a few problems in your code, presented here in order of impact:
recursively calling start
losing the reference to this
clearing timeout as soon as it's set
unused variables
selecting the element at each iteration
Here is some better code (that works):
function SpriteAnimation(frameWidth, spriteWidth, spriteElement, shouldLoop){
this.frameWidth = frameWidth;
this.spriteWidth = spriteWidth;
this.selector = document.getElementById(spriteElement);
this.curPx = 0;
this.shouldLoop = shouldLoop;
}
SpriteAnimation.prototype.start = function(){
this.selector.style.backgroundPosition = "-" + this.curPx + "px 0px";
this.curPx += this.frameWidth;
if (this.curPx <= this.spriteWidth){
setTimeout(this.start.bind(this), 2000);
} else if (this.shouldLoop) {
this.curPx = 0;
this.start();
}
};
var letter = new SpriteAnimation(935.4, 17774, "letter", true);
letter.start();
DEMO: http://jsbin.com/oJIYoRU/1/edit

This function calls itself recursively with no base case. As soon as you invoke it you will lock the UI and overflow the call-stack.
SpriteAnimation.prototype.start = function(){
... some code ...
this.start();
}

Related

Trying to load a random link out of array

I know this is a very noob question, looked up a couple of examples but me being a beginner I'm having trouble implementing it in my code.
I think the title is pretty clear cut, I would like that the loop starts with a random item, and continues picking random items out of the array.
var bgImageArray = [
"link1" , "link2" , "link3"
]
base = "https://lh3.googleusercontent.com/pw/";
bgImageArray.forEach(function(img){
new Image().src = base + img;
});
function backgroundSequence() {
window.clearTimeout();
var k = 0;
for (i = 0; i < bgImageArray.length; i++) {
setTimeout(function(){
document.getElementById('animated-bg').style.background = "url(" + base + bgImageArray[k] + ") no-repeat center center";
if ((k + 1) === bgImageArray.length) { setTimeout(function() { backgroundSequence() }, (60000 / tempo.value))} else { k++; }
}, (60000 / tempo.value) * i)
}
}
``
use this code for select random element from array
var bgImageArray = ["link1", "link2", "link3"];
const random = Math.floor(Math.random() * bgImageArray.length);
console.log(random, bgImageArray[random]);
Perhaps better like this
Update: Missed the random part
let bgImageArray = ["link1", "link2", "link3"];
const base = "https://lh3.googleusercontent.com/pw/";
bgImageArray = bgImageArray.map(img => new Image().src = base + img);
function backgroundSequence() {
const rndImageSrc = bgImageArray[Math.floor(Math.random()*bgImageArray.length)].src;
document.getElementById('animated-bg').style.background = "url(" + rndImageSrc + ") no-repeat center center";
}
let tId = setInteval(backgroundSequence,tempo*1000);

Is there any way of chaining too many document.getElementById on a particular id of javascript object?

function showShape(){
var top=Math.random()*370;
var left=Math.random()*350;
var width=Math.random()*300;
var height=Math.random()*300;
start = new Date().getTime();
document.getElementById("shape").style.display = "block";
document.getElementById("shape").style.top=top + "px";
document.getElementById("shape").style.left=left + "px";
document.getElementById("shape").style.width=width+"px";
document.getElementById("shape").style.height=height+"px";
}
I want to reduce the redundancy.There is too many documentgetelement that I want to get rid of.
1) Cache your element
2) Create an randomise function that accepts a number and returns an integer.
3) Ensure your element has its position property set.
const shape = document.getElementById('shape');
function rnd(seed) {
return Math.round(Math.random() * seed);
}
function showShape(shape) {
var top = rnd(370);
var left = rnd(350);
var width = rnd(300);
var height = rnd(300);
shape.style.position = 'absolute';
shape.style.display = 'block';
shape.style.top = `${top}px`;
shape.style.left = `${left}px`;
shape.style.width = `${width}px`;
shape.style.height = `${height}px`;
}
showShape(shape);
#shape {
background-color: blue;
}
<div id="shape" />
Further documentation:
Template literals
It's called caching. Not only you will write less code, but also greatly improve performance.
const shapeElem = document.getElementById("shape");
function showShape(){
var top=Math.random()*370;
var left=Math.random()*350;
var width=Math.random()*300;
var height=Math.random()*300;
start = new Date().getTime();
shapeElem.style.display = "block";
shapeElem.style.top=top + "px";
shapeElem.style.left=left + "px";
shapeElem.style.width=width+"px";
shapeElem.style.height=height+"px";
}

trying to reach function inside a class javascript

Good evening, If someone could help me , I would be glad.I am trying to reach some functions in my Car class. Firstly I am trying to assign inpSpeed input value to Car class function Drive and then I wanna print out to console all cars info when I press the button: btnRace and the problem is I dont really know how to call them , because everytime I call them it says:"undefined".
here is my code so far:
carsArray = [];
btnCarName.onclick = function(){
carsArray.push({/*obj.element1, obj.element2, obj.element3*/});
}
btnRace.onclick = function(){
for(j in carsArray)
{
console.log(Car(carsArray[j]));
}
}
function Car(name,speed)
{
this.carBrand = name;
this.speed = speed;
this.distance = 0;
this.Drive = function(time)
{
if(time > 0)
return this.distance = (this.speed * (time/10));
}
this.printData = function()
{
for(var i = 0; i < Car.length; i++)
{
console.log('Car brand: ' + this.carBrand);
console.log('Speed: ' + this.speed);
console.log('distance: ' + this.Drive());
console.log('---------------------------');
}
}
}
For the this keyword to work, you must instantiate Car() with the new keyword:
var toyota = new Car('toyota', 100)
console.log(toyota.speed);
There may however be a couple other issues. What exactly is expected from Car.length?

Javascript gallery with two items at a time

I have this gallery that shows two items at a time, but I was wondering if there's a more elegant way than repeating the same code all the time for the item B in the gallery.
Here's the main code:
var Gallery = new Object();
window.onload = function(){
Gallery.Images = ['red','blue','pink','green','yellow','purple','orange','navy'];
Gallery.CurrentIndexA = 0;
Gallery.CurrentIndexB = 1;
};
Gallery.Next = function(){
if (Gallery.CurrentIndexA < (Gallery.Images.length-1)){
Gallery.CurrentIndexA++;
Gallery.CurrentIndexB++;
console.log("A is:" + Gallery.CurrentIndexA);
console.log("B is:" + Gallery.CurrentIndexB);
}
else {
Gallery.CurrentIndexA = 0;
}
Gallery.Display();
};
Gallery.Prev = function(){
if (Gallery.CurrentIndexA > 0){
Gallery.CurrentIndexA--;
Gallery.CurrentIndexB--;
console.log("A is:" + Gallery.CurrentIndexA);
console.log("B is:" + Gallery.CurrentIndexB);
}
else {
Gallery.CurrentIndexA = (Gallery.Images.length-1);
}
Gallery.Display();
};
Gallery.Display = function(){
var photoA = document.getElementById('photoA');
var photoB = document.getElementById('photoB');
var currentImageA = Gallery.Images[Gallery.CurrentIndexA];
var currentImageB = Gallery.Images[Gallery.CurrentIndexB];
photoA.className = currentImageA;
photoB.className = currentImageB;
};
Here's a fiddle: http://jsfiddle.net/2AdA9/1/
Thanks very much!
If you want to make it look better one thing you could do is something like this.
Gallery.moveUp = function (){
Gallery.CurrentIndexA += 1;
Gallery.CurrentIndexB += 1;
};
Gallery.moveDown = function (){
Gallery.CurrentIndexA -= 1;
Gallery.CurrentIndexB -= 1;
};
Then just call those functions when you want to move up or move back.

How can I make several Elements when extending a DIV-Element?

I am trying to "extend" a DIV via Javascript by using a newly created div as prototype of my object.
As I understand Javascript, on creating a new instance of my Object via "new", the prototype-object is copied, assigned to "this" an then the function is executed (as the constructor).
Everything seems to work, except that whenever I create another object, and add it to the DOM, it "replaces" the original div. To be more exact: The constructor always changes the same div.
Using MyTest.prototype = document.createElement("div"); gives me the described behavior, the two commented lines after that in my code example are what I also tried, but to no avail.
I know trying to extend the DOM is frowned upon, but I want to understand this behavior, because I thought I knew how prototypes work and this simply does not fit my idea.
Here is a minimal example of what I am trying to do:
<!DOCTYPE html>
<html>
<head>
<title>Div-Prototype-Test</title>
<script type="text/javascript">
var height = 20;
var top = 0;
function MyTest() {
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
this.style.backgroundColor = "rgb("+ r +","+ g +","+ b +")";
this.style.position = "absolute";
this.style.width = "500px";
this.style.height = height + "px";
this.style.top = top + "px";
top += height;
document.getElementsByTagName("body")[0].appendChild(this);
}
MyTest.prototype = document.createElement("div");
// MyTest.prototype = document.createElement("div").cloneNode(true);
// MyTest.prototype = new Element();
window.addEventListener(
"load",
function() {
var a = new MyTest();
var b = new MyTest();
var c = new MyTest();
var d = new MyTest();
}
);
</script>
</head>
<body>
</body>
</html>
PS: Because of a certain Javascript-Framework my search for anything that changes the prototype in Javascript always resulted in hundreds of results that had nothing to do with my problem - please tell me if I missed a question that already discusses this.
Edit:
To make my question clearer:
Here is an example where I use an object as prototype - its properties get copied.
function A() {
}
A.prototype = { property: 4 };
A.prototype.set = function(num) {
this.property = num;
}
window.addEventListener(
"load",
function() {
var message = "";
var x1 = new A();
message += "A1 : "+ x1.property +"\n";
x1.set(15);
message += "A1 : "+ x1.property +"\n";
var x2 = new A();
message += "A2 : "+ x2.property +"\n";
alert(message);
}
);
The alert then said:
A1 : 4
A1 : 15
A2 : 4
The Div in my first example however does not seem to be copied, it behaves like a Singleton or Monostate. Should it not go like this?
Protype object is copied into a new object
the new object is assigned to "this"
this is given to the constructor
this is returned by the constructor (if no return statement is specified)
MyTest.prototype = document.createElement("div");
This line is executed only once. It creates a MyTest.prototype object which is also a DOM element <div>. Every MyTest object will receive this same prototype. Therefore, every MyTest object you create will be associated with this single <div> you created only once. You will have to create a new <div> for every MyTest.
Try this pattern:
MyTest = function() {
var myDiv = document.createElement("div");
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
myDiv.style.backgroundColor = "rgb("+ r +","+ g +","+ b +")";
myDiv.style.position = "absolute";
myDiv.style.width = "500px";
myDiv.style.height = height + "px";
myDiv.style.top = top + "px";
top += height;
document.getElementsByTagName("body")[0].appendChild(myDiv);
return myDiv;
}
This function creates a new <div>, using the createElement() call. Then, it sets all the properties you want on that new <div>. Finally, it returns your new <div>. As such, you can call it as
var myNewDiv = MyTest();
var myNewDiv = new MyTest();
Both options would work. In the second case a dummy new object is created by the new keyword, but it doesn't matter, as the new <div> created inside the function is actually returned.
You are mixing all kind of things. First, check my answer to this SO question. Second, Extending the Element Object can be done, but is is not supported by all browsers. Check this SO question.
Seems to me that you are planning to add elements to the document in some standardized way. Your code could be rewritten to (I modified it a bit):
function appendElement(content,id) {
var rgb = 'rgb('+ [Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)].join(',') +')';
var top = Math.floor( Math.random() * 300 + 20 );
var left = Math.floor( Math.random() * 100 + 10 );
this.style.backgroundColor = rgb;
this.style.position = "absolute";
this.style.width = "500px";
this.style.height = "200px";
this.style.left = left+"px";
this.style.top = top+"px";
this.innerHTML = content || '';
this.id = id || Math.Random*10000+1
document.getElementsByTagName("body")[0].appendChild(this);
}
Now you can use this to append any element to the document using `appendElement as follows:
appendElement.call(document.createElement('div'),'empty div');
appendElement.call(document.createElement('span'),'new empty span','span1');
Would that be an idea for what you aim at?
I found a workaround, it basically works the other way round - the prototype is a blank object and I copy the new objects data into a div in the constructor:
var height = 20;
var top = 0;
function deepCopy(fromObject, toObject, depth) {
if(typeof(fromObject) != "object" || typeof(toObject) != "object") {
// throw "deepCopy only copies objects"
return;
}
if (typeof(depth) == "undefined") {
depth = 0;
} else if (depth > 100) {
// Recursion depth too high. Abort.
// throw "deepCopy recursion depth cap hit"
return;
}
for (var key in fromObject) {
if (typeof(fromObject[key]) == "object" && fromObject[key] != null) {
if (typeof(fromObject[key].nodeType) != "undefined") {
toObject[key] = fromObject[key].cloneNode(true);
} else {
if (typeof(toObject[key]) != "object") {
toObject[key] = {};
}
deepCopy(fromObject[key], toObject[key], depth + 1);
}
}
toObject[key] = fromObject[key];
}
}
function MyTest() {
// This is ugly...
var self = document.createElement("div");
deepCopy(MyTest.prototype, self);
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
self.style.backgroundColor = "rgb("+ r +","+ g +","+ b +")";
self.style.position = "absolute";
self.style.width = "500px";
self.style.height = height + "px";
self.style.top = top + "px";
top += height;
document.getElementsByTagName("body")[0].appendChild(self);
return self;
}
MyTest.prototype = {};
// MyTest.prototype = document.createElement("div").cloneNode(true);
// MyTest.prototype = new Element();
window.addEventListener(
"load",
function() {
var a = new MyTest();
var b = new MyTest();
var c = new MyTest();
var d = new MyTest();
}
);
Although I have the feeling that my deepCopy-function is a rather inelegant (and possibly very buggy) way to perform the task, but the other way round with using cloneNode() did not work.
My original problem came from this: When the prototype is copied, all scalar values are copied, while all objects are simply referenced (like copying pointers, the pointer value is duplicated, but not the data it points to).
Hope this helps someone.

Categories