Is there any way to have a function run with a setTimeout and also return a value assigned to a variable?
An example of what I mean is like this:
function count(start, target) {
if (start < target) {
start += 0.1;
return start;
} else if (start > target) {
return target;
}
// i'm fully aware the return above will prevent this line occur
// this is my issue currently
setTimeout(function () {
count(start, target)
}, 1000);
}
var el = document.getElementById('test');
var value = count(0.0, 1.0); //get the opacity value
function check() {
el.style.opacity = value; //assign what the value is in this moment
if (value != 1.0) {
setTimeout(check, 0);
}
}
check();
I know this code won't work the way i want it because return exit's the function, I wrote it to explain what I am trying to do.
The reason I want to do this in this kinda way is because I have an element which i want to fade in by altering it's opacity.
So i have a function which would increment a start value to a target value using what ever easing i want, this then returns said value which would be assigned to the element's opacity property.
I don't want to pass the element to this count function because that means it limits the use of it for when i want to animate other things besides elements.
What would the solution to this problem be?
I think what you are trying to do is to call the count method again if the value passed for start and target are not the same if so
function count(start, target) {
var ret;
if (start < target) {
ret = start + 0.1;
} else if (start > target) {
ret = target;
}
if (start != target) {
setTimeout(function () {
count(ret, target)
}, 1000);
}
return ret;
}
Related
I need to set interval once for each slider element inside array when "if" statement is true but my code sets unlimited interval every time "if" statement is true. it is my js code:
const sliders = [realestateSlider,carsSlider,spectechnicSlider,motorcyclesSlider,partsSlider,beautySlider,clothesSlider,bestsellersSlider,topproductsSlider,salesSlider];
function checkIfIntoView(sliders){
sliders.forEach(function(slider,index){
if(slider.getBoundingClientRect().top <= window.innerHeight || document.documentElement.innerHeight){
setInterval(function(){slider.scrollLeft += scrollWidth;},1000);
}
});
}
window.addEventListener('wheel',function(event){ checkIfIntoView(sliders); });
Since you call the checkIfIntoView function in event listener, event listner will tiger that function each and every time a on wheel event occur. So if you want to call the checkIfIntoView function once the wheel event triggered, use a Boolean variable.
let ran = false;
function checkIfIntoView(sliders){
if (!ran) {
ran = true
sliders.forEach(function(slider,index){
if(slider.getBoundingClientRect().top <= window.innerHeight || document.documentElement.innerHeight){
setInterval(function(){slider.scrollLeft += scrollWidth;},1000);
}
});
}
}
Assuming that those are all Elements in the sliders Array, maybe you're looking to do something like:
const sliders = [realestateSlider, carsSlider, spectechnicSlider, motorcyclesSlider, partsSlider, beautySlider, clothesSlider, bestsellersSlider, topproductsSlider, salesSlider];
onscroll = e=>{
requestAnimationFrame(()=>{
let b, t, l;
for(let n of sliders){ // n is each node
b = n.getBoundingClientRect(); t = b.top; l = b.left;
if(t+b.height > -1 && t <= innerHeight && l+b.width > -1 && l <= innerWidth){
console.log('in view');
}
else{
console.log('not in view');
}
}
});
}
Note that you can always leave window. off of any property of window.
I'm not sure exactly what you are asking... But if you want to scroll until slider.getBoundingClientRect().top >= window.innerHeight then you could write your function like this:
const sliders = [realestateSlider,carsSlider,spectechnicSlider,motorcyclesSlider,partsSlider,beautySlider,clothesSlider,bestsellersSlider,topproductsSlider,salesSlider];
function checkIfIntoView(sliders){
sliders.forEach(function(slider,index){
let interval = setInterval(function(slider){
if (slider.getBoundingClientRect().top <= window.innerHeight || document.documentElement.innerHeight) {
slider.scrollLeft += scrollWidth;
} else {
clearInterval(interval);
}
}, 1000);
}
window.addEventListener('wheel',function(event){ checkIfIntoView(sliders); });
That way the interval will be cleared after the criteria is met. Keep in mind setInterval will continue to run until it is cleared via clearInterval
I have a code that is supposed to show some text in a typewriter effect, but the console says that length is not defined. I do not know what to do, please help me.
function typeWriter(theX) {
var i = 0;
text = theX;
var leng = text.length;
if (i < leng) {
document.getElementById("theTexts").innerHTML += text.charAt(i);
setTimeout(typeWriter, speed);
}
}
var speed = 50;
typeWriter('Frog-E Console');
var speed = 60;
typeWriter('Booting up');
By calling setTimeout(typeWriter, speed) you are calling typeWriter without passing it anything. This function expect one argument (a string), so you need to pass it :
function typeWriter(theX) {
var i = 0;
text = theX;
var leng = text.length;
if (i < leng) {
document.getElementById("theTexts").innerHTML += text.charAt(i);
setTimeout(typeWriter.bind(this, text), speed);
}
}
var speed = 50;
typeWriter('Frog-E Console');
var speed = 60;
typeWriter('Booting up');
<div id="theTexts"></div>
However this doesn't produce the expected result, you should do something like this :
function typeWriter(text) {
const char = text[0]
theTexts.innerHTML += char;
if (text.length > 1) setTimeout(typeWriter.bind(this, text.substring(1)), speed);
}
var speed = 50;
typeWriter('Frog-E Console');
<div id="theTexts"></div>
Some issues:
When setTimeout calls your typeWriter function, it won't pass it a value for theX so you end up with theX being undefined, so text is undefined, so you get the error.
Your second typeWriter call won't wait until the previous one finishes before starting, so they'll stomp on each other. You can fix that by having typeWriter return a promise it fulfills when it's done.
Your text variable is undeclared, making it what I call an implicit global; we don't need that variable, so let's just ditch it.
Probably best to pass speed in rather than using a global.
Rather than having setTimeout call typeWriter directly, I'd probably use an inner function. See comments:
// Accept both text and speed
function typeWriter(text, speed) {
// Return a promise we'll fulfill when done
return new Promise(resolve => {
// Start out showing nothing
let currentLength = 0;
// Start the process by showing the first character
tick();
function tick() {
// Add one to what we're showing
++currentLength;
// Show it
document.getElementById("theTexts").innerHTML = text.substring(0, currentLength);
// Done?
if (currentLength === text.length) {
// Yes, fulfill the promise
resolve();
} else {
// No, keep going after a delay
setTimeout(tick, speed);
}
}
});
}
// Do the first one, wait for it to finish, then do the next
typeWriter("Frog-E Console", 50)
.then(() => {
return typeWriter("Booting up", 60);
});
<div id="theTexts"></div>
I am currently trying to make a program where the text changes as the phone moves every couple value(s) using the P5.JS deviceMoved() function.
(the gif below displays how i wanted the text to change eventually as the device moved)
As seen on the code below, I've put all the text in the array and I wanted to change the index to +1 each time say the move value ads 30 and repeat until all the text is gone.
let button;
let permissionGranted = false;
let nonios13device = false;
let cx, cy
let value = 0;
var myMessages = ["The", "Quick", "Brown", "Fox", "Jumped", "Over", "The", "Lazy", "Dog"];
var index = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(255)
text(myMessages[index], width / 2, height / 2);
fill(value);
text(value, width / 3, height / 3);
textSize(30)
}
function deviceMoved() {
value = value + 5;
if (value > 255) {
value = 0;
}
}
function onMove() {
var currentValue = value + 30;
if (value = currentValue) {
index++;
return;
}
if (index >= myMessages.length) {
index = 0;
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.3.1/lib/p5.js"></script>
I think my problem is within the onMove function, where I need to define the current value and what values could change the text, I'm fairly new at this so any insight/solution to do this would be highly appreciated :)
Thank you!
There are several issues related to the onMove function. First and foremost it is never called, and unlike deviceMoved it is not a special function that p5.js automatically invokes. Additional issues:
function onMove() {
// You create a currentValue variable that is just value + 30.
// Within the same function, checking if value is >= currentValue,
// assuming that is what you intended, will be fruitless because it
// is never true.
// What you probably want to do is declare "currentValue" as a global
// variable and check the difference between value and currentValue.
var currentValue = value + 30;
// This is the assignment operator (single equal sign), I think you meant
// to check for equality, or more likely greater than or equal to.
if (value = currentValue) {
index++;
// You definitely do not want to return immediately here. This is where
// you need to check for the case where index is greater than or equal
// to myMessages.length
return;
}
if (index >= myMessages.length) {
index = 0;
}
}
Here's a fixed version:
function deviceMoved() {
value = value + 5;
if (value > 255) {
// When value wraps around we need to update currentValue as well to
// keep track of the relative change.
currentValue = 255 - value;
value = 0;
}
onMove();
}
let currentValue = 0;
function onMove() {
if (value - currentValue >= 30) {
// Update currentValue so that we will wait until another increment of
// 30 before making the next change.
currentValue = value;
index++;
// We only need to make this check after we've incremented index.
if (index >= myMessages.length) {
index = 0;
}
}
}
In order to test this out on my mobile device (iOS 14) I had to add some code to request access to the DeviceMotionEvent, and host it in an environment using HTTPS and not embedding in an iframe. You can see my code on glitch and run it live here.
I'm trying to make a fallback function that imitates Promise for ie and whatnot
I have the following code:
function goPromise(nr){
console.time("promise");
var sum = 0;
var prom = function(){
return new Promise(function(resolve){
sum = sum + nr;
nr = nr-1;
resolve();
});
}
var doThat = function(){
if(nr > 0){
prom().then(function(){
nr = nr - 1;
doThat()
})
}
else {
console.log(sum);
console.timeEnd("promise");
}
}
doThat();
}
function goNormal(nr){
console.time("normal");
var sum = 0;
var x = function(){
if(nr > 0){
sum = sum + nr;
nr = nr -1;
x();
}
else {
console.timeEnd("normal")
}
}
x();
}
The goNormal works fine and faster than goPromise. That until i give it a big number like 50.000. In which case it gives me this
What does promise have that it can do this stuff no matter how many times?
And how can I implement it in vanilla js ?
A promise is a callback in the future, thus it does NOT recurse. It's like using window.setTimeout (which would solve your issue in the goNormal instance). In the case of the promise, you've set it up and thus the current execution of the "doThat" function actually terminates.
As there is a promise involved, which results in a callback in the future, you'd expect it to be slower. You'll find that using setTimeout will also slow your execution plan down.
The example given is contrived as recursion is not required, however, I get your point. Is there a particular problem you're trying to solve or is this academic?
See my example code below with setTimeout.
function goNormal(nr){
console.time("normal");
var sum = 0;
var x = function(){
if(nr > 0){
sum = sum + nr;
nr = nr -1;
window.setTimeout(x, 10);
}
else {
console.timeEnd("normal")
}
}
x();
}
You can use requestAnimationFrame in order to call the function again, and again, and again ... at roughly 60fps intervals ... and cause it to stop until it meets a certain condition. See simple example:
function recurring() {
var t1 = performance.now();
if (t1 > 1000) {
console.log("Done!");
} else {
console.log("Still going ... ");
window.requestAnimationFrame(recurring);
}
}
window.requestAnimationFrame(recurring);
This post offers two nice solutions that allow you to execute a recursive function as if it was a simple loop.
The first one, the "Trampoline" method:
function trampoline(cb) {
while (cb && cb instanceof Function) {
cb = cb();
}
return cb;
};
function goNormalT(nr) {
console.time("normal");
var sum = 0;
var x = function(){
if(nr > 0){
sum = sum + nr;
nr = nr -1;
return x.bind(null, nr);
}
else {
console.timeEnd("normal");
return null;
}
}
return trampoline(x.bind(null));
};
goNormalT(50000);
And a second one, a "Tail Call Optimizer":
function tco(f) {
var value;
var active = false;
var accumulated = [];
return function accumulator() {
accumulated.push(arguments);
if (!active) {
active = true;
while (accumulated.length) {
value = f.apply(this, accumulated.shift());
}
active = false;
return value;
}
}
}
function goNormal(nr) {
console.time("normal");
var sum = 0;
var x = tco(function() {
if (nr > 0) {
sum = sum + nr;
nr = nr - 1;
return x();
} else {
console.timeEnd("normal");
return null;
}
});
return x();
};
goNormal(50000);
The second one being a bit more sophisticated implementation of the first method.
If the ways these work are hard to grasp, make sure to check the article for an in depth explanation.
I want to slow down console.log in my loop
// function update to actualize value
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
}
For example, show one console.log immediatly, and then, each .5s
How can we do that ?
(maybe with setTimeout() but I don't want start delay)
The simplest way would be to introduce a timeout before running the update again each time...
// function update to actualize value
function update() {
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
requestAnimationFrame(function() {
setTimeout(update, 5000);
});
}
I used setTimeout() in preference over setInterval() as doing it this way (as well as moving the call to the end of the function) will make sure everything is completed, before starting the 5 second pause. It ensures there's no overlap, should the preceeding code take longer than 5 seconds.
First create a variable to store the time of the last console.log. Next, update that variable each time you console.log a value. Finally, add a check for the threshold.
var lastOutput = 0; // Setting this to 0 initially will ensure it runs immediately
var outputThreshold = 500; // in milliseconds
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
if (new Date().valueOf() - lastOutput > outputThreshold) {
// threshold met, output and update
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
lastOutput = new Date().valueOf();
}
}
update(); // fire first call to update, after that requestAnimationFrame() will handle future calls
If you want the time delay inside the for loop, you'd do this:
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
}
logger();
}
Now, things are going to be pretty messy because you're also using requestAnimationFrame() to schedule another iteration of the whole thing; that really won't make sense anymore. You'll probably want to have that wait until the logging process is done:
function update() {
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
else
requestAnimationFrame(update);
}
logger();
}