Javascript | How to stop a function that is running on a website - javascript

So, I've got a script that allows the user that goes on my website to activate like a rain feature. But I want them to be able to turn it off? The thing is that I don't have a clue on how to do this and I've tried many different things like break, return, some timeout thing, but nothing has given result, so I'm turning to you people who are smart and know this more then I do :)
Here is the script and I activate the script from a button press with onclick event.
var amountOfDrops = 150;
var started = false;
function randRange( minNum, maxNum) {
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);}
// function to generate drops
function createRain() {
if (started === false){
for( i=1;i<amountOfDrops;i++) {
var dropLeft = randRange(0,2400);
var dropTop = randRange(-1000,1400);
$('.rain').append('<div class="drop" id="drop'+i+'"></div>');
$('#drop'+i).css('left',dropLeft);
$('#drop'+i).css('top',dropTop);
}
console.log("Start");
console.log(started);
started = !started; //flips the bool to stop
}else{
console.log("Shut down");
console.log(started);
started = !started; //flips the bool to start
return false;
}}
I have searched around on the internet but I can't find anything about it so help is greatly appreciated.
EDIT(1)
So, let me fill in some gaps.
This is my CSS for it:
.drop {
background:-webkit-gradient(linear,0% 0%,0% 100%, from(rgba(13,52,58,1) ), to(rgba(255,255,255,0.6)) );
background: -moz-linear-gradient(top, rgba(13,52,58,1) 0%, rgba(255,255,255,.6) 100%);
width:1px;
height:89px;
color: #5cb1d1;
position: absolute;
bottom:200px;
-webkit-animation: fall .63s linear infinite;
-moz-animation: fall .63s linear infinite;
}
/* animate the drops*/
#-webkit-keyframes fall {
to {margin-top:900px;}
}
#-moz-keyframes fall {
to {margin-top:900px;}
}
This is how i call it in the html code:
<script type="text/javascript" src="scripts/Rain.js"></script>
<button onclick="createRain()">Start Rain</button>

I think we can solve this problem using setInterval(). It works for me just check it out.
var amountOfDrops = 150;
var started = false;
function randRange( minNum, maxNum) {
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);}
// function to generate drops
function createRain(started) {
if (started === false){
for( i=1;i<amountOfDrops;i++) {
var dropLeft = randRange(0,2400);
var dropTop = randRange(-1000,1400);
$('.rain').append('<div class="drop" id="drop'+i+'"></div>');
$('#drop'+i).css('left',dropLeft);
$('#drop'+i).css('top',dropTop);
}
}else{
started = !started; //flips the bool to start
return false;
}}
$(document).on("click",".start",function(){
started = false;
setInterval(function(){
createRain(started)
},200)
})
$(document).on("click",".end",function(){
started = true;
setInterval(function(){
createRain(started)
},200)
$(".drop").remove()
})
body{
background-color: #000000;
width: 100%;
overflow: hidden;
}
.btn{
border: 1px solid #ffffff;
color: #ffffff;
background-color: transparent;
margin: 20px auto;
padding:5px 20px;
}
.btn:hover{
color: #eeeeee;
}
.drop{
background-color: #000;
width: 1px;
height: 3px;
background-color: #ffffff;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<button class="btn end">End</button>
<button class="btn start">Start</button>
</div>
<div class="rain">
</div>

i have the same problem,
you can try to add a timer to your function
and you can use :
$('#someElement').click(function(){
// Clear the timeout
clearTimeout(timer);
});

You could try to make a setInterval and then have a setTimeout for a clearInterval. Just a thought and this could be in or outside the function. Also, I'm pretty sure that JavaScript doesn't execute the console.log() and flips the boolean until the for statement is done. So the best way to go is a setInterval then have a clearInterval, whether a setTimeout or just a click of the button.

Related

How to write a simple marquee effect with javascript

I need everyone's help. I currently need to implement a marquee effect. The yellow box needs to be scrolled up to show the name. Every time I scroll, I have to stay in the middle of the box for 1 second before continuing to scroll. I can find such an example on the Internet. , but the logic of this program is a bit difficult for me to understand for urban beginners. I wonder if anyone would like to provide a simpler and easier-to-understand writing method if I want to achieve this marquee effect?
​​Sorry, I am a beginner in the program, the current logic More complex programs are more difficult to understand.
function slideLine(box, stf, delay, speed, h) {
var slideBox = document.getElementById(box);
var delay = delay || 1000,
speed = speed || 20,
h = h || 40;
var tid = null,
pause = false;
var s = function() {
tid = setInterval(slide, speed);
};
var slide = function() {
if (pause) return;
slideBox.scrollTop += 1;
if (slideBox.scrollTop % h == 0) {
clearInterval(tid);
slideBox.appendChild(slideBox.getElementsByTagName(stf)[0]);
slideBox.scrollTop = 0;
setTimeout(s, delay);
}
};
setTimeout(s, delay);
}
slideLine("kanban_info", "p", 1000, 25, 40);
.kanban {
position: absolute;
margin: 0 auto;
width: 278px;
height: 50px;
background-color: yellow;
left: 50%;
transform: translate(-50%);
text-align: center;
line-height: 6;
}
.kanban .kenban_wrap {
height: 38px;
transform: translateY(28px);
overflow: hidden;
}
.kanban .kenban_wrap .kanban_info {
line-height: 38px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="kanban">
<div class="kenban_wrap" id='kanban_info'>
<p class="kanban_info">Allen</p>
<p class="kanban_info">james</p>
<p class="kanban_info">jack</p>
</div>
</div>
By combining scroll-behavior with anchor tags that are programmatically clicked you can simplify it. This should be easier to understand and you can go from there, even if it might not be the best solution.
let links = document.querySelectorAll("a"); // List of links
let div = document.querySelector("div");
let index = 0;
let t = 2000; // setTimeout duration
// Change Scroll behavior to prevent the animation from the last to first list item
function scrollBeh() {
if(index == 1) {
div.style.scrollBehavior = "auto";
t = 0; // Timeout duration to 0 to prevent `1` being shown longer than other list items
} else {
div.style.scrollBehavior = "smooth";
t = 2000;
}
}
// Loop through list items
function resetInd() {
if(index < 3) {
index++;
} else {
index = 0;
}
}
function clickLinks() {
links[index].click();
resetInd();
scrollBeh();
setTimeout(clickLinks, t);
}
setTimeout(clickLinks, t);
div {
width: 200px;
height: 100px;
background-color: darkblue;
overflow: hidden;
scroll-behavior: smooth;
}
li {
height: 100px;
list-style: none;
}
a {
text-decoration: none;
color: #FFF;
font-size: 50px;
}
<div>
<ul>
<li id="one">1</li>
<li id="two">2</li>
<li id="three">3</li>
<li id="one_loop">1</li>
</ul>
</div>

How to prevent a function form executing multiple times in a row in JS?

*It works now.
I am trying to make onEnter() and onSpace() functions not execute more then once in a row. For example, if I call onEnter(), it should execute the function only the first time, any calls in a row afterwards shouldn't execute. This is the same for onSpace().
The program should run in an order of onEnter() then onSpace() or vice versa, however, if one of the functions is repeated, it should just run it once.
If you run the code below, you can see the problem, press the "Enter" key to get the next question and press the "Space" bar to find the answer.
Thank you for taking the time to help, this is my first project after learning a bit of JavaScript, any tips on improving my code is much appreciated.
const onEnter = () => {
if (document.getElementById("answer").style.visibility="visible") {
document.getElementById("answer").style.visibility="hidden";
}
let x = Math.floor((Math.random() * 10) + 0);
let y = Math.floor((Math.random() * 10) + 0);
let addition = x + " + " + y;
let answer = x + y;
document.getElementById("question").innerHTML = addition;
document.getElementById("answer").innerHTML = answer;
toggleQuestion();
};
const toggleQuestion = () => {
let qTop = document.getElementById("question");
qTop.classList.toggle("questionAlt")
};
const onSpace = () => {
toggleQuestion();
document.getElementById("answer").style.visibility="visible";
}
document.body.addEventListener("keydown", function keyEnter() {
if (event.keyCode == 13) {
onEnter();
}
});
document.body.addEventListener("keydown", function keySpace() {
if (event.keyCode == 32) {
onSpace();
}
});
body {
overflow: hidden;
}
div {
position: fixed;
top: 0;
left: 0;
height: 97.9%;
width: 98.9%;
border: 10px solid rgb(0, 0, 0);
background-color: rgb(155, 163, 167);
}
#answer {
visibility: hidden;
}
button {
visibility: hidden;
}
#question {
position: absolute;
text-align: center;
margin-top: 15vw;
left: 40.6vw;
font-family: 'Knewave';
font-size: 8vw;
}
.question1 {
position: absolute;
animation: qBottom;
animation-duration:0.2s;
transition: 0.18s;
opacity: 1;
bottom: 13vw;
}
#keyframes qBottom {
0% {bottom:0.01px;}
100% {bottom: 13vw;}
}
#answer {
position: absolute;
color: white;
text-align: center;
width: 100%;
margin-top: 20%;
font-family: 'Knewave';
font-size: 5.8vw;
background-color: rgb(0, 0, 0);
}
.questionAlt {
position: absolute;
animation: qTop;
animation-duration:0.2s;
transition: 0.18s;
opacity: 0.5;
font-size: 20vw;
top: -10vw;
}
#keyframes qTop {
0% {top:0.01px;}
100% {top:-10vw;}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<link href="style.css" rel="stylesheet" type="text/css" />
<link href='https://fonts.googleapis.com/css?family=Knewave' rel='stylesheet'>
</head>
<body>
<script src="script.js"></script>
<div id="plusBackground">
<button onclick="keyEnter()"> Next Question </button>
<button for="generate" onclick="" onclick="keySpace()"> Reveal </button>
<h1 id="question"> 8 + 8 </h1>
<h2 id="answer"> 16 </h2>
</div>
</body>
</html>
I updated my answer to meet your updated question
Actually you dont need to register multiple eventlistener :
let current;
document.body.addEventListener("keydown", function keyEnter() {
if (event.keyCode == 13 && event.keyCode != current) {
console.log("Enter")
current = event.keyCode;
}
if (event.keyCode == 32 && event.keyCode != current) {
console.log("space")
current = event.keyCode;
}
});
It's not quite clear what you mean by "once in a row" but does removeEventListener or the once option for addEventListener help get you to where you want to be?
You can create some simple flag for that, i.e:
// Set flag to false
let entered = false;
document.body.addEventListener("keydown", function keyEnter() {
if (event.keyCode == 13) {
// Check flag, if false, run function
if(!entered) {
// Set flag to true
entered = true;
// run your function once
onEnter();
}
}
});
I would use two global flag variables both initialized to true: okToRunEnter and okToRunSpace.
Each related function should return immediately if its associated global flag variables is false. If it passes that check, it should set both flag variables to false. Right before exiting, the function should set the other flag variable to true.

How to animate a progress bar with negatives using Element.animate()

I'm attempting to mimic the following widget with HTML/CSS/JavaScript:
https://gyazo.com/76bee875d35b571bd08edbe73ead12cb
The way that I have it set up is the following:
I have a bar with a background color that has a gradient from red to green which is static.
I then have two blinders that is supposed to represent the negative space to give the illusion that the colored bars are animating (in reality, the blinders are simply sliding away)
I did it this way because I figured it might be easier instead of trying to animate the bar going in both directions, but now I'm not so sure lol. One requirement that I'm trying to keep is that the animation only deals with transform or opacity to take advantage of optimizations the browser can do (as described here: https://hacks.mozilla.org/2016/08/animating-like-you-just-dont-care-with-element-animate/)
The example has a few buttons to help test various things. The "Random positive" works great, and is exactly what I want. I haven't quite hooked up the negative yet tho because I'm not sure how to approach the problem of transitioning from positive to negative and vice-versa.
Ideally, when going from a positive to a negative, the right blinder will finish at the middle, and the left blinder will pick up the animation and finish off where it needs to go.
So for example, if the values is initially set to 40%, and the then set to -30%, the right blinder should animate transform: translateX(40%) -> transform: translateX(0%) and then the left blinder should animate from transform: translateX(0%) -> transform: translateX(-30%) to expose the red.
Also, the easing should be seamless.
I'm not sure if this is possible with the setup (specifically keeping the easing seamless, since the easing would be per-element, I think, and can't "carry over" to another element?)
Looking for guidance on how I can salvage this to produce the expected results, or if there's a better way to deal with this.
Note: I'm using jquery simply for ease with click events and whatnot, but this will eventually be in an application that's not jquery aware.
Here's my current attempt: https://codepen.io/blitzmann/pen/vYLrqEW
let currentPercentageState = 0;
function animate(percentage) {
var animation = [{
transform: `translateX(${currentPercentageState}%)`,
easing: "ease-out"
},
{
transform: `translateX(${percentage}%)`
}
];
var timing = {
fill: "forwards",
duration: 1000
};
$(".blind.right")[0].animate(animation, timing);
// save the new value so that the next iteration has a proper from keyframe
currentPercentageState = percentage;
}
$(document).ready(function() {
$(".apply").click(function() {
animate($("#amount").val());
});
$(".reset").click(function() {
animate(0);
});
$(".random").click(function() {
var val = (Math.random() * 2 - 1) * 100;
$("#amount").val(val);
animate(val);
});
$(".randomPos").click(function() {
var val = Math.random() * 100;
$("#amount").val(val);
animate(val);
});
$(".randomNeg").click(function() {
var val = Math.random() * -100;
$("#amount").val(val);
animate(val);
});
$(".toggleBlinds").click(function() {
$(".blind").toggle();
});
$(".toggleLeft").click(function() {
$(".blind.left").toggle();
});
$(".toggleRight").click(function() {
$(".blind.right").toggle();
});
});
$(document).ready(function() {});
.wrapper {
margin: 10px;
height: 10px;
width: 800px;
background: linear-gradient(to right, red 50%, green 50%);
border: 1px solid black;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.blind {
height: 100%;
position: absolute;
top: 0;
background-color: rgb(51, 51, 51);
min-width: 50%;
}
.blind.right {
left: 50%;
border-left: 1px solid white;
transform-origin: left top;
}
.blind.left {
border-right: 1px solid white;
transform-origin: left top;
}
<div class="wrapper">
<div class='blind right'></div>
<div class='blind left'></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>
<input id="amount" type="number" placeholder="Enter percentage..." value='40' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>
<button class="reset" href="#">Reset</button>
I've modified your code. Have a look at the code.
let currentPercentageState = 0;
function animate(percentage) {
var animation = [{
transform: `translateX(${currentPercentageState}%)`,
easing: "ease-out"
},
{
transform: `translateX(${percentage}%)`
}
];
var timing = {
fill: "forwards",
duration: 1000
};
if (percentage < 0) {
$(".blind.right")[0].animate(
[{
transform: `translateX(0%)`,
easing: "ease-out"
},
{
transform: `translateX(0%)`
}
], timing);
$(".blind.left")[0].animate(animation, timing);
} else {
$(".blind.left")[0].animate(
[{
transform: `translateX(0%)`,
easing: "ease-out"
},
{
transform: `translateX(0%)`
}
], timing);
$(".blind.right")[0].animate(animation, timing);
}
// save the new value so that the next iteration has a proper from keyframe
//currentPercentageState = percentage;
}
$(document).ready(function() {
$(".apply").click(function() {
animate($("#amount").val());
});
$(".reset").click(function() {
animate(0);
});
$(".random").click(function() {
var val = (Math.random() * 2 - 1) * 100;
$("#amount").val(val);
animate(val);
});
$(".randomPos").click(function() {
var val = Math.random() * 100;
$("#amount").val(val);
animate(val);
});
$(".randomNeg").click(function() {
var val = Math.random() * -100;
$("#amount").val(val);
animate(val);
});
$(".toggleBlinds").click(function() {
$(".blind").toggle();
});
$(".toggleLeft").click(function() {
$(".blind.left").toggle();
});
$(".toggleRight").click(function() {
$(".blind.right").toggle();
});
});
$(document).ready(function() {});
.wrapper {
margin: 10px;
height: 10px;
width: 800px;
background: linear-gradient(to right, red 50%, green 50%);
border: 1px solid black;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.blind {
height: 100%;
position: absolute;
top: 0;
background-color: rgb(51, 51, 51);
min-width: 50%;
}
.blind.right {
left: 50%;
border-left: 1px solid white;
transform-origin: left top;
}
.blind.left {
border-right: 1px solid white;
transform-origin: left top;
}
<div class="wrapper">
<div class='blind right'></div>
<div class='blind left'></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>
<input id="amount" type="number" placeholder="Enter percentage..." value='40' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>
<button class="reset" href="#">Reset</button>
You need to animate the things in two steps. The first step is to reset the previous state to initial state(which should be set to 0) and in the second step, you need to run the other animation which will actually move it to the destination state.
In order to achive this you can do,
let currentPercentageState = 0;
const animationTiming = 300;
function animate(percentage) {
let defaultTranformVal = [{
transform: `translateX(${currentPercentageState}%)`,
easing: "ease-out"
}, {transform: `translateX(0%)`}];
var animation = [{
transform: `translateX(0%)`,
easing: "ease-out"
},{
transform: `translateX(${percentage}%)`,
easing: "ease-out"
}];
var timing = {
fill: "forwards",
duration: animationTiming
};
if (percentage < 0) {
if(currentPercentageState > 0) {
$(".blind.right")[0].animate(defaultTranformVal, timing);
setTimeout(() => {
$(".blind.left")[0].animate(animation, timing);
}, animationTiming);
} else {
$(".blind.left")[0].animate(animation, timing);
}
}
if(percentage > 0) {
if(currentPercentageState < 0) {
$(".blind.left")[0].animate(defaultTranformVal, timing);
setTimeout(() => {
$(".blind.right")[0].animate(animation, timing);
}, animationTiming);
} else {
$(".blind.right")[0].animate(animation, timing);
}
}
// save the new value so that the next iteration has a proper from keyframe
currentPercentageState = percentage;
}
Here, you will see we have two transformations. The first one defaultTranformVal will move the currentPercentageState to zero and then the other one which will move from 0 to percentage.
You need to handle a couple of conditions here. The first one is if you are running it the first time(means there is no currentPercentageState), you don't need to run defaultTranformVal. If you have currentPercentageState then you need to run defaultTranformVal and then run the second animation.
Note:- You also need to clear the timeout in order to prevent the memory leak. This can be handle by storing the setTimout return value and then when next time it's running clear the previous one with the help of clearTimeout.
Here is the updated codepen example:-
https://codepen.io/gauravsoni119/pen/yLeZBmb?editors=0011
EDIT: I actually did manage to solve this!
let easing = "cubic-bezier(0.5, 1, 0.89, 1)";
let duration = 1000;
let easeReversal = y => 1 - Math.sqrt((y-1)/-1)
https://codepen.io/blitzmann/pen/WNrBWpG
I gave it my own cubic-bezier function of which I know the reversal for. The post below and my explanation was based on an easing function using sin() which isn't easily reversible. Not only that, but the built in easing function for ease-out doesn't match the sin() one that I had a reference for (I'm not really sure what the build in one is based on). But I realized I could give it my own function that I knew the reversal for, and boom, works like a charm!
This has been a very informative experience for me, I'm glad that I've got a solution that works. I still think I'll dip my toes in the other ideas that I had to see which pans out better in the long term.
Historical post:
So, after a few nights of banging my head around on this, I've come to the conclusion that this either isn't possible the way I was thinking about doing it, or if it is possible then the solution is so contrived that it's probably not worth it and I'd be better off developing a new solution (of which I've thought of one or tow things that I'd like to try).
Please see this jsfiddle for my final "solution" and a post-mortem
https://jsfiddle.net/blitzmann/zc80p1n4/
let currentPercentageState = 0;
let easing = "linear";
let duration = 1000;
function animate(percentage) {
percentage = parseFloat(percentage);
// determine if we've crossed the 0 threshold, which would force us to do something else here
let threshold = currentPercentageState / percentage < 0;
console.log("Crosses 0: " + threshold);
if (!threshold && percentage != 0) {
// determine which blind we're animating
let blind = percentage < 0 ? "left" : "right";
$(`.blind.${blind}`)[0].animate(
[
{
transform: `translateX(${currentPercentageState}%)`,
easing: easing
},
{
transform: `translateX(${percentage}%)`
}
],
{
fill: "forwards",
duration: duration
}
);
} else {
// this happens when we cross the 0 boundry
// we'll have to create two animations - one for moving the currently offset blind back to 0, and then another to move the second blind
let firstBlind = percentage < 0 ? "right" : "left";
let secondBlind = percentage < 0 ? "left" : "right";
// get total travel distance
let delta = currentPercentageState - percentage;
// find the percentage of that travel that the first blind is responsible for
let firstTravel = currentPercentageState / delta;
let secondTravel = 1 - firstTravel;
console.log("delta; total values to travel: ", delta);
console.log(
"firstTravel; percentage of the total travel that should be done by the first blind: ",
firstTravel
);
console.log(
"secondTravel; percentage of the total travel that should be done by the second blind: ",
secondTravel
);
// animate the first blind.
$(`.blind.${firstBlind}`)[0].animate(
[
{
transform: `translateX(${currentPercentageState}%)`,
easing: easing
},
{
// we go towards the target value instead of 0 since we'll cut the animation short
transform: `translateX(${percentage}%)`
}
],
{
fill: "forwards",
duration: duration,
// cut the animation short, this should run the animation to this x value of the easing function
iterations: firstTravel
}
);
// animate the second blind
$(`.blind.${secondBlind}`)[0].animate(
[
{
transform: `translateX(${currentPercentageState}%)`,
easing: easing
},
{
transform: `translateX(${percentage}%)`
}
],
{
fill: "forwards",
duration: duration,
// start the iteration where the first should have left off. This should put up where the easing function left off
iterationStart: firstTravel,
// we only need to carry this aniamtion the rest of the way
iterations: 1-firstTravel,
// delay this animation until the first "meets" it
delay: duration * firstTravel
}
);
}
// save the new value so that the next iteration has a proper from keyframe
currentPercentageState = percentage;
}
// the following are just binding set ups for the buttons
$(document).ready(function () {
$(".apply").click(function () {
animate($("#amount").val());
});
$(".reset").click(function () {
animate(0);
});
$(".random").click(function () {
var val = (Math.random() * 2 - 1) * 100;
$("#amount").val(val);
animate(val);
});
$(".randomPos").click(function () {
var val = Math.random() * 100;
$("#amount").val(val);
animate(val);
});
$(".randomNeg").click(function () {
var val = Math.random() * -100;
$("#amount").val(val);
animate(val);
});
$(".flipSign").click(function () {
animate(currentPercentageState * -1);
});
$(".toggleBlinds").click(function () {
$(".blind").toggle();
});
$(".toggleLeft").click(function () {
$(".blind.left").toggle();
});
$(".toggleRight").click(function () {
$(".blind.right").toggle();
});
});
animate(50);
//setTimeout(()=>animate(-100), 1050)
$(function () {
// Build "dynamic" rulers by adding items
$(".ruler[data-items]").each(function () {
var ruler = $(this).empty(),
len = Number(ruler.attr("data-items")) || 0,
item = $(document.createElement("li")),
i;
for (i = -11; i < len - 11; i++) {
ruler.append(item.clone().text(i + 1));
}
});
// Change the spacing programatically
function changeRulerSpacing(spacing) {
$(".ruler")
.css("padding-right", spacing)
.find("li")
.css("padding-left", spacing);
}
changeRulerSpacing("30px");
});
.wrapper {
margin: 10px auto 2px;
height: 10px;
width: 600px;
background: linear-gradient(to right, red 50%, green 50%);
border: 1px solid black;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.blind {
height: 100%;
position: absolute;
top: 0;
background-color: rgb(51, 51, 51);
min-width: 50%;
}
.blind.right {
left: 50%;
border-left: 1px solid white;
transform-origin: left top;
}
.blind.left {
border-right: 1px solid white;
transform-origin: left top;
}
#buttons {
text-align: center;
}
/* Ruler crap */
.ruler-container {
text-align: center;
}
.ruler, .ruler li {
margin: 0;
padding: 0;
list-style: none;
display: inline-block;
}
/* IE6-7 Fix */
.ruler, .ruler li {
*display: inline;
}
.ruler {
display:inline-block;
margin: 0 auto;https://jsfiddle.net/user/login/
background: lightYellow;
box-shadow: 0 -1px 1em hsl(60, 60%, 84%) inset;
border-radius: 2px;
border: 1px solid #ccc;
color: #ccc;
height: 3em;
padding-right: 1cm;
white-space: nowrap;
margin-left: 1px;
}
.ruler li {
padding-left: 1cm;
width: 2em;
margin: .64em -1em -.64em;
text-align: center;
position: relative;
text-shadow: 1px 1px hsl(60, 60%, 84%);
}
.ruler li:before {
content: '';
position: absolute;
border-left: 1px solid #ccc;
height: .64em;
top: -.64em;
right: 1em;
}
<div class="wrapper">
<div class='blind right'></div>
<div class='blind left'></div>
</div>
<div class="ruler-container">
<ul class="ruler" data-items="21"></ul>
</div>
<div id="buttons">
<input id="amount" type="number" placeholder="Enter percentage..." value='-80' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="flipSign">Flip Sign</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>
<button class="reset" href="#">Reset</button>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>
<hr />
<p><strong>A note</strong> on the attempt made here:</p>
<p>
I was trying to animate a percentage bar that has both positive and negative values. But I set a challenge as well: I wanted to achieve this via animations utilizing only the compositor - which means animating opacity or transform <strong>only</strong> (no color, width, height, position, etc). The ideas presented here were based on the concept of blinds. I have a static element with a background gradient of red to green, then I have two elements that "blind" the user to the background. These blinds, being a simple element, simply slide into and out of place.
</p>
<p>The problem that I ran into was timing the two animations correctly when they switched signage. It's currently working (very well) for linear animation, but as soon as you introduce an easing function it gets wonky. The reason for this is due to the value that I'm using to set the first animation length (iteration, not duration), as well as the second animations start to pick up where the first left off. The value that I was using is the percentage of the total travel distance that each of the blinds will have to do.</p>
<p>So, for example, if you have a value of 50, and go to -80, that's a total travel distance of 130. The first blind travels <code>50 / 130 = ~0.3846</code> of the total distance, and the second blind will travel <code>1 - ~0.3846 = ~0.6154</code> of the total distance.</p>
<p>But, these are not the correct values for the <em>duration</em> of the animation. Instead, these are the percentages of the easing values (the y-axis). To get the duration for these, I would have to find the x value (given the known y value). eg, for an ease-out animation for a value going from 50 to -80, the animation crosses our 0 at ~0.03846, and we would have to solve for x given <code>0.03846 = sin((x * PI) / 2)</code>.</p>
<p>With the help of Wolfram Alpha, I was able to find a few test values this got me much closer to the actual animation, but the blinds always stopped slightly off the mark. I eventually chalked this up to one of two reasons: the fact that the valuess are always going to be approximate and the browser is never going to be 100% accurate, or / and 2) the browser is using a slightly different easing function than I was using for reference. Regardless, being so constrained by the fact that this "animation" relies on two different aniamtions lining up perfectly, I decided to leave this version be and go in a different direction.</p>
<p>
If anyone finds an actual solution to this, please post an answer here: https://stackoverflow.com/questions/62866844/how-to-animate-a-progress-bar-with-negatives-using-element-animate
</p>
Thanks to those that attempted this admittedly tricky problem

Why isn't this 'charAt' function working?

I am trying to create a typing effect using Vanilla JS, but for some reason the charAt function isn't working, and when I replace i with something like 0, it works, but it spits it all out at once even though it's wrapped in a setTimeout() function
var sentence = document.getElementsByClassName('sentence')[0];
var words = ['websites', 'apps', 'games'];
var speed = 100;
function type(word) {
for(var i = 0; i < word.length; i++) {
setTimeout(function() {
sentence.innerHTML += word.charAt(i);
}, speed);
}
}
type(words[0]);
* {
font-family: Arial;
}
.container {
display: flex;
align-items: center;
justify-content: center;
}
.cursor {
background: #000;
width: 2px;
height: 15px;
animation: blink 1s steps(5, start) infinite;
}
#keyframes blink {
to { visibility: hidden; }
}
<div class="container">
<div class="sentence">We make </div>
<div class="cursor"></div>
</div>
Use an "asynchronous" loop using recursion, because now you start all your timers at once:
var sentence = document.getElementsByClassName('sentence')[0];
var words = ['websites', 'apps', 'games'];
var speed = 100;
function type(word) {
if (!word.length) return; // Nothing to do
setTimeout(function() {
sentence.textContent += word.charAt(0);
type(word.substr(1)); // call recursively only now
}, speed);
}
type(words[0]);
* {
font-family: Arial;
}
.container {
display: flex;
align-items: center;
justify-content: center;
}
.cursor {
background: #000;
width: 2px;
height: 15px;
animation: blink 1s steps(5, start) infinite;
}
#keyframes blink {
to { visibility: hidden; }
}
<div class="container">
<div class="sentence">We make </div>
<div class="cursor"></div>
</div>
I'm pretty sure switching in this code block for your timeout will solve your issue. I haven't had the ability to test it myself though.
setTimeout(function(i) {
sentence.innerHTML += word.charAt(i);
}.bind(this,i), speed * i);
You probably want to use recursion so that each time a new letter is added to the innerHtml, it starts a new timeout. Right now it's creating all your timeouts at the same time so they all fire basically at the same time.
You should increase the speed as 100 is very low and you can't see it. "websites" is still loading in 800ms so it is hard to see anything.
Don't use "type" as your function name as this is a reserved jquery function.
This is how it works:
var sentence = document.getElementsByClassName('sentence')[0];
var words = ['websites', 'apps', 'games'];
var speed = 100;
function typewriter_string(word) {
sentence.textContent='';
for(var i = 0; i < word.length; i++) {
doSetTimeout(i, word);
}
}
function doSetTimeout(i, word){
setTimeout(function() {
sentence.textContent += word.charAt(i);
}, speed*i);
}
typewriter_string('websites');
In your code the "i" is always 8 because of the setTimeout in the for-loop.

Prevent animation during animation

I'm trying to do an animation, everything works fine but I can't seem to prevent user from clicking during the animation and breaking the page layout.
I have managed to stop user when the divs are moving but I can't prevent when one div is expanded.
This is my fiddle: http://fiddle.jshell.net/p2fo4ek0/18/
<div class="internet_wrap">
<div class="internet tel" id="off_wrap">
<div class="package" id="one">
<div class="inner_package">
<div class="title">title</div>
</div>
</div>
<div class="package" id="two">
<div class="inner_package">
<div class="title">title</div>
</div>
</div>
<div class="package" id="three">
<div class="inner_package">
<div class="title">title</div>
</div>
</div>
</div>
</div>
var animating = false;
var clickedDiv, prevDiv, distance;
$('#off_wrap').on('click', '.package', function (e) {
e.stopPropagation();
if(animating == false) {
animating = true;
clickedDiv = $(this).closest('.package'),
prevDiv = $("#off_wrap > :first-child"),
distance = clickedDiv.offset().left - prevDiv.offset().left;
if (!clickedDiv.is(":first-child")) {
$.when(clickedDiv.animate({
left: -distance
}, 2000),
prevDiv.animate({
left: distance
}, 2000)).done(function () {
prevDiv.css('left', '0px');
clickedDiv.css('left', '0px');
//clickedDiv.css('width', $(window).width() - (clickedDiv.offset().left * 2));
clickedDiv.css('z-index', '10000000');
clickedDiv.css('overflow', 'visible');
clickedDiv.find(".inner_package").animate({width: 260}, 2000); //regullo kte
prevDiv.insertBefore(clickedDiv);
clickedDiv.prependTo("#off_wrap");
animating = false;
});
} else {
clickedDiv.css('z-index', '1000');
clickedDiv.find(".inner_package").css('transition', 'all 2s ease-in');
clickedDiv.find(".inner_package").css('width', '260px');
}
}
});
$(document).click(function() {
animating = false;
if(animating == false) {
clickedDiv.css('z-index', '1');
clickedDiv.find(".inner_package").css('transition', 'all 2s ease-in');
clickedDiv.find(".inner_package").css('width', '60px');
animating = false;
}
});
.internet {
text-align:center;
background: none repeat scroll 0% 0% rgba(248, 0, 140, 0.5);
padding-bottom:10px;
position: relative;
}
.internet .package {
display: inline-block;
/*border: 1px solid #FFFC00;*/
height: 60px;
width: 60px;
border-radius: 30px;
margin: 20px;
background:#fff;
color: rgba(0, 178, 248, 1);
position: relative;
cursor: pointer;
vertical-align:middle;
}
.inner_package {
display: inline-block;
width: 60px;
transition: all 2s ease-in;
background: #fff;
border-radius: 30px;
height: 60px;
}
It looks like you've put a lot of code in already attempting to fix the issue. I'm also a little confused as to what the issue is, as your jsFiddle doesn't give me trouble with clicking during the animation. Perhaps it's only certain situations you're speaking of.
Either way, I saw this.
$(document).click(function() {
animating = false;
if(animating == false) {
clickedDiv.css('z-index', '1');
clickedDiv.find(".inner_package").css('transition', 'all 2s ease-in');
clickedDiv.find(".inner_package").css('width', '60px');
animating = false;
}
});
That animating = false; both before the if statement and inside the if statement function seems redundant, and I suspect one of them needs to read animating = true; Let me know if that fixes your problem or not.
UPDATE: While I don't understand the animations themselves, I can say this. Your using animation as a control against multiple animations running. You have a statement that seems like it might be your problem.
if(animating == false) {
animating = true;
//do stuff
animating = false;
}
This may be your problem. Try getting more specific logic. like
if(animatingOne == false && animatingTwo == false && animatingThree == false) {
//logic to find out which animation is triggered
//turn the appropriate animation variable to true
//turn if off when done
}
If this doesn't work, keep playing with logic. I suspect that one variable animating is unable to accommodate the complexity of what you're doing.

Categories