How can I transform values inside a function onClick? - javascript

Basically, I've imported a WebGL globe and want to make it stop spinning when I click on the globe.
Here's my attempt at making it stop:
setInterval(function() {
var c = earth.getPosition();
earth.setCenter([c[0], c[1] + 0.9]);
earth.onclick.setCenter({c[0], c[1]);
}, 50);
Basically I want to get rid of the +0.9 onClick. If it helps, also adding the function with the earth object below:
<script src="http://www.webglearth.com/v2/api.js"></script>
<script>
function initialize() {
var options = {atmosphere: true, center: [0, 0], zoom: 0};
var earth = new WE.map('earth_div', options);
WE.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg', {
subdomains: '1234',
attribution: 'Tiles Courtesy of MapQuest'
}).addTo(earth);
</script>
I'm guessing my syntax isn't correct with the .onclick. What should it be instead?

Inspecting the examples on webglearth, the onClick is defined as:
earth.on('click', callback)
So you need to define the callback function as:
callback = function(){···}
or in short:
earth.on('click', function(e){···});
If you need to stop the animation, you have to use clearInterval on onClick event
var interval = setInterval(function() {
var c = earth.getPosition();
earth.setCenter([c[0], c[1] + 0.9]);
}, 50);
···
earth.on('click', function(){
clearInterval(interval);
});

According to http://www.webglearth.org/api, it should be earth.on('click', yourCallback());, not .onclick. Also, I would consider some changes to your code:
First off, don't set the onclick handler within the setatimeout function all the time. Do it one time on intialization and use a variable instead of 0.9 inside the timeout that u can set to 0 inside your onclick callback. In case you want to stop animation forever, you might consider breaking out of the setTimeout loop in case you stumble upon that variable being 0.
Im gonna leave implementation up to you

Related

Javascript: using one function on multiple elements

I'm new to JavaScript and I'm having trouble figuring out how to resize multiple elements with one function for my rhythm game. This is for my CSP class and theres no use of jQuery sadly. I'm also limited to the commands that the program (AppLab) I'm using has provided. My goal right now is to make an "animation" of a circle growing to its desired size to indicate that it should be clicked. I need these elements to appear while another one is in the process growing and so on.
I'm aware that my code probably sucks so if there is also a way to simplify or improve it I would love to know.
This is my current program code and the hitIndicator function is the one I'm having the most trouble with:
var circleSizeW = 0;
var circleSizeL = 0;
var score = 0;
hitCircle("hitcircle", 300, 6, 206);
hitCircle("image2", 300, 6, 682);
function circleEffects(circleid, whentohit) {
setTimeout(function() {
onEvent(circleid, "click", function() {
playSound("47 (1).mp3", false);
hideElement(circleid);
});
}, whentohit);
}
function hitIndicator(circleid, growthRate) {
var xPos = getXPosition(circleid);
var yPos = getYPosition(circleid);
var t = setInterval(function() {
circleSizeW = circleSizeW + growthRate;
circleSizeL = circleSizeL + growthRate;
xPos = xPos - (growthRate/2);
yPos = yPos - (growthRate/2);
showElement(circleid);
setSize(circleid, circleSizeW, circleSizeL);
setPosition(circleid, xPos, yPos);
if (circleSizeW >= 60) {
clearInterval(t);
circleSizeW = 0;
circleSizeL = 0;
}
}, 50);
}
function scoreSystem(circleid, whentohit) {
setTimeout(function() {
onEvent(circleid, "click", function() {
score = score + 100;
setText("scoreTrack", score);
});
}, whentohit);
}
function hitCircle(circleid, whentohit, growthRate, appearancetime) {
setTimeout(function() {
console.log("hitcircle");
circleEffects(circleid, whentohit);
hitIndicator(circleid, growthRate);
scoreSystem(circleid, whentohit);
}, appearancetime);
My code is nowhere near completion either so there are still many things that needs to be done.
I'm not sure how to have multiple circles running that function at similar times because when I try to fix the errors/change the values of the functions' parameters they sometimes loop twice, infinitely loop, or receive the changed values of the previous circle while the previous circle is still growing.
I am also fairly new with Javascript, but I have a fairly good idea of what should be done here.
I would write an object constructor for instantiating circles.
Scroll down this page to see how to make object constructors.
Then for your circle object add a hitindicator method.
This page covers methods.
Then set up a function that will retrieve and loop through every instantiated circle object and run .hitindicator on each circle. Best way to do this, might be to add every instantiated circle to a circle array, then just loop through the array?
Then have an update() function that calls the function in step 3, and have update() called every "frame" with setInterval.
The pages linked should give you enough information to figure it out from here.

Tween.js Not Calling onUpdate Function

I am using tweenjs and Typescript to change the x and y coordinates of a three.js cube. I created the following tween to change the x position of an item of class "FallingItem".
this.movementArcData.horzTween = new TWEEN.Tween(this.movementArcData.pos.x)
.to(this.movementArcData.newPos.x, this.movementArcData.movementTime * 1000)
.onUpdate(this.updateXPos)
.onComplete(this.horzTweenComplete);
where "this.movementArcData" is an object containing the following:
horzTween - the tween itself
pos.x - the original position of the item
movementTime - the time it takes to complete the movement, 2000 milliseconds
updateXPos - a member function of the a FallingItem object with the following code:
updateXPos(){
this.mesh.position.x = this.movementArcData.pos.x;
console.log("update x: " + this.movementArcData.pos.x);
}
horzTweenComplete - a member funtion of the FallingItem object with the following code:
horzTweenComplete(){
this.movementArcData.horzTweenComplete = true;
}
Neither the updateXPos or horzTweenComplete callback is getting fired.
I am calling TWEEN.update in my render loop like so:
TWEEN.update(dt);
Since the tween's onComplete event never fires, the TWEEN.update is called constantly. What am I missing that is causing the tween not to work properly?
I had a similar case when TWEEN was not calling my onUpdate function. Found out I had to call window.requestAnimationFrame() in order to tell the browser that I, i.e. TWEEN, "want to perform an animation and requests that the browser call a specified function to update an animation before the next repaint."
function animate(time) {
window.requestAnimationFrame(animate);
TWEEN.update(time);
}
new TWEEN
.Tween({ y: 0 })
.to({y: 1000}, 700)
.easing(TWEEN.Easing.Exponential.InOut)
.onUpdate(function () {
window.scrollTo(0, this.y);
})
.start();
animate();
The above example was taken from https://github.com/tweenjs/tween.js/blob/master/examples/00_hello_world.html.
Tween.js needs to be passed the elapsed time, not a delta time. Passing a running elapsed time fixed the problem.
Also, it's supposed to be passed an object containing the value you want interpolated. It looks like passing the value itself doesn't work. I had success with this:
let tweenElement = {
x: this.tweenInfo.pos.x,
y: this.tweenInfo.pos.y,
item: this
}
this.tweenInfo.tweenUp = new TWEEN.Tween(tweenElement)
.to({y : this.tweenInfo.newPos.y}
, this.tweenInfo.movementTime * 0.5 * 1000)
.easing( TWEEN.Easing.Cubic.InOut )
.onUpdate(function(){
this.item.updateYPos(tweenElement, this)
})
.onComplete(function(){
this.item.tweenUpComplete();
});
Another potential cause (I know probably not in this particular case) is that TWEEN.removeAll() is being called somewhere else in the code. One sign of this happening is that other tweens are working perfectly fine but some are not.

How can I loop jQuery code?

I've got a jQuery code which is supposed to change images after some amount of time and it works well, but it obviously stops as soon as the code ends. How can I make it run over and over again? I tried using javascript "if" loop but it didn't do anything.. or maybe I did it wrong?
(w4s and w5s are img's IDs)
Also I'm quite new to jQuery so if you have any comments about any errors I've made, I'd be glad to hear them!
Here's the code
$(function () {
$("#w4s").hide();
$("#w5s").hide();
$(document).ready(function () {
$(function () {
$("#w3s").delay("4000").fadeOut("slow", function () {
$("#w4s").fadeIn("slow",function () {
$(this).delay("4000").fadeOut("slow",function () {
$("#w5s").fadeIn("slow");
});
});
});
});
});
});
I guess you need something like this
window.setInterval(function() {
alert('I happen every 8 seconds');
}, 8000);
First of all:
$(document).ready(function() {...
is equivalent to
$(function() {...
so keep the latter and drop the usage of the former.
Second, understand what this invocation actually does: it tells jQuery to fire the callback (function() {...) once the DOM's ready. Therefore, you generally only need a single invocation of this pattern for all your code (unless you want different scopes, that is).
So, start your code like this in the outer most scope:
<script type="text/javascript">
$(function() {
// Your code goes here !!!
});
</script>
Now, since we've covered the basics, let's take care of your problem.
$(function(){
var looplength = 8000;
// You can combine selectors!!!
$("#w4s, #w5s").hide();
// Let's drop all these nested `domready` callbacks and
// in their stead set up an interval
window.setInterval(function() {
$("#w3s").delay("4000").fadeOut("slow", function(){
$("#w4s").fadeIn("slow",function(){
$(this).delay("4000").fadeOut("slow",function(){
$("#w5s").fadeIn("slow");
});
});
});
}, looplength);
});
I would use a timeout for this - its probably just a personal preference, but I find them much more efficient than intervals, and it gives me more control over the continuation of the loop.
Something like this would do it:
//store the timer in your outer scope
var timer = null;
//create an empty elements var in your outer scope for use later
var elements;
//create a loop function do do most of your work for you
function loop(duration, index){
//set the defaults if none are passed in
duration = typeof duration == "number" ? duration : 8000;
index = typeof index == "number" ? index : 0;
//your function made a bit more generic
//by selecting from the store elements list
$(elements[index]).fadeOut("slow", function(){
//Increase the index by 1 to select the next element,
//or reset to 0 if it is greater than the number of elements you have
index = index + 1 < elements.length ? index + 1 : 0;
$(elements[index]).fadeIn("slow");
});
//clear the timeout in case it hasn't been called already
clearTimeout(timer);
//set the timeout function to call this function again
// passing it back the index and duration
timer = setTimeout(function() {
loop(duration, index)
}, duration);
};
//Instantiate the loop function for the first time on document.ready
//It should continue on its own after that
$(document).ready(function() {
//set the elements after the DOM is loaded
elements = $("#w3s, #w4s, #w5s");
loop(4000);
});
Hope this helps. It a fairly robust approach, so you could reuse this function elsewhere as well. If you need to cancel the loop at any point, you have it stored as timer so you can just call clearTimeout('timer') so long as you are in the same scope.
Fiddle available here - https://jsfiddle.net/heuw8dt0/2/
EDIT:
Moved element selection inside the DOM ready function

Using setInterval() for a function with parameters in JavaScript

I need to call a function every 100ms, which is easy enough, but what if I need that function to accept a parameter?
The problem is that I create an object, and periodically need to update it. I have tried setting the object reference to a global, that didn't work. I tried setting it to a function variable, still no luck. Apparently I need to pass in the object, which I cant't figure out how to do using setInterval. There has to be a trick to this?
The code below works on forst call, but after that it fails at:
setCounterText.segment.DisplayText("AAABBB");
And complains that setCounterText.segment.DisplayText() is not a function...
Thanks...
window.onload = function ()
{
setInterval(setCounterText, 1000);
}
function setCounterText()
{
//"use strict";
var num;
if(!setCounterText.isInit)
{
num = 0;
setCounterText.isInit=true;
var canvas = document.getElementById('c');
var container = document.getElementById('container');
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
// Create a new sixteen segment display
setCounterText.segment = new SixteenSegment(1, canvas);
update(setCounterText.segment);
setCounterText.segment.DispayText("T-000:00:00.0");
}
num++;
setCounterText.segment.DisplayText("AAABBB");
}
You can create another function to act as a clojure for the setCounterText function and pass that as a parameter to setInterval.
setInterval(function() {
setCounterText(anotherParameter);
}, 1000);
That will capture your parameter and call the setCounterText function whenever the interval triggers.
Regarding the error you are getting, it's impossible to say without knowing the code in the SixteenSegment function but it should have a property set on it called DisplayText.

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.

Categories

Resources