Javascript object property check if returned true - javascript

Using a library called Velocity JS for animating: http://julian.com/research/velocity/
I am using it as follows:
var velocity = new Velocity(element, {
translateX: 250,
complete: function() {
return true;
}
}, 5);
What I am trying to do is check if complete has returned true on successful completion of the animation so that I can toggle the animation.
What is the best method for checking if it is true
if(velocity.complete() === true)
This returns undefined.
Any help is appreciated thanks.

If you need to store the fact that the animation has completed, in order to read that information later based on user interaction (e.g., clicking a button), simply set a variable in an outer scope:
var animationComplete = false;
var velocity = new Velocity(element, {
translateX: 250,
complete: function() {
animationComplete = true;
}
}, 5);
And read it when the user interaction happens:
$("#someButton").on("click", function() {
if(animationComplete) {
// do something only if the animation has completed
}
});
It does not matter where you store that value (as a local or global variable, as a property on an object, etc.), as long as it's visible to the functions that need to read and set it.
Note that the return value of the complete callback is never used. The complete callback function object is passed into the Velocity constructor, and it is called later from inside of velocity.js's own code somewhere. Velocity.js probably does not expose (or even store) this return value anywhere.

You already pass the complete function, but returning true is not so helpful in this case. Start the second animation when the complete function is called:
var velocity = new Velocity(element, {
translateX: 250,
complete: function() {
// start another animation
}
}, 5);
If you need to toggle animation on click or another event you have to store animation boolean outside somewhere, even in the dom element or using jQuery data method:
var $velocity = $("#velocity");
$velocity.data("animating", false);
$velocity.data("direction", "left");
$velocity.on("click", function () {
if ($velocity.data("animating")) {
return;
}
$velocity.data("animating", true);
if ($velocity.data("direction") === "left") {
var ml = -30;
$velocity.data("direction", "right");
} else {
var ml = 30;
$velocity.data("direction", "left");
}
$velocity.velocity({marginLeft:ml},{
duration:500,
complete: function () {
$velocity.data("animating", false);
}
});
});
JSFIDDLE

Related

accessing event listener data across functions in a component

I am trying to access data obtained from an event listener function in init() in another function in an aframe component. I have tried to use binding so that the data obtained in the event listener can be bound to "this" space.
Here is my code
AFRAME.registerComponent('move', {
schema: {
},
init: function() {
this.el.sceneEl.addEventListener('gameStarted', testfun.bind(this), {once:
true});
function testfun(e){
this.speed = e.detail.source;
console.log(this.speed); // I get proper values here
}
console.log(this.speed); // value is not updated and I only get "undefined"
},
tick: function(t, dt) {
console.log(this.speed); // I get undefined
}
});
I thought if I bind this to function, I can access the data outside the even listener scope as well. Could you please spare a little time to help me figure this out?
The reason is that the variable (speed) you are modifying is out of scope. Since you have declared a new function testfun with its own properties in function init.
If you can use ES5+ syntax then you can declare testfun as an arrow function instead and you are done.
For more read about here: https://zendev.com/2018/10/01/javascript-arrow-functions-how-why-when.html
try this:
AFRAME.registerComponent("move", {
schema: {},
init: function() {
this.speed = 0;
this.el.sceneEl.addEventListener("gameStarted", testfun, {
once: true
});
const testfun = e => {
this.speed = e.detail.source;
console.log(this.speed); // I get proper values here
};
console.log(this.speed); // value is not updated and I only get "undefined"
},
tick: function(t, dt) {
console.log(this.speed); // I get undefined
}
});
That’s expected behavior. You have probably not realized that events will fire at any arbitrary time, after your console.log calls. By the time init runs this.speed is not yet initialized. You have to wait until gameStarted event fires to get a value. The same goes for tick before the event fires. Give this.speed an initial value to avoid undefined
AFRAME.registerComponent('move', {
schema: {
},
init: function() {
var self = this;
this.speed = 0;
this.el.sceneEl.addEventListener('gameStarted', testfun.bind(this), {once:
true});
function testfun(e){
self.speed = e.detail.source;
console.log(self.speed); // I get proper values here
}
console.log(this.speed); // value is not updated and I only get "undefined"
},
tick: function(t, dt) {
console.log(this.speed); // I get undefined
}

Javascript recursive call creates range-error

I'm trying to get the following script working, but when it falls into the continueAnimation function it isn't updating the cPreloaderTimeout variable and it runs in a 'Uncaught RangeError: Maximum call stack size exceeded'.
var loadingIcon = {
cSpeed : 8,
cWidth : 64,
cHeight : 32,
cTotalFrames : 7,
cFrameWidth : 64,
cImageSrc : 'sprites.png',
cImageTimeout : false,
cIndex : 0,
cXpos : 0,
cPreloaderTimeout : false,
SECONDS_BETWEEN_FRAMES : 0,
startAnimation : function() {
document.getElementById('loaderImage').style.backgroundImage='url('+ this.cImageSrc+')';
document.getElementById('loaderImage').style.width=this.cWidth+'px';
document.getElementById('loaderImage').style.height=this.cHeight+'px';
//FPS = Math.round(100/(maxSpeed+2-speed));
FPS = Math.round(100/this.cSpeed);
SECONDS_BETWEEN_FRAMES = 1 / FPS;
this.cPreloaderTimeout = setTimeout( this.continueAnimation(), SECONDS_BETWEEN_FRAMES/1000);
},
continueAnimation : function() {
this.cXpos += this.cFrameWidth;
//increase the index so we know which frame of our animation we are currently on
this.cIndex += 1;
//if our cIndex is higher than our total number of frames, we're at the end and should restart
if (this.cIndex >= this.cTotalFrames) {
this.cXpos =0;
this.cIndex=0;
}
if(document.getElementById('loaderImage'))
document.getElementById('loaderImage').style.backgroundPosition=(-this.cXpos)+'px 0';
this.cPreloaderTimeout = setTimeout(this.continueAnimation(), SECONDS_BETWEEN_FRAMES*1000);
},
stopAnimation : function(){//stops animation
clearTimeout( this.cPreloaderTimeout );
this.cPreloaderTimeout = false;
}
}
jQuery( document ).ready(function() {
jQuery( document ).on("click", "#test", function(){
var loader = loadingIcon;
loader.startAnimation();
setTimeout( loader.stopAnimation(), 3000);
});
});
It was a plain javascript script at first, but I'm trying to make an object from it so it can be re-used and multiple times at the same time. The problem is now that the cPreloaderTimeout variable isn't set correctly when startAnimation or continueAnimation is triggerd.
You have a couple issues.
First, your cPreloaderTimeout isn't going to be set like you think it is, as you're not creating an object with a prototype, so the scope of this inside that function is going to be the function itself, not the object.
Second, setTimeout takes a function, but you're calling the function when you try and use it, so the value sent to setTimeout will be the results of the function, not the function itself.
Consider instead the format:
function LoadIcon() {
this.cSpeed = 8;
// All your other properties
}
LoadIcon.prototype.startAnimation = function() {
// your startAnimation function, concluding with
this.preloaderTimeout = setTimeout(this.continueAnimation.bind(this), SECONDS_BETWEEN_FRAMES/1000);
}
// the rest of the methods built the same way
//then, your calling code would look like:
var loadIcon = new LoadIcon();
loadIcon.startAnimation();
EDIT
I updated the setTimeout call as I'd forgotten about binding to this for correct scoping when the callback fires.

bacon.js: Strange behaviour with holdWhen and onValue

Take a look at the working CodePen here: http://codepen.io/djskinner/pen/JdpwyY
// Animation start events push here
var startBus = new Bacon.Bus();
// Animation end events push here
var endBus = new Bacon.Bus();
// Balance updates push here
var balanceBus = new Bacon.Bus();
// A Property that determines if animating or not
var isAnimating = Bacon.update(false,
[startBus], function() { return true; },
[endBus], function() { return false; }
);
// Only update the displayBalance when not animating
var displayBalance = Bacon.update(0,
[balanceBus.holdWhen(isAnimating)], function(previous, x) {
return x;
}
);
setTimeout(function() {
var streamTemplate = Bacon.combineTemplate({
balance: displayBalance
});
// Uncommenting this block changes the way the system behaves
// streamTemplate.onValue(function(initialState) {
// console.log(initialState);
//})();
// Print the displayBalance
streamTemplate.onValue(function(v) {
console.log(v.balance);
});
});
Pressing the balance button generates a new random number. A Property is created that uses holdWhen to restrict balance updates coming through until the isAnimating Property becomes false.
If I was interested in getting the initial state of streamTemplate, I might get the value and immediately unsubscribe:
streamTemplate.onValue(function(initialState) {
console.log(initialState);
})();
However, once I do this the displayBalance Property behaves differently and I no longer receive updates.
Why would this seemingly inert change make such a drastic different to the system? Surely the behaviour of the system shouldn't be dependent on whether someone has subscribe and unsubscribed to the streamTemplate at some point in the past?
This behaviour has been confirmed as a bug that has been fixed in 0.7.67.
See here for details.

Write a wrapper object in Javascript

First off, let me apologize if my question isn't worded correctly - I'm not a professional coder so my terminology might be weird. I hope my code isn't too embarrassing :(
I have a fade() method that fades an image in and out with a mouse rollover. I would like to use a wrapper object (I think this is the correct term), to hold the image element and a few required properties, but I don't know how to accomplish this. fade() is called from the HTML, and is designed to be dropped into a page without much additional setup (so that I can easily add new fading images to any HTML), just like this:
<div id="obj" onmouseover="fade('obj', 1);" onmouseout="fade('obj', 0);">
The fade(obj, flag) method starts a SetInterval that fades the image in, and when the pointer is moved away, the interval is cleared and a new SetInterval is created to fade the image out. In order to save the opacity state, I've added a few properties to the object: obj.opacity, obj.upTimer, and obj.dnTimer.
Everything works okay, but I don't like the idea of adding properties to HTML elements, because it might lead to a future situation where some other method overwrites those properties. Ideally, I think there should be a wrapper object involved, but I don't know how to accomplish this cleanly without adding code to create the object when the page loads. If anyone has any suggestions, I would greatly appreciate it!
Here's my fader method:
var DELTA = 0.05;
function fade(id, flag) {
var element = document.getElementById(id);
var setCmd = "newOpacity('" + id + "', " + flag + ")";
if (!element.upTimer) {
element.upTimer = "";
element.dnTimer = "";
}
if (flag) {
clearInterval(element.dnTimer);
element.upTimer = window.setInterval(setCmd, 10);
} else {
clearInterval(element.upTimer);
element.dnTimer = window.setInterval(setCmd, 10);
}
}
function newOpacity(id, flag) {
var element = document.getElementById(id);
if (!element.opacity) {
element.opacity = 0;
element.modifier = DELTA;
}
if (flag) {
clearInterval(element.dnTimer)
element.opacity += element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity > 100) {
element.opacity = 100;
element.modifier = DELTA;
return;
}
element.opacity = Math.ceil(element.opacity);
} else {
clearInterval(element.upTimer)
element.opacity -= element.modifier;
element.modifier += DELTA; // element.modifier increases to speed up fade
if (element.opacity < 0) {
element.opacity = 0;
element.modifier = DELTA;
return;
}
element.opacity =
Math.floor(element.opacity);
}
setStyle(id);
}
function setStyle(id) {
var opacity = document.getElementById(id).opacity;
with (document.getElementById(id)) {
style.opacity = (opacity / 100);
style.MozOpacity = (opacity / 100);
style.KhtmlOpacity = (opacity / 100);
style.filter = "alpha(opacity=" + opacity + ")";
}
}
You are right, adding the handlers in your HTML is not good. You also loose the possible to have several handlers for event attached to one object.
Unfortunately Microsoft goes its own way regarding attaching event handlers. But you should be able to write a small wrapper function to take care of that.
For the details, I suggest you read quirksmode.org - Advanced event registration models.
An example for W3C compatible browsers (which IE is not): Instead of adding your event handler in the HTML, get a reference to the element and call addEventListener:
var obj = document.getElementById('obj');
obj.addEventListener('mouseover', function(event) {
fade(event.currentTarget, 1);
}, false);
obj.addEventListener('mouseout', function(event) {
fade(event.currentTarget, 0);
}, false);
As you can see I'm passing directly a reference to the object, so in you fade method you already have a reference to the object.
You could wrap this in a function that accepts an ID (or reference) and every time you want to attach an event handler to a certain element, you can just pass the ID (or reference) to this function.
If you want to make your code reusable, I suggest to put everything into an object, like this:
var Fader = (function() {
var DELTA = 0.05;
function newOpacity() {}
function setStyle() {}
return {
fade: function(...) {...},
init: function(element) {
var that = this;
element.addEventListener('mouseover', function(event) {
that.fade(event.currentTarget, 1);
}, false);
element.addEventListener('mouseout', function(event) {
that.fade(event.currentTarget, 0);
}, false);
}
};
}())
Using an object to hold your functions reduces pollution of the global namespace.
Then you could call it with:
Fader.init(document.getElementById('obj'));
Explanation of the above code:
We have an immediate function (function(){...}()) which means, the function gets defined and executed (()) in one go. This function returns an object (return {...};, {..} is the object literal notation) which has the properties init and fade. Both properties hold functions that have access to all the variables defined inside the immediate function (they are closures). That means they can access newOpacity and setStyle which are not accessible from the outside. The returned object is assigned to the Fader variable.
This doesn't directly answer your question but you could use the jQuery library. It's simple, all you have to do is add a script tag at the top:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js">
Then your div would look like:
<div id="obj" onmouseover="$('#obj').fadeIn()" onmouseout="$('#obj').fadeOut()">
jQuery will handle all the browser dependencies for you so you don't have to worry about things like differences between firefox and mozilla etc...
If you want to keep your HTML clean, you should consider using JQuery to set up the events.
Your HTML will look like this:-
<div id="obj">
Your JavaScript will look "something" like this:-
$(document).ready(function() {
$("#obj").mouseover(function() {
Page.fade(this, 1);
}).mouseout(function(){
Page.fade(this, 0);
});
});
var Page = new function () {
// private-scoped variable
var DELTA = 0.05;
// public-scoped function
this.fade = function(divObj, flag) {
...
};
// private-scoped function
var newOpacity = function (divObj, flag) {
...
};
// private-scoped function
var setStyle = function (divObj) {
...
};
};
I introduced some scoping concept in your Javascript to ensure you are not going to have function overriding problems.

Open social viewer state (isOwner)

We are creating a gadget for the opensocial API 0.7.
In some functions we have to decide, if the viewer is the owner.
We couldn't use the usual function for this purpose:
return gadgets.util.getUrlParameters().viewer == gadgets.util.getUrlParameters().owner;
so we had to create a workaround and get the information via a DataRequest.
The DataRequest calls a callback function and has no useable return value.
We tried a quick hack by using global variables to set the corresponding value.
The issue at this point is, that the function does not 'wait' for the callback-function to be finished. We know this is no good code/style at all, but we tried to force a timeout for debug reasons.
Handling all the code within the callback-function (as suggested in the examples of the opensocial docs) is not possible.
We are looking for something like a real 'sleep()' in JavaScript to wait for the callback-function to complete or another alternative to get the owner information about the viewer.
globalWorkaroundIsOwner = false;
function show_teaser(){
if (current_user_is_owner()){
// ...
}
// ...
}
function current_user_is_owner() {
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), 'viewer');
// This will set the the correct value
req.send( user_is_owner_workaround );
// This is an attempt to delay the return of the value.
// An alert() at this point delays the return as wanted.
window.setTimeout("empty()", 2000);
// This return seems to be called too early (the variable is false)
return globalWorkaroundIsOwner;
}
function user_is_owner_workaround(dataResponse) {
var viewer = dataResponse.get('viewer').getData();
globalWorkaroundIsOwner = viewer.isOwner();
// value is correct at this point
}
Can you use an additional flag in order to indicate whether the remote query has already returned the required value?
var globalWorkaroundIsOwner = false;
var workaroundStarted = false, workAroundComplete = false;
var checker;
function show_teaser(){
if (!workaroundStarted) {
workaroundStarted = true;
current_user_is_owner();
}
if (workaroundComplete) {
if (globalWorkaroundIsOwner){
// ...
}
// ...
if (checker) {
clearInterval(checker);
}
}
}
function current_user_is_owner() {
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), 'viewer');
checker = setInterval("show_teaser()", 1000);
// This will set the the correct value
req.send( user_is_owner_workaround );
}
function user_is_owner_workaround(dataResponse) {
var viewer = dataResponse.get('viewer').getData();
globalWorkaroundIsOwner = viewer.isOwner();
workAroundComplete = true;
// value is correct at this point
}

Categories

Resources