Scope issue with a sequence of fadeIn's - javascript

I the following code I have a UL with x3 LI's. I want the LI's to fadeIn in a sequence but am loosing scope somewhere I think. The problem is that only the last item in the sequence is run. I initially thought this was to do with a loop, so I removed all of them. Any help would be great.
Thanks is Advance.
function Sequence() {
var sequence = [];
var pos = 0;
Sequence.prototype.add = function(obj) {
sequence.push(obj);
};
Sequence.prototype.start = function() {
sequence[pos].run();
};
Sequence.prototype.next = function() {
pos++;
sequence[pos].run();
};
};
function fadeIn(params) {
this.id = params.id;
this.onComplete = params.onComplete;
var self = this;
var timer;
var i = params.opacity;
fadeIn.prototype.run = function(){
timer = setInterval(function() {
params.element.style.opacity = i / 10;
i++;
if (i / 10 == 1) {
clearInterval(timer);
self.onComplete();
}
}, params.fps);
}
};
var sequence = new Sequence();
var fader = document.getElementById('fader1');
var items = fader.getElementsByTagName("li");
sequence.add(new fadeIn({
"id": "instance_0",
"element": items[0],
"opacity": 0,
"fps": 80,
"onComplete": function() {
sequence.next();
}
}));
sequence.add(new fadeIn({
"id": "instance_1",
"element": items[1],
"opacity": 0,
"fps": 80,
"onComplete": function() {
sequence.next();
}
}));
sequence.start();

Yes, this is a scope issue. The problem is in the line:
fadeIn.prototype.run = function(){
When you define a method on the prototype, you're defining the method on all instances of the fadeIn class. So each time you call the constructor, you're redefining the method with the new params in the closure.
The solution is to define the method on this (or, as you've renamed it, self), which is the new instance, rather than the class:
self.run = function(){
Working example here: http://jsfiddle.net/nrabinowitz/wrQMa/3/

Related

How to use Mina in Snap svg?

This is an animation code using Snap svg:
var s = Snap(3000,3000);
var circle7 = s.circle(130,90,5);
var circle8 = s.circle(155,90,5);
var circle9 = s.circle(180,90,5);
var circle10 = s.circle(205,90,5);
var circle11 = s.circle(230,90,5);
var circle12 = s.circle(255,90,5);
circle.attr({fill:"#ffffff",opacity:0});
circle1.attr({fill:"#ffffff",opacity:0});
circle2.attr({fill:"#ffffff",opacity:0});
circle3.attr({fill:"#ffffff",opacity:0});
circle4.attr({fill:"#ffffff",opacity:0});
circle5.attr({fill:"#ffffff",opacity:0});
circle6.attr({fill:"#ffffff",opacity:0});
circle7.attr({fill:"#ffffff",opacity:0});
circle8.attr({fill:"#ffffff",opacity:0});
circle9.attr({fill:"#ffffff",opacity:0});
circle10.attr({fill:"#ffffff",opacity:0});
circle11.attr({fill:"#ffffff",opacity:0});
circle12.attr({fill:"#ffffff",opacity:0});
circle.animate({fill:"#0000FF",opacity:1},4500);
circle1.animate({fill:"#0000FF",opacity:1},4000);
circle2.animate({fill:"#0000FF",opacity:1},3500);
circle3.animate({fill:"#0000FF",opacity:1},3000);
circle4.animate({fill:"#0000FF",opacity:1},2500);
circle5.animate({fill:"#0000FF",opacity:1},2000);
circle6.animate({fill:"#0000FF",opacity:1},1500);
circle7.animate({fill:"#0000FF",opacity:1},4500);
circle8.animate({fill:"#0000FF",opacity:1},5000);
circle9.animate({fill:"#0000FF",opacity:1},5500);
circle10.animate({fill:"#0000FF",opacity:1},6000);
circle11.animate({fill:"#0000FF",opacity:1},6500);
circle12.animate({fill:"#0000FF",opacity:1},7000);
var cloud = s.image("D:/DigiMKey/login page_files/cloud.png", 0 , 260, 180,
125);
cloud.attr({opacity:0,width:100,height:65});
cloud.animate({opacity:1,width:200,height:125},1000)
var school =s.image("D:/DigiMKey/images/School-Icon.png", 265, 50, 100,100);
school.attr({opacity:0,width:80,height:80});
school.animate({opacity:1,width:100,height:100},2000)
I want to animate these objects using Mina so that they can be executed one after another.
Objects should fade in one by one.
how to do that?
This code gives no errors, however they all come at once instead of one after another.
There is a callback in animate function, use it to call the next animation.
var
s = Snap(300,300),
circle7 = s.circle(130,90,5).attr({fill:"#fff",opacity:0}),
circle8 = s.circle(155,90,5).attr({fill:"#fff",opacity:0}),
circle9 = s.circle(180,90,5).attr({fill:"#fff",opacity:0}),
circle10 = s.circle(205,90,5).attr({fill:"#fff",opacity:0}),
circle11 = s.circle(230,90,5).attr({fill:"#fff",opacity:0}),
circle12 = s.circle(255,90,5).attr({fill:"#fff",opacity:0}),
duration = 500,
props = {fill:"#00f",opacity:1};
/*
var anim = function() { circle.animate({fill:"#00f",opacity:1}, duration,mina.linear,anim1);}
var anim1 = function() { circle1.animate({fill:"#00f",opacity:1},duration,mina.linear,anim2);}
var anim2 = function() { circle2.animate({fill:"#00f",opacity:1},duration,mina.linear,anim3);}
var anim3 = function() { circle3.animate({fill:"#00f",opacity:1},duration,mina.linear,anim4);}
var anim4 = function() { circle4.animate({fill:"#00f",opacity:1},duration,mina.linear,anim5);}
var anim5 = function() { circle5.animate({fill:"#00f",opacity:1},duration,mina.linear,anim6);}
var anim6 = function() { circle6.animate({fill:"#00f",opacity:1},duration,mina.linear,anim7);}
*/
var anim7 = function() { circle7.animate(props,duration,mina.linear,anim8);}
var anim8 = function() { circle8.animate(props,duration,mina.linear,anim9);}
var anim9 = function() { circle9.animate(props,duration,mina.linear,anim10);}
var anim10= function() { circle10.animate(props,duration,mina.linear,anim11);}
var anim11= function() { circle11.animate(props,duration,mina.linear,anim12);}
var anim12= function() { circle12.animate(props,duration,mina.linear);};
anim7();
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>
There is nothing there that makes them animate in a sequence.
The way to animate in a sequence is to use a callback, which is part of the animate() method. So you can do...
circle1.animate({ fill:"#0000FF" }, 1000, mina.linear, callbackFunc2 );
function callbackFunc2() {
circle2.animate({ fill:"#0000FF" }, 1000, mina.linear, callbackFunc3 );
}
function callbackFunc3()...
And so on. Naturally that can feel a bit clunky when you have a lot, so I find it useful to add a sequence plugin to help. For example here
function nextFrame ( el, frameArray, whichFrame ) {
if( whichFrame >= frameArray.length ) { return }
el.animate( frameArray[ whichFrame ].animation, frameArray[ whichFrame ].dur, nextFrame.bind( null, el, frameArray, whichFrame + 1 ) );
}
I also extended it a bit and suggested it here (see the jsfiddle) which may be of use, if you're doing a lot of them.

Using a Passed Dot-Notated Parameter as Property Accessor within a Function

I'm fairly certain this is a simple mistake, but I'm confused as to why this won't work. I've been unable to find any documentation on using multiple brackets in a row when accessing properties. This is part of my code that creates a link that when clicked runs the "dropItem" function:
var dropDest = "screwdriver";
var dropCat = "supplies.tools";
dropButton.onclick= function() {dropItem(dropCat, dropDest)};
The "dropItem" function:
var dropItem = function(category, droppedItem) {
player.inventory[category][droppedItem].amount = player.inventory[category][droppedItem].amount - 1; };
I get a "Cannot read property 'screwdriver' of undefined" error. How would one properly pass this? Both properties need to be variables for it to work. Is this an error due to multiple brackets?
EDIT:
I realized I forgot to include how the player object is structured, and this is critical for understanding why I needed to pass a dot-notated category:
var player = {
"health": 100,
"hydration": 100,
"hunger": 100,
"energy": 100,
"inventory": {
"supplies": {
"tools": {
"screwdriver": {
"amount": 0,
"condition": 0
}
},
"buldingMaterials": {
"wood": {
"amount": 0,
"condition": 0
}
}
},
"aidPacks": {
"healthPacks": 0
}
}
}
This is why I need to be able to access both player.inventory.supplies.tools and player.inventory.aidPacks.healthpacks with the same function.
You can make your code more generic and easier. This is useful when you have more than two categories - then you dont need to work with [0] and 1 - here is the code:
var player = { "inventory": { "supplies": { "tools" : { "screwdriver" : { "amount" : 1}}}}};
var dropDest = "screwdriver";
var dropCat = "supplies.tools";
function getProperty(obj, str) {
return str.split(".").reduce(function(o, x) { return o[x] }, obj);
}
function dropItem(category, droppedItem) {
var accessString = dropCat + "." + droppedItem;
getProperty(player.inventory, accessString).amount -= 1;
alert(getProperty(player.inventory, accessString).amount);
}
dropItem(dropCat, dropDest);
Here is the fiddle: https://jsfiddle.net/j7g0d8kz/3/
After looking at lightstalker89's code I realized that my issue was not the brackets, but rather the category that was being passed was not parsing the dot notation in the middle, thus staying as "supplies.tools" instead of separating fully. However, due to the way the player object is structured I needed to be able to pass dot notated categories, so I split the string:
var player = { "inventory": { "supplies": { "tools" : { "screwdriver" : { "amount" : 1}}}}};
var dropDest = "screwdriver";
var dropCat = "supplies.tools";
function dropItem(category, droppedItem) {
if(category.indexOf(".") > 0) {
var newCat = category.split(".");
var cat1 = newCat[0];
var cat2 = newCat[1];
player.inventory[cat1][cat2][droppedItem].amount = player.inventory[cat1][cat2][droppedItem].amount - 1;
alert(player.inventory[cat1][cat2][droppedItem].amount);
}else {
player.inventory[category][droppedItem].amount = player.inventory[category][droppedItem].amount - 1;
alert(player.inventory[category][droppedItem].amount);
}
}
dropItem(dropCat, dropDest);
Here's the JSFiddle: https://jsfiddle.net/j7g0d8kz/
Again, huge thanks to #lightstalker89 for making me realise my mistake.
How does your player object look like? Is it an empty object when you want to change the properties.
Basically you can use multiple brackets as you did.
If the player object does not have the needed properties, then your code should look like this:
var player = {};
var dropDest = "screwdriver";
var dropCat = "supplies.tools";
function dropItem(category, droppedItem) {
if(!player.inventory){
player.inventory = {};
}
if(!player.inventory[category]){
player.inventory[category] = {};
}
if(!player.inventory[category][droppedItem]){
player.inventory[category][droppedItem] = { amount: 0};
}
player.inventory[category][droppedItem].amount = player.inventory[category][droppedItem].amount - 1;
}
dropItem(dropCat, dropDest);
alert(JSON.stringify(player));
alert(player.inventory[dropCat][dropDest].amount);
Here is the fiddle: https://jsfiddle.net/csqbjnd7/1/
If the player object exists you can use multiple brackets. The code should then look like this:
var player = { "inventory": { "supplies.tools" : { "screwdriver" : { "amount" : 1}}}};
var dropDest = "screwdriver";
var dropCat = "supplies.tools";
function dropItem(category, droppedItem) {
player.inventory[category][droppedItem].amount = player.inventory[category][droppedItem].amount - 1;
}
dropItem(dropCat, dropDest);
alert(player.inventory[dropCat][dropDest].amount);
alert(JSON.stringify(player));
Here is the fiddle: https://jsfiddle.net/9o1949oz/2/

Using tween on class object, function definiton?

I am trying to do scene effects opening and closing scenes. But something is wrong with my self and this values. It says not defined or not working. How can ı define these functions.
Ways I tried to define
First way:
var tween = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(menuOutCompleted(nextScreen,isNextScreenPopUp));
It is throwing "menuOutCompleted is not defined";
Second way:
var tween = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(self.menuOutCompleted(nextScreen,isNextScreenPopUp));
It is not throwing any exception but does not do tween. It directly execute menuOutCompleted.
Third way:
var tween = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(this.menuOutCompleted(nextScreen,isNextScreenPopUp));
It works like second way.
My js class
function CreditsScreen(SceneContainer)
{
var closePopUp_Button, background;
this.name = "CreditsScreen";
var self = this;
this.scene = new createjs.Container();
this.loadScreen = function()
{
background = new createjs.Shape();
background.graphics.beginBitmapFill(loader.getResult("coronaLogo")).drawRect(0,0,512,512);
background.x = 200;
background.y = 0;
closePopUp_Button = new createjs.Sprite(buttonData, "exitIdle");
closePopUp_Button.framerate = 30;
closePopUp_Button.x = 400;
closePopUp_Button.y = 22;
// play.addEventListener("click", handleClickPlay);
this.scene.alpha = 0;
this.scene.addChild(background);
this.scene.addChild(closePopUp_Button);
}
this.menuIn = function()
{
console.log("menuIn CreditsScreen" );
stage.addChild(this.scene);
//ptoblemetic part with self.menuInCompleted?
var tween = createjs.Tween.get(this.scene).to({y : 0, x : 0, alpha : 1}, 5000).call(self.menuInCompleted);
}
this.menuInCompleted = function()
{
console.log("menuInCompleted CreditsScreen" );
self.addButtonEventListeners();
}
this.menuOut = function(nextScreen,isNextScreenPopUp)
{
console.log("menuOut CreditsScreen" );
self.removeButtonEventListeners();
if(isNextScreenPopUp == true)
{
self.menuOutCompleted(nextScreen,isNextScreenPopUp);
}
else
{
//problematic part with menuInCompleted?
var tweenSplash = createjs.Tween.get(this.scene).to({alpha : 0}, 5000).call(menuOutCompleted(nextScreen,isNextScreenPopUp));
}
}
this.menuOutCompleted = function(nextScreen,isNextScreenPopUp)
{
console.log("menuOutCompleted CreditsScreen" );
if (isNextScreenPopUp)
{
}
else
{
stage.removeChild(this.scene);
this.scene.x = 0;
this.scene.y = 0;
this.scene.alpha = 1;
}
changeMenu(nextScreen, this.name, isNextScreenPopUp, true);
}
Ok. I solved the problem. Itis my call function. I send the parameters like menuoutcompleted(a,b) but in tween structure it must be (menuoutCompleted,[a,b]).
Now, It works :)

objects in an array alerted randomly

I have built this code using javascript that makes a few objects called monster. I then put those monsters in an array and finally am trying to call one of thous monsters to the console randomly. Unfortunately it displays in my console log as undefined. Any advice on how to get a random monster in the console log every time I refresh the page?
function Monster(type, level, mAttack, mAgility, mHP) {
this.type = type;
this.level = level;
this.mAttack = mAttack;
this.mAgility = mAgility;
this.mHP = mHP;
}
Monster.prototype.logInfo = function() {
console.log("I am a : ", this.type);
console.log("I am level : ", this.level);
console.log("I have the attack of : ", this.mAttack);
console.log("I have the agility : ", this.mAgility);
console.log("I have the health : ", this.mHP);
}
var troll = new Monster("troll", 1, 10, 10, 10);
var skeleton = new Monster("skeleton", 1, 10, 10, 10);
var slime = new Monster("slime", 1, 10, 10);
var boar = new Monster("boat", 1, 10, 10);
var monsterList = new Array();
monsterList[0] = troll;
monsterList[1] = skeleton;
monsterList[3] = slime;
monsterList[4] = boar;
var summonRandomMonster = function (){
monsterSummoner = monsterList[Math.floor(Math.random() * monsterList.length)];
}
console.log(monsterSummoner);
You create a function but never call it. Therefor monsterSummoner is never set.
// THIS IS NEVER CALLED
var summonRandomMonster = function (){
monsterSummoner = monsterList[Math.floor(Math.random() * monsterList.length)];
}
console.log(monsterSummoner);
Try this instead. Notice that now that the function is called the value is set.
var monsterSummoner;
var summonRandomMonster = function (){
monsterSummoner = monsterList[Math.floor(Math.random() * monsterList.length)];
}
summonRandomMonster();
console.log(monsterSummoner);
You're close... Change your last four lines as follows -
var monsterList = [troll,skeleton,slime,boar];
var summonRandomMonster = function (){
monsterSummoner = monsterList[Math.floor(Math.random() * monsterList.length)];
console.log(monsterSummoner);
}
for (var i = 0; i < 100; i++) {
summonRandomMonster();
}
There are atleast two problems in this code. The variable "monsterSummoner" is not defined, and the function "summonRandomMonster" is not called.

Javascript function works for a while, then freezes the browser

I'm using this JS code to make a banner:
var images = ["../../images/g11.jpg","../../images/g9.jpg","../../images/g10.jpg"];
var titulos = ["title1","title2","title3"];
var resumos = ["ddd","aaa","bbb"];
var noticias = ["190","204","200"];
var total = 3;
var indice = 0;
function rotate() {
document.getElementById('imageb').src = images[indice];
document.getElementById('titulob').innerHTML = titulos[indice];
document.getElementById('resumob').innerHTML = resumos[indice];
document.getElementById('noticiab').value = noticias[indice];
indice++;
if (indice > total - 1) indice = 0;
}
function banner() {
rotate();
setTimeout(banner, 5000);
}
It works how expected, but after some loops it freezes the browser. Pretty sure I'm not using setTimeout properly. Any ideas?
Edit:
Working so far:
function rotate(indice) {
document.getElementById('imageb').src = images[indice];
document.getElementById('titulob').innerHTML = titulos[indice];
document.getElementById('resumob').innerHTML = resumos[indice];
document.getElementById('noticiab').value = noticias[indice];
}
function banner(indice) {
var f1 = function() { banner(indice); };
var total = 3;
rotate(indice);
indice++;
if (indice > total - 1) indice = 0;
setTimeout(f1, 5000);
}
I'm posting this as a CW because it's a total guess.
Completely FWIW, here's how I'd minimally change that code: Live Copy | Live Source
(function() {
var entries = [
{
img: "../../images/g11.jpg",
titulo: "title1",
resumo: "ddd",
noticia: "190"
},
{
img: "../../images/g9.jpg",
titulo: "title2",
resumo: "aaa",
noticia: "204"
},
{
img: "../../images/g10.jpg",
titulo: "title3",
resumo: "bbb",
noticia: "200"
}
];
var indice = 0;
function rotate() {
var entry = entries[indice];
document.getElementById('imageb').src = entry.img;
document.getElementById('titulob').innerHTML = entry.titulo;
document.getElementById('resumob').innerHTML = entry.resumo;
document.getElementById('noticiab').value = entry.noticia;
indice = (indice + 1) % data.length;
}
function banner() {
rotate();
setTimeout(banner, 5000);
}
banner();
})();
Changes:
Put everything in a scoping function to avoid creating global variables.
Use an array of objects rather than parallel arrays.
Use the array's length rather than a separate total variable.
Use the remainder trick for getting the wrap-around on the indice variable.
I added a call to banner(); at the end to get things started, but I assume you have that and just didn't show it.
But again, I don't see any reason your code shouldn't be working as is, other than the possibility of some weird global variable conflict.

Categories

Resources