Best Way to Hide/Show Multiple Surfaces with RenderControllers - javascript

What's the best practice to show/hide multiple surfaces simultaneously? Do I group all the surfaces under one render controller? Or, does each surface have a render controller assigned to it?
I am currently doing the latter, but am left with the distinct difficulty of attempting to trigger follow-up transitions. This is hard for me because my implementation doesn't provide a clear indication of when all the surfaces have been hidden. It is even harder because the hide transition is triggered with a bounded random time interval (between 200 to 2000 millisecond).
Any solutions? Code below:
for (var i = 0; i < surfaces.length; i += 1) {
var surface = surfaces[i][0];
var renderController = surfaces[i][1];
if (s.id !== clickedSurface.id) {
var fn = (function (s, rc) {
return function () { Timer.setTimeout(function () {rc.hide()}, getRandomArbitrary(200,2000)); };
})(surface, renderController);
s.colored ? Timer.setTimeout(fn, 2500) : fn();
}
}

If you wanted to iterate through your surfaces and hide one at a time, you could do something like the following code shows.
Example jsBin Here
function _hideNext(index) {
if (index === surfaces.length) {
//do something final, now complete
_showNext(0);
} else {
var rc = surfaces[index][1];
var surface = surfaces[index][0];
var nextIndex = index + 1;
rc.hide(surface, _hideNext.bind(this, nextIndex));
}
}
_hideNext(0);
Just change your RenderController options to your needs.

Related

How reduce delay in javascript / jquery change event?

I have five filters (multiple selects), which filter the data behind five visualisations in a dashboard. The filters have a JQuery change event, but the first line of code in this event takes half a second to happen. I don't understand why there's a delay. If I remove the code after the first line of code, the delay goes away. It's as though the code is not running in sequence.
The purpose of that first line of code is to make visible five "misty" (semi opaque) divs to obscure the graphics until the update code has run.
I'm using chosen.js on the selects but even when I remove chosen, there is still a delay. The filters are built dynamically. Here's the code that adds the change event:
for (i=0; i<filters0Length; i++) {
$("[id='"+filters[0][i]+"']").on('change',function(e,p){
d3.selectAll("div.misty").style("visibility","visible");//make the fade divs appear - takes half a second
if (!p) {
for (var j=0; j<filters[0].length; j++) { filters[6][j] = []; filters[5][j].filterAll(); }
} else {
if (p.selected) {
var tempIndex = filters[0].indexOf(e.target.id);//whether it's company, portfolio, industry or country
filters[6][tempIndex].push(p.selected);//store this filter
filters[5][tempIndex].filterFunction(function(d){ return filters[6][tempIndex].indexOf(d)!=-1; });
}
if (p.deselected) {
var tempIndex = filters[0].indexOf(e.target.id);//whether it's company, portfolio, industry or country
var tempIndex2 = filters[6][tempIndex].indexOf(String(p.deselected));
filters[6][tempIndex].splice(tempIndex2,1);
filters[5][tempIndex].filterAll();
if (filters[6][tempIndex].length>0) { filters[5][tempIndex].filterFunction(function(d){ return filters[6][tempIndex].indexOf(d)!=-1; }); }
window.portfolio_p = window.portfolio_p2;
}
}
update();
})
}
If I remove the update commands, the code runs much quicker:
for (i=0; i<filters0Length; i++) {
$("[id='"+filters[0][i]+"']").on('change',function(e,p){
d3.selectAll("div.misty").style("visibility","visible");//make the fade divs appear - takes half a second
}
Mmm, I have to agree you have an odd bug.
All I can suggest is pushing the filter manipulation into a later event thread.
You could contrive to use a Promise but window.setTimeout() is less expensive.
for(i=0; i<filters0Length; i++) {
$("[id='"+filters[0][i]+"']").on('change', function(e, p) {
d3.selectAll('div.misty').style('visibility', 'visible');
window.setTimeout(function() {
if (!p) {
// etc...
} else {
// etc...
}
update();
}, 0); // in practice the delay will be something like 15 ms.
})
}

Changing multiple different classes with javascript

I have been building a standalone web app for myself to learn about asp.net MVC4 and in so doing have been learning more about HTML 5, CSS and JavaScript. Needless to say I have learned alot and realized their is much more to learn. Before I ask any questions however I do a thorough search of this site and others to try and find things myself as I find I learn better by doing.
My issue here is just trying to find a way to simplify my JavaScript code if at all possible. I am not looking for JQuery at this time but will be looking into it in the future. For now strictly JavaScript.
Here is a portion of my JavaScript code that I am looking to truncate.
window.addEventListener('load', tap);
function tap() {
var elements = document.getElementByClassName('tap');
for ( var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/tap_icon.jpg>")';
}
}
This code works great for what I intended it to do which is change the element class of 'tap' to a specific image no matter how many elements have the class name.
The problem is I have 8 different classes that I am doing this with one for each image that needs to be displayed. Shown below are 2 of the functions.
window.addEventListener('load', tap);
function tap() {
var elements = document.getElementsByClassName('tap');
for ( var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/tap_icon.jpg">';
}
}
window.addEventListener('load', redMana);
function redMana() {
var elements = document.getElementsByClassName('red');
for (var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/Red_Mana.jpg">';
}
}
Is there a way to bundle this all into one function or would it be better to keep them separate to maintain readability and easier updating if new file names are created for images/new classes added to the app.
Anything that would point me in the right direction would be much appreciated. Thank you all for you knowledge and assistance.
Edit:
Sorry I am coming back to this so late. I am in the midst of moving and starting a new job.
I have updated my JavaScript File to reflect the answer provided however when I run the application, only the first call fires.
Here is the full script for right now I will need to add further calls.
window.addEventListener('load', setImage);
function setImage(className, imageName) {
var elements = document.getElementByClassName(className);
for (var i = elements.length - 1; i >= 0; --i) {
elements[i].innerHTML = '<img src="/Content/Images/' + imageName + '.jpg">';
}
setImage('tap', 'tap_icon');
setImage('c2', '2cmana');
setImage('red', 'Red_Mana');
}
When I run the app the first call
setImage('tap', 'tap_icon');
fires, but then the following calls do nothing. I don't know what I am missing on this.
In order to not repeat the same code over and over you could add parameters to the function assuming it's the same function with different input:
//For Example:
function setImage(className, imageName) {
var elements = document.getElementsByClassName(className);
for (var i = elements.length - 1; i >= 0; --i)
{
elements[i].innerHTML = '<img src="/Content/Images/' + imageName + '.jpg">';
}
}
You could then call the function like this:
setImage('red', 'Red_Mana');
setImage('tap', 'tap_icon');
Since you mentioned you're looking to the future I'm proving an answer with ES6 features.
To simplify things you could abstract all similar codes and build a configuration-base code like so:
const baseImgPath = "/Content/Images/",
classImageMap = [
{ className: 'tap', imageName: 'tap_icon.jpg' },
{ className: 'red', imageName: 'Red_Mana.jpg' }
];
window.addEventListener('load', onWindowLoad);
function onWindowLoad() {
classImageMap.forEach(setImageToClass);
}
function setImageToClass(mapping) {
let { className, imageName } = mapping;
document
.getElementsByClassName(className)
.forEach(elem => elem.innerHTML = `<img src="${baseImgPath+imageName}"/>` );
}

Unity javascript infinite spawning

So I'm trying to set up a system in a project where these spawn points will spawn targets that move towards the player and have to be destroyed before reaching a certain point, or its game over. Everything seems to be working fine except for one issue. The spawners don't stop spawning. They're supposed to do waves, spawning more enemies after each wave has been finished. I'm totally lost as to where the error might be.
Small note, originally I had the spawn count be 3 times the enemyspawncount, and spawnCount would count down to 0, then jump to 2 and remain there.
Spawner script:
var targetPrefab:Transform;
var spawnCount = deathcounter.enemySpawnCount;
function Start()
{
StartCoroutine("CoStart");
}
function CoStart() : IEnumerator
{
while (true)
yield CoUpdate();
}
function CoUpdate(){
spawnCount = deathcounter.enemySpawnCount;
while(spawnCount > 0)
{
var target= Instantiate(targetPrefab, transform.position, transform.rotation);
target.rigidbody.AddForce(Vector3.right * (deathcounter.enemySpawnCount *0.5 * 100));
spawnCount = spawnCount - 1;
Debug.Log("Spawn" + spawnCount);
yield WaitForSeconds (5);
}
deathcounter.timeToSpawn = false;
}
Target script:
var spawnCount = deathcounter.enemyDeathCount;
function OnTriggerEnter() {
Destroy (gameObject);
deathcounter.enemyDeathCount = deathcounter.enemyDeathCount + 1;
}
Death Counter script:
static var enemyDeathCount = 0;
static var enemySpawnCount = 1;
static var timeToSpawn : boolean = true;
function Update () {
if(enemyDeathCount % 3 == 0 && enemyDeathCount != 0){
timeToSpawn = true;
enemySpawnCount = enemySpawnCount + 1;
}
}
The issue could be in function CoUpdate(). The value of deathcounter.enemySpawnCount never gets reduced in that function. So if CoUpdate() gets called again, deathcounter.enemySpawnCount will still have the same value, and more enemy prefabs will get instantiated.
If that is the issue, and I'm not just misreading your code, you can solve that easily by setting deathcounter.enemySpawnCount after you set spawnCount:
spawnCount = spawnCount - 1;
deathcounter.enemySpawnCount = spawnCount;
Debug.Log("Spawn" + spawnCount);
Debug.Log("Spawn (double-check) " + deathcounter.enemySpawnCount);
With much more mature eyes, I can look back and correct myself.
First I have to flip the order of these commands so they both trigger.
function OnTriggerEnter() {
deathcounter.enemyDeathCount = deathcounter.enemyDeathCount + 1;
Destroy (gameObject);
}
Second I have to redo how spawnCount is handled. It should be removed from the 'target' script, and given a set initial value, not set to another variable value. It should only be changed in the while loop with each iteration, and in the death counter script, inside the if statement, so it is set to be equal to the new enemySpawnCount value only when that if statement is true.

Trouble with OOJS

I have a variable number of sliders on a page. Each slider contains a variable number of slides, and a pair of prev/next buttons to navigate sequentially through its respective slides.
Here is the markup for a typical slider:
<section class="foo">
<button class="prev"></button>
<ul class="container slider">
<li class="slide" id="a01"></li>
<li class="slide" id="a02"></li>
<li class="slide" id="a03"></li>
</ul>
<button class="next"></button>
</section>
Each slider should know the index of the slide it is currently showing. This leads me to believe that on page load, I should loop through all $('.slider') elements and instantiate an object for each. How do I write the class that defines the slider? It should have a property that contains the index of the current slide being shown, and a method that increments or decrements that property depending on whether the user clicked 'next' or 'prev'. I've come as far as this:
var slider = function () {
this.pager = 0;
this.incrementPager = function () {
console.log(this.pager++);
};
};
$(function () {
$('.prev, .next').click(slider);
});
... But my code doesn't do much :)
Here's how to make a jQuery plugin, which I think is a better way since you're using jQuery, which is not really meant for Class based OO.
$.fn.slider = function() {
$(this).each(function() {
var wrapper = $(this);
var index = 0;
var listItems = wrapper.find('.slide');
wrapper.find('.prev').click(function(){
if (index > 0) {
$(listItems[index]).removeClass('active');
index--;
$(listItems[index]).addClass('active');
}
});
wrapper.find('.next').click(function(){
if (index < listItems.length - 1) {
$(listItems[index]).removeClass('active');
index++;
$(listItems[index]).addClass('active');
}
});
});
};
And you could use it like
$('section').slider();
Working Example http://jsfiddle.net/MR8wE/
If you really want some OO in there you could do the following (but I think it goes against how jQuery works).
/**
* Adds behavior to the HTML for a slider
*/
function Slider(wrapper) {
var me = this;
me.wrapper = $(wrapper);
me.index = 0;
me.listItems = wrapper.find('.slide');
wrapper.find('.prev').click(function(){
me.prev();
});
wrapper.find('.next').click(function(){
me.next();
});
}
Slider.prototype.next = function() {
this.go(1;)
}
Slider.prototype.previous = function() {
this.go(-1);
}
Slider.prototype.go = function(steps) {
var oldIndex = this.index;
var newIndex = oldIndex + steps
if (newIndex >= 0 && newIndex < this.listItems.length ) {
$(this.listItems[oldIndex]).removeClass('active');
$(listItems[new]).addClass('active');
this.index = newIndex;
}
}
Then you would use it like
var sliders = [];
$('section').each(function(){
sliders.push(new Slider(this));
});
// Control the slider without user intervention
sliders[0].next();
sliders[1].go(2);
Example http://jsfiddle.net/MR8wE/2/
In response to the question in the comments:
function Slider(elm)
{
this.domElement = elm;
this.pager = 0;
}
Slider.prototype.incrementPager = function()
{
this.pager++;
};//all instances share this method ==> only 1 function object in mem.
$(function()
{
var objectCollection = (function(elements)
{
var toReturn = [];
var scanElementsFunction = function()
{//declared in this scope, to have access to elements var
'use strict';//avoid setting globals
if (this.allElems === undefined)
{
this.allElems = elements;
}
return this.allElems;
};
elements.each(function(idx,el)
{
toReturn[idx] = new Slider(el);//<-- pass DOMElement to constructor
toReturn[idx].getAll = scanElementsFunction;//<-- reference, no new function object
});
return toReturn;
}($('.prev, .next')));//this scope preserves the elements returned by jQuery selector
console.log(objectCollection);
});
There's nothing really useful in there, but I tried to incorporate as much tricks and tips while keeping the snippet as short as possible. If some of the code is unclear, let me know and I'll provide some more explanation (which might take an hour or two, 'cause I'm going to grab a bite now :))
The following approach uses the more formal constructor function and prototype which keeps it quite lightweight rather than stacking enclosures. The constructor is...
function Slider(slides, transition) {
// Keeps a reference to the current slide index
this._current = 0;
// An array of slides
this._slides = slides;
// A transition function
this._transition = transition;
}
It accepts an array of slides and a function which will be given two slides when we transition between them. This means we keep control of our transition effect by externalizing it. It is also framework agnostic and has no dependencies to jQuery or any other framework. The constructor itself doesn't do much. The following method is our meat...
// The function that swaps slides
Slider.prototype.goto = function(i) {
// Do a bit of sense checking
if(i > this._slides.length || i < 0)
throw new Error("Slide does not exist");
// Swap the slides by passing them to the transition function
var currentSlide = this._slides[this._current];
var nextSlide = this._slides[i];
this._transition(currentSlide, nextSlide);
// Update the current index
this._current = i;
};
It takes the new index for a slide and passes the old and the new slide to the transition function. It then updates the index it uses for tracking the current slide. We then want to implement a rotating previous and next function so the following methods describe how we can do that using modulus, note we have to add the length of the slides because negative modulus does not work how we want it to for this function.
// Calculate the next index as a rotating index
Slider.prototype.getNextIndex = function() {
return (this._current + 1) % this._slides.length;
};
// Calculate the previous index as a rotating index
Slider.prototype.getPrevIndex = function() {
return (this._current + this._slides.length - 1) % this._slides.length;
};
Then we add some sugar...
// Sugar to go next and prev
Slider.prototype.next = function() {
this.goto(this.getNextIndex());
};
Slider.prototype.prev = function() {
this.goto(this.getPrevIndex());
};
You may have a problem with associating the prev and next buttons with their sliders. You can find them before and after the slider element or as I have done below have them contained in the slider element. To set up sliders using jQuery you could do the following...
$(".slider").each(function() {
var slider = new Slider($(this).find(".slide"), function(a, b) {
$(a).hide();
$(b).show();
});
$(this).data("slider", slider);
$(this).find(".prev").click(function() {
slider.prev();
});
$(this).find(".next").click(function() {
slider.next();
});
});
EDIT Here is it in action http://jsfiddle.net/w8u69/
And because the logic for transitioning is exposed you can quite easily add in transitioning effects without modifying the original Slider "class". http://jsfiddle.net/w8u69/1/
EDIT Just to show the power in this approach, without modifying the original slider "class" you can add in additional logic to automatically move between slides. You can do this with a decorator, or with inheritance, but this example shows how it can be done with composition. http://jsfiddle.net/w8u69/4/
One last thing and this is possibly the most important thing about the pure OO approach, by simply changing the integration code and keeping the OO "classes" untouched, we can reuse the logic we have written for the slider and plug it into a completely different framework. This fiddle shows it working with MooTools http://jsfiddle.net/w8u69/5/

Creating a loop from a series of onMouseOver Events

How can I create a loop out of this function:
window.onload = function makeHalo() {
document.getElementById("d1").onmouseover = function() {
this.id ="d1On";
this.className="hover";
document.getElementById("menu1").style.color="#6DC5E6";
};
document.getElementById("menu1").onmouseover = function() {
this.style.color="#6DC5E6";
document.getElementById("d1").className="hover";
document.getElementById("d1").id="d1On";
};
document.getElementById("d1").onmouseout = function() {
this.id ="d1";
this.className="";
document.getElementById("menu1").style.color="#FFFFFF";
};
document.getElementById("menu1").onmouseout = function() {
this.style.color="#FFFFFF";
document.getElementById("d1On").className="";
document.getElementById("d1On").id="d1";
};
document.getElementById("d2").onmouseover = function() {
this.id ="d2On";
this.className="hover";
document.getElementById("menu2").style.color="#6DC5E6";
};
document.getElementById("menu2").onmouseover = function() {
this.style.color="#6DC5E6";
document.getElementById("d2").className="hover";
document.getElementById("d2").id="d2On";
};
document.getElementById("d2").onmouseout = function() {
this.id ="d2";
this.className="";
document.getElementById("menu2").style.color="#FFFFFF";
};
document.getElementById("menu2").onmouseout = function() {
this.style.color="#FFFFFF";
document.getElementById("d2On").className="";
document.getElementById("d2On").id="d2";
};
}
The function pretty much learns the ID of an image when its hovered, changes the ID of that element, adds a class to the element, and changes the color of another element
The second part learns the ID of a list item when its hovered, changes its color, and changes the ID of the other image element and adds a class to that element as well.
The onmouseout simply resets everything.
On the actual HTML page, it is a menu page with lists. Below there a continent map, which is a background image. When you hover over a list item, it swaps out a point on a map with another picture for an indicator. You can also hover the points on the map to change the color of the links on the lists.
I tried doing something like this, but the loop only goes to the last iteration for some of the elements. The links change color fine, but it will only swap the picture for "d43" regardless of what link I hover over.
window.onload = function makeHalo() {
var i = 1;
for (i=1; i<44; i++) {
var menu = "menu"+i;
var d = "d"+i;
var On = "d"+i+"On";
document.getElementById(d).onmouseover = function() {
this.id = On;
this.className="hover";
document.getElementById(menu).style.color="#6DC5E6";
};
document.getElementById(menu).onmouseover = function() {
this.style.color="#6DC5E6";
document.getElementById(d).className="hover";
document.getElementById(d).id=On;
};
document.getElementById(d).onmouseout = function() {
this.id = d;
this.className="";
document.getElementById(menu).style.color="#FFFFFF";
};
document.getElementById(menu).onmouseout = function() {
this.style.color="#FFFFFF";
document.getElementById(On).className="";
document.getElementById(On).id=d;
};
}
}
Any help will be greatly appreciated.
The primary technical issue you're facing is that you're creating closures in a loop. Each one of those callbacks closes over the same i variable, whose value will be the same for each of the callbacks (its value after the final iteration). This is fixed by wrapping the body of the loop in its own function that receives i as an argument, thus creating a local copy on each iteration.
There are a number of style and performance issues, as well:
The bodies of those callbacks are in many cases exactly the same (the mouseover and mouseout pairs end up dong the same work in each block).
You're retrieving the same elements by ID repeatedly. This is unnecessary; you should save a reference.
You're identifying the state of an element by changing its ID. This isn't generally how you want to handle this. An ID shouldn't change.
I would write it more like this (addressing the closure issue and the first two bullet items above (not addressing the ID problem)):
for (var i = 1; i <= 2; i++) {
(function(i) {
var d = document.getElementById("d" + i);
var menu = document.getElementById("menu" + i);
d.onmouseover = menu.onmouseover = function() {
d.id = "d" + i + "On";
d.className = "hover";
menu.style.color = "#6DC5E6";
};
d.onmouseout = menu.onmouseout = function() {
d.id = "d" + i;
d.className = "";
menu.style.color = "#FFFFFF";
};
})(i);
}
This handles just two elements; simply change the loop max to make it work for more.
You can see a working demo here:
http://jsfiddle.net/ezYtq/
Is your last div in your HTML "d43" or is it "d44"? Your loop will run through d1 through d43 because you have i<44 which means when i is 44 it will exit the loop so it will stop at d43.
If you want it to get to d44, then either change the condition to: i <= 44
or change it to i < 45
By the way is there is reason you are not using jQuery it's design to make things like this much easier, in several ways. Maybe you listed what you were actually trying to accomplish with this code for example whether it's a menu system or something we might be able to suggest better approaches.
No need for JavaScript here... just use the CSS :hover pseudo-class.
But, to answer your question:
Do not change the id of your element. This seems fundamentally wrong. Change, add, or remove a class instead. What are you trying to accomplish by changing the id?
Don't keep track of id's, just keep track of element references directly.
Most importantly, when you are doing your loop, by the time the functions are called, the value of i is 45, for all elements. Solve this by passing i to a function that creates your event handlers:
window.onload = function makeHalo() {
for (var i = 1; i < 44; i++) {
(function (i) {
var menu = document.getElementById("menu" + i);
var d = document.getElementById("d" + i);
function over () {
d.className = "hover";
menu.style.color = "#6DC5E6";
}
d.onmouseover = over;
menu.onmouseover = over;
function out () {
d.className = "";
menu.style.color = "#FFFFFF";
}
d.onmouseout = out;
menu.onmouseout = out;
})(i);
}
}

Categories

Resources