I've been trying to create a html whack a mole game in which a mole has a class added to it at a certain interval, another timeout function is then triggered giving the user 3 seconds to click the mole and remove the class before a check is carried out which determines if that mole still has the class attached to it.
here is a jsfiddle of my game : https://jsfiddle.net/gko9puqf/1/ and below is my javascript.
var score = 0;
var numberofpipes = 9;
var lastnum = 0;
var intervalseconds;
var interval;
var haslost = false;
var checkpipetimer;
var timeoutfunc;
var timeoutinit;
var timers = [];
var burstingpipes = {};
var timeoutinit = setTimeout(startaburst, 3000);
$('#scorecontainer').text(score);
//starts a bursting pipe
function startaburst() {
clearTimeout(timeoutinit);
if (score < 10) {
intervalseconds = 2;
} else if (score >= 10 && score < 25) {
intervalseconds = 1.5;
} else if (score >= 25 && score < 40) {
intervalseconds = 1;
} else if (score >= 40 && score < 60) {
intervalseconds = 0.5;
} else if (score >= 60) {
intervalseconds = 0.25;
} else if (score > 100) {
intervalseconds = 0.1;
}
interval = intervalseconds * 1000;
burstingpipe();
//creating a loop with the new timeout value as the game gets harder.
//also assigning it to the timeoutfunc variable so i can cancel the loop later.
timeoutfunc = setTimeout(startaburst, interval);
}
//adds the bursting pipe attributes to the pipe intersections
function burstingpipe() {
randomnum = Math.floor(Math.random() * 9) + 1;
//cant be the same twice in case of overlapping
if ((randomnum == lastnum) || $("." + randomnum).hasClass("burstingpipe")) {
//if the random num is still valid after -1, -1
if (((randomnum - 1) >= 0) && !($("." + (randomnum - 1)).hasClass("burstingpipe"))) {
randomnum = (randomnum - 1);
//add one to the random number
} else if (((randomnum + 1) <= (numberofpipes)) && !($("." + (randomnum + 1)).hasClass("burstingpipe"))) {
randomnum = (randomnum + 1);
} else {
burstingpipe();
}
}
//make the lastnum the current number so we dont get 2 in a row
lastnum = randomnum;
randomdiv = $("." + randomnum);
console.log(randomdiv.hasClass("burstingpipe"));
//adds shake animation and red glow
console.log(randomnum);
randomdiv.addClass("burstingpipe");
//setting a timeout of 3 seconds, so th user has 3 seconds to press each
//bursting pipe before it bursts.
checkpipetimer = setTimeout(haspipeburst.bind(this, randomdiv), 3000);
}
//function to check if the pipe has burst.
function haspipeburst(pipecheck) {
console.log(pipecheck);
console.log(pipecheck.hasClass("burstingpipe"));
//checking to see if the pipe still has the class attached after 3 seconds
//and if the user has already lost.
if (pipecheck.hasClass("burstingpipe")) {
//if the pipe still has the class attached - game over.
haslost = true;
$("#result").text("you have lost");
//stopping the loop.
clearTimeout(timeoutfunc);
//changing the background color to make it look like the pipe has broken.
//(will possibly change to image in future)
//$(".hitpoint").removeClass("burstingpipe");
$(pipecheck).css("background-color", "#49c1e2");
}
}
//when the user clicks a hitpoint the class is removed and they gain a point.
$(document).on('click', '.hitpoint', function() {
if ($(this).hasClass("burstingpipe") && haslost == false) {
$(this).removeClass("burstingpipe");
score++;
$("#scorecontainer").text(score);
}
});
it works as expected up until the timeout gets significantly shorter (around a score of 40) and the moles glitch out as if the timeout was ignored.
I've been staring at the code for hours now and have made little progress so I am hoping you can help me out! i believe its something to do with the timeouts not being completed properly.
any help is greatly appreciated, thanks.
A bit of a late addition, but was working a bit on this in-between other tasks. As stated a problem with starting multiple timers is that you need to remember the specific timers and not only the last one. In the code below that is done by keeping a 'bursting pipe' inside a single class (function) with its own timer.
Perhaps I went a bit overboard, but as also stated by others, I liked the game you made :) One of the changes is not looping through all pipes to get a pipes that's not bursting, but remove the pipe from available pipes once it's bursting. This also negates the need for numbering the divs. More details in the code-comments. Of course you're free to ignore this code completely, but since I had it about finished, am posting it anyway.
Fiddle
var score = 24; //set higher for testing purposes
var pipes = $('.hitpoint').toArray() ,
last = null,
haslost = false,
interval = 2, //start interval
thresholds = {10: 1.5, 25: 1 , 40: 0.5, 60:0.25, 100 :1}; //interval thresholds
setTimeout(startaburst, 3000); //intial timeout (doesn't need to be cleared, because it's fired once)
$('#scorecontainer').text(score);
//starts a bursting pipe
function startaburst() {
if(haslost)return; //already lost
if(pipes.length>0){ //pick a pipe to burst unless all pipes allready bursting
var i;
while(true){
var p = pipes[i = Math.floor(Math.random() * pipes.length)]; //get random element from the available pipes
if(p!==last || pipes.length === 1)break;
}
pipes.splice(i,1); //remove pipe from available pipes
last = p; //remember last to prevent reusing the same pipe twice
new burstingPipe(p);
}
setTimeout(startaburst, interval * 1000); //wait until staring the new burst. interval is increased inside backInGame if the score increases
}
function burstingPipe(pipe){
this.pipe = $(pipe);
this.pipe.addClass("burstingpipe");
function checkBurst(){
this.dispose();
if(haslost)return; //already lost on other pipe
haslost = true;
$("#result").text("you have lost");
//changing the background color to make it look like the pipe has broken.
//(will possibly change to image in future)
this.pipe.css("background-color", "#49c1e2");
};
this.dispose=function(){
this.pipe.off('click'); //unbind click (no longer bursting or already burst)
this.pipe.removeClass("burstingpipe");
}
function backInGame(){
clearTimeout(this.timer); //clear the burst timeout (specific for this pipe)
this.dispose();
pipes.push(this.pipe[0]); //make pipe available again (NB, because the array contains of DOM elements and not jquery objects, [0] is needed)
var int = thresholds[++score]; //increase the score and check if interval should be increased for the new score
if(int && int < interval){
//optional: some message or css that interval is increased
interval =int;
}
$("#scorecontainer").text(score);
}
this.pipe.click(backInGame.bind(this)); //bind the click
this.timer =setTimeout(checkBurst.bind(this), 3000);
}
#keyframes shake {
5%,
15%,
25%,
35%,
45%,
55%,
65%,
75%,
85%,
95% {
left: 0;
right: 1vh;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
10%,
20%,
30%,
40%,
50%,
60%,
70%,
80%,
90%,
100% {
left: 1vh;
right: 0;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
}
#-webkit-keyframes shake {
5%,
15%,
25%,
35%,
45%,
55%,
65%,
75%,
85%,
95% {
left: 0;
right: 1vh;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
10%,
20%,
30%,
40%,
50%,
60%,
70%,
80%,
90%,
100% {
left: 1vh;
right: 0;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
}
#-moz-keyframes shake {
5%,
15%,
25%,
35%,
45%,
55%,
65%,
75%,
85%,
95% {
left: 0;
right: 1vh;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
10%,
20%,
30%,
40%,
50%,
60%,
70%,
80%,
90%,
100% {
left: 1vh;
right: 0;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
}
#-o-keyframes shake {
5%,
15%,
25%,
35%,
45%,
55%,
65%,
75%,
85%,
95% {
left: 0;
right: 1vh;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
10%,
20%,
30%,
40%,
50%,
60%,
70%,
80%,
90%,
100% {
left: 1vh;
right: 0;
outline: none;
border-color: red;
box-shadow: 0 0 10px red;
}
}
html {
height: 100%;
width: 100%;
}
* {
margin: 0;
padding: 0;
}
body {
height: 100%;
width: 100%;
}
#gamecontainer {
height: 100%;
width: 100%;
background-color: #49c1e2;
}
#gameinformation {
height: 10%;
display: flex;
flex-direction: row;
align-items: center;
padding-left: 10%;
}
#pipecontainer {
height: 80%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.pipe {
height: 8vh;
width: 100vw;
background-color: #a5a5a5;
display: flex;
flex-direction: row;
justify-content: space-around;
}
.hitpoint {
height: 10vh;
width: 10vh;
background-color: #6d6d6d;
border-radius: 2vh;
position: relative;
bottom: 1vh;
cursor: pointer;
}
#scoretext {
color: #fff;
font-size: 6vh;
}
#scorecontainer {
color: #fff;
font-size: 6vh;
}
#statusupdate {
color: #fff;
font-size: 6vh;
}
.burstingpipe {
animation-name: shake;
animation-duration: 3s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="gamecontainer">
<div id="gameinformation">
<p id="scoretext">Score: </p>
<div id="scorecontainer">
</div>
</div>
<div id="pipecontainer">
<div class="pipe">
<div class="hitpoint"></div>
<div class="hitpoint"></div>
<div class="hitpoint"></div>
</div>
<div class="pipe">
<div class="hitpoint"></div>
<div class="hitpoint"></div>
<div class="hitpoint"></div>
</div>
<div class="pipe">
<div class="hitpoint"></div>
<div class="hitpoint"></div>
<div class="hitpoint"></div>
</div>
</div>
<div id="statusupdate">
<p id="result"></p>
</div>
</div>
I liked your game :)
And here is your problem: when the score increase, your are decreasing the timeout interval to have multiple shacked zone at the same time. And to stop all timer if the user loose, you are using this line:
timeoutfunc = setTimeout(startaburst, interval);
and then
clearTimeout(timeoutfunc);
This won't work because timeoutfunc will only contain the last launched Timeout and not all launched timeout (remember, while the check is done each 3 sec, the shaking timeout will run multiple times). So you need an array to keep all intervals and then clear all off them.
I updated your fiddle (also removed some unneeded lines)
var score = 0;
var numberofpipes = 9;
var lastnum = 0;
var intervalseconds;
var interval;
var haslost = false;
var checkpipetimer;
var timeoutfunc = [];
var timers = [];
var burstingpipes = {};
setTimeout(startaburst, 3000);
$('#scorecontainer').text(score);
//starts a bursting pipe
function startaburst() {
if (score < 10) {
intervalseconds = 2;
} else if (score >= 10 && score < 25) {
intervalseconds = 1.5;
} else if (score >= 25 && score < 40) {
intervalseconds = 1;
} else if (score >= 40 && score < 60) {
intervalseconds = 0.5;
} else if (score >= 60) {
intervalseconds = 0.25;
} else if (score > 100) {
intervalseconds = 0.1;
}
interval = intervalseconds * 1000;
burstingpipe();
//creating a loop with the new timeout value as the game gets harder.
//also assigning it to the timeoutfunc variable so i can cancel the loop later.
timeoutfunc.push(setTimeout(startaburst, interval));
}
//adds the bursting pipe attributes to the pipe intersections
function burstingpipe() {
randomnum = Math.floor(Math.random() * 9) + 1;
//cant be the same twice in case of overlapping
if ((randomnum == lastnum) || $("." + randomnum).hasClass("burstingpipe")) {
//if the random num is still valid after -1, -1
if (((randomnum - 1) >= 0) && !($("." + (randomnum - 1)).hasClass("burstingpipe"))) {
randomnum = (randomnum - 1);
//add one to the random number
} else if (((randomnum + 1) <= (numberofpipes)) && !($("." + (randomnum + 1)).hasClass("burstingpipe"))) {
randomnum = (randomnum + 1);
} else {
burstingpipe();
}
}
//make the lastnum the current number so we dont get 2 in a row
lastnum = randomnum;
randomdiv = $("." + randomnum);
console.log(randomdiv.hasClass("burstingpipe"));
//adds shake animation and red glow
console.log(randomnum);
randomdiv.addClass("burstingpipe");
//setting a timeout of 3 seconds, so th user has 3 seconds to press each
//bursting pipe before it bursts.
checkpipetimer = setTimeout(haspipeburst.bind(this, randomdiv), 3000);
}
//function to check if the pipe has burst.
function haspipeburst(pipecheck) {
console.log(pipecheck);
console.log(pipecheck.hasClass("burstingpipe"));
//checking to see if the pipe still has the class attached after 3 seconds
//and if the user has already lost.
if (pipecheck.hasClass("burstingpipe")) {
//if the pipe still has the class attached - game over.
haslost = true;
$("#result").text("you have lost");
//stopping the loop.
for (var i = timeoutfunc.length - 1; i >= 0; i--) {
clearTimeout(timeoutfunc[i]);
}
//changing the background color to make it look like the pipe has broken.
//(will possibly change to image in future)
//$(".hitpoint").removeClass("burstingpipe");
$(pipecheck).css("background-color", "#49c1e2");
}
}
//when the user clicks a hitpoint the class is removed and they gain a point.
$(document).on('click', '.hitpoint', function() {
if ($(this).hasClass("burstingpipe") && haslost == false) {
$(this).removeClass("burstingpipe");
score++;
$("#scorecontainer").text(score);
}
});
Related
I want to show loading progress bar till the page load. If the internet might be slow and page take more to load, the progress bar show till the page fully load.
I attempted to add code, but because internet speeds vary, it is inaccurate. Could you please help me with this? I want to add a progress bar that starts at 0% while the page is loading and goes up to 100% after the page is completely loaded, dependent on the page loading speed.
$(window).on('load', function() {
$('#preloader').fadeOut(500);
$('body').removeClass('pre_loader');
});
var width = 100,
perfData = window.performance.timing, // The PerformanceTiming interface represents timing-related performance information for the given page.
EstimatedTime = -(perfData.loadEventEnd - perfData.navigationStart),
time = parseInt((EstimatedTime / 1000) % 60) * 100;
// Loadbar Animation
$(".loadbar").animate({
width: width + "%"
}, time);
// Percentage Increment Animation
function animateValue(id, start, end, duration) {
var range = end - start,
current = start,
increment = end > start ? 1 : -1,
stepTime = Math.abs(Math.floor(duration / range)),
obj = $(id);
var timer = setInterval(function() {
current += increment;
$(obj).text(current + "%");
//obj.innerHTML = current;
if (current == end) {
clearInterval(timer);
}
}, stepTime);
}
// Fading Out Loadbar on Finised
setTimeout(function() {
$('.preloader-wrap').fadeOut(100);
}, time);
<div class="preloader-wrap">
<div class="loader">
<div class="trackbar">
<div class="loadbar">
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
Sound like CSS (only) animation would be an option to consider. Inline your progress bar and it's <style> first thing as the page loads. Then remove then and make the body visible again on page load event. You can cheat time if you use some easing function that will never finish.
If you need numbers in your progress bar, then there is an options for that; even by a variable in modern browsers https://css-tricks.com/animating-number-counters/
For example (need to play with the percent values a bit):
<!-- almost first thing on page -->
<style>
.container {
width: 400px;
height: 50px;
position: relative;
border: 1px solid black;
}
.progress {
background: blue;
float: left;
color: white;
width: 100%;
height: 50px;
line-height: 50px;
animation-name: slideInFromLeft;
animation-duration: 30s;
animation-timing-function: cubic-bezier(0, .9, .9, .999);
text-align: center;
}
.percent::before {
content: counter(count);
animation-name: counter;
animation-duration: 30s;
animation-timing-function: cubic-bezier(0, .9, .9, .999);
counter-reset: count 0;
}
#keyframes slideInFromLeft {
0% {
width: 0%;
}
99% {
width: 99%;
}
}
#keyframes counter {
0% {
counter-increment: count 0;
}
10% {
counter-increment: count 50;
}
20% {
counter-increment: count 60;
}
30% {
counter-increment: count 70;
}
40% {
counter-increment: count 80;
}
50% {
counter-increment: count 90;
}
60% {
counter-increment: count 95;
}
70% {
counter-increment: count 98;
}
80% {
counter-increment: count 99;
}
90% {
counter-increment: count 90;
}
100% {
counter-increment: count 100;
}
}
</style>
<div class="container">
<div class="progress">
<span class="percent">%</span>
</div>
</div>
I used pixi js on my website to create a dynamic gradient animation with shape-changing blobs. The animation works perfectly fine the only problem I am facing is when I run page speed tests test it assumes the page has not been rendered since the animation is running still and gives it astronomical loading times.
I assume the part of the problem is page speed runs until the javascript has finshed executing which never will since this is a 2d animation.
If anyone has any idea on how to solve this problem please let me know.
import * as PIXI from "https://cdn.skypack.dev/pixi.js";
import { KawaseBlurFilter } from "https://cdn.skypack.dev/#pixi/filter-kawase-blur";
import SimplexNoise from "https://cdn.skypack.dev/simplex-noise";
import hsl from "https://cdn.skypack.dev/hsl-to-hex";
import debounce from "https://cdn.skypack.dev/debounce";
// return a random number within a range
function random(min, max) {
return Math.random() * (max - min) + min;
}
// map a number from 1 range to another
function map(n, start1, end1, start2, end2) {
return ((n - start1) / (end1 - start1)) * (end2 - start2) + start2;
}
// Create a new simplex noise instance
const simplex = new SimplexNoise();
// ColorPalette class
class ColorPalette {
constructor() {
this.setColors();
this.setCustomProperties();
}
setColors() {
// pick a random hue somewhere between 220 and 360
this.hue = ~~random(220, 360);
this.complimentaryHue1 = this.hue + 30;
this.complimentaryHue2 = this.hue + 60;
// define a fixed saturation and lightness
this.saturation = 95;
this.lightness = 50;
// define a base color
this.baseColor = hsl(this.hue, this.saturation, this.lightness);
// define a complimentary color, 30 degress away from the base
this.complimentaryColor1 = hsl(
this.complimentaryHue1,
this.saturation,
this.lightness
);
// define a second complimentary color, 60 degrees away from the base
this.complimentaryColor2 = hsl(
this.complimentaryHue2,
this.saturation,
this.lightness
);
// store the color choices in an array so that a random one can be picked later
this.colorChoices = [
this.baseColor,
this.complimentaryColor1,
this.complimentaryColor2
];
}
randomColor() {
// pick a random color
return this.colorChoices[~~random(0, this.colorChoices.length)].replace(
"#",
"0x"
);
}
setCustomProperties() {
// set CSS custom properties so that the colors defined here can be used throughout the UI
document.documentElement.style.setProperty("--hue", this.hue);
document.documentElement.style.setProperty(
"--hue-complimentary1",
this.complimentaryHue1
);
document.documentElement.style.setProperty(
"--hue-complimentary2",
this.complimentaryHue2
);
}
}
// Orb class
class Orb {
// Pixi takes hex colors as hexidecimal literals (0x rather than a string with '#')
constructor(fill = 0x000000) {
// bounds = the area an orb is "allowed" to move within
this.bounds = this.setBounds();
// initialise the orb's { x, y } values to a random point within it's bounds
this.x = random(this.bounds["x"].min, this.bounds["x"].max);
this.y = random(this.bounds["y"].min, this.bounds["y"].max);
// how large the orb is vs it's original radius (this will modulate over time)
this.scale = 1;
// what color is the orb?
this.fill = fill;
// the original radius of the orb, set relative to window height
this.radius = random(window.innerHeight / 6, window.innerHeight / 3);
// starting points in "time" for the noise/self similar random values
this.xOff = random(0, 1000);
this.yOff = random(0, 1000);
// how quickly the noise/self similar random values step through time
this.inc = 0.002;
// PIXI.Graphics is used to draw 2d primitives (in this case a circle) to the canvas
this.graphics = new PIXI.Graphics();
this.graphics.alpha = 0.825;
// 250ms after the last window resize event, recalculate orb positions.
window.addEventListener(
"resize",
debounce(() => {
this.bounds = this.setBounds();
}, 250)
);
}
setBounds() {
// how far from the { x, y } origin can each orb move
const maxDist =
window.innerWidth < 1000 ? window.innerWidth / 3 : window.innerWidth / 5;
// the { x, y } origin for each orb (the bottom right of the screen)
const originX = window.innerWidth / 1.25;
const originY =
window.innerWidth < 1000
? window.innerHeight
: window.innerHeight / 1.375;
// allow each orb to move x distance away from it's x / y origin
return {
x: {
min: originX - maxDist,
max: originX + maxDist
},
y: {
min: originY - maxDist,
max: originY + maxDist
}
};
}
update() {
// self similar "psuedo-random" or noise values at a given point in "time"
const xNoise = simplex.noise2D(this.xOff, this.xOff);
const yNoise = simplex.noise2D(this.yOff, this.yOff);
const scaleNoise = simplex.noise2D(this.xOff, this.yOff);
// map the xNoise/yNoise values (between -1 and 1) to a point within the orb's bounds
this.x = map(xNoise, -1, 1, this.bounds["x"].min, this.bounds["x"].max);
this.y = map(yNoise, -1, 1, this.bounds["y"].min, this.bounds["y"].max);
// map scaleNoise (between -1 and 1) to a scale value somewhere between half of the orb's original size, and 100% of it's original size
this.scale = map(scaleNoise, -1, 1, 0.5, 1);
// step through "time"
this.xOff += this.inc;
this.yOff += this.inc;
}
render() {
// update the PIXI.Graphics position and scale values
this.graphics.x = this.x;
this.graphics.y = this.y;
this.graphics.scale.set(this.scale);
// clear anything currently drawn to graphics
this.graphics.clear();
// tell graphics to fill any shapes drawn after this with the orb's fill color
this.graphics.beginFill(this.fill);
// draw a circle at { 0, 0 } with it's size set by this.radius
this.graphics.drawCircle(0, 0, this.radius);
// let graphics know we won't be filling in any more shapes
this.graphics.endFill();
}
}
// Create PixiJS app
const app = new PIXI.Application({
// render to <canvas class="orb-canvas"></canvas>
view: document.querySelector(".orb-canvas"),
// auto adjust size to fit the current window
resizeTo: window,
// transparent background, we will be creating a gradient background later using CSS
transparent: true
});
// Create colour palette
const colorPalette = new ColorPalette();
app.stage.filters = [new KawaseBlurFilter(30, 10, true)];
// Create orbs
const orbs = [];
for (let i = 0; i < 10; i++) {
const orb = new Orb(colorPalette.randomColor());
app.stage.addChild(orb.graphics);
orbs.push(orb);
}
// Animate!
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
app.ticker.add(() => {
orbs.forEach((orb) => {
orb.update();
orb.render();
});
});
} else {
orbs.forEach((orb) => {
orb.update();
orb.render();
});
}
document
.querySelector(".overlay__btn--colors")
.addEventListener("click", () => {
colorPalette.setColors();
colorPalette.setCustomProperties();
orbs.forEach((orb) => {
orb.fill = colorPalette.randomColor();
});
});
:root {
--dark-color: hsl(var(--hue), 100%, 9%);
--light-color: hsl(var(--hue), 95%, 98%);
--base: hsl(var(--hue), 95%, 50%);
--complimentary1: hsl(var(--hue-complimentary1), 95%, 50%);
--complimentary2: hsl(var(--hue-complimentary2), 95%, 50%);
--font-family: "Poppins", system-ui;
--bg-gradient: linear-gradient(
to bottom,
hsl(var(--hue), 95%, 99%),
hsl(var(--hue), 95%, 84%)
);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
max-width: 1920px;
min-height: 100vh;
display: grid;
place-items: center;
padding: 2rem;
font-family: var(--font-family);
color: var(--dark-color);
background: var(--bg-gradient);
}
.orb-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
}
strong {
font-weight: 600;
}
.overlay {
width: 100%;
max-width: 1140px;
max-height: 640px;
padding: 8rem 6rem;
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.375);
box-shadow: 0 0.75rem 2rem 0 rgba(0, 0, 0, 0.1);
border-radius: 2rem;
border: 1px solid rgba(255, 255, 255, 0.125);
}
.overlay__inner {
max-width: 36rem;
}
.overlay__title {
font-size: 1.875rem;
line-height: 2.75rem;
font-weight: 700;
letter-spacing: -0.025em;
margin-bottom: 2rem;
}
.text-gradient {
background-image: linear-gradient(
45deg,
var(--base) 25%,
var(--complimentary2)
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-moz-background-clip: text;
-moz-text-fill-color: transparent;
}
.overlay__description {
font-size: 1rem;
line-height: 1.75rem;
margin-bottom: 3rem;
}
.overlay__btns {
width: 100%;
max-width: 30rem;
display: flex;
}
.overlay__btn {
width: 50%;
height: 2.5rem;
display: flex;
justify-content: center;
align-items: center;
font-size: 0.875rem;
font-weight: 600;
color: var(--light-color);
background: var(--dark-color);
border: none;
border-radius: 0.5rem;
transition: transform 150ms ease;
outline-color: hsl(var(--hue), 95%, 50%);
}
.overlay__btn:hover {
transform: scale(1.05);
cursor: pointer;
}
.overlay__btn--transparent {
background: transparent;
color: var(--dark-color);
border: 2px solid var(--dark-color);
border-width: 2px;
margin-right: 0.75rem;
}
.overlay__btn-emoji {
margin-left: 0.375rem;
}
a {
text-decoration: none;
color: var(--dark-color);
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
/* Not too many browser support this yet but it's good to add! */
#media (prefers-contrast: high) {
.orb-canvas {
display: none;
}
}
#media only screen and (max-width: 1140px) {
.overlay {
padding: 8rem 4rem;
}
}
#media only screen and (max-width: 840px) {
body {
padding: 1.5rem;
}
.overlay {
padding: 4rem;
height: auto;
}
.overlay__title {
font-size: 1.25rem;
line-height: 2rem;
margin-bottom: 1.5rem;
}
.overlay__description {
font-size: 0.875rem;
line-height: 1.5rem;
margin-bottom: 2.5rem;
}
}
#media only screen and (max-width: 600px) {
.overlay {
padding: 1.5rem;
}
.overlay__btns {
flex-wrap: wrap;
}
.overlay__btn {
width: 100%;
font-size: 0.75rem;
margin-right: 0;
}
.overlay__btn:first-child {
margin-bottom: 1rem;
}
}
<!-- Canvas -->
<canvas class="orb-canvas"></canvas>
<!-- Overlay -->
<div class="overlay">
<!-- Overlay inner wrapper -->
<div class="overlay__inner">
<!-- Title -->
<h1 class="overlay__title">
Hey, would you like to learn how to create a
<span class="text-gradient">generative</span> UI just like this?
</h1>
<!-- Description -->
<p class="overlay__description">
In this tutorial we will be creating a generative “orb” animation
using pixi.js, picking some lovely random colors and pulling it all
together in a nice frosty UI.
<strong>We're gonna talk accessibility, too.</strong>
</p>
<!-- Buttons -->
<div class="overlay__btns">
<button class="overlay__btn overlay__btn--transparent">
View
</a>
</button>
<button class="overlay__btn overlay__btn--colors">
<span>Randomise Colors</span>
<span class="overlay__btn-emoji">🎨</span>
</button>
</div>
</div>
</div>
Try execute your "pixi related code" after page is loaded. So i mean this code:
// Create a new simplex noise instance
const simplex = new SimplexNoise();
...
// Create PixiJS app
const app = new PIXI.Application({
// render to <canvas class="orb-canvas"></canvas>
view: document.querySelector(".orb-canvas"),
// auto adjust size to fit the current window
resizeTo: window,
// transparent background, we will be creating a gradient background later using CSS
transparent: true
});
// Create colour palette
const colorPalette = new ColorPalette();
app.stage.filters = [new KawaseBlurFilter(30, 10, true)];
// Create orbs
const orbs = [];
for (let i = 0; i < 10; i++) {
const orb = new Orb(colorPalette.randomColor());
app.stage.addChild(orb.graphics);
orbs.push(orb);
}
// Animate!
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
app.ticker.add(() => {
orbs.forEach((orb) => {
orb.update();
orb.render();
});
});
} else {
orbs.forEach((orb) => {
orb.update();
orb.render();
});
}
document
.querySelector(".overlay__btn--colors")
.addEventListener("click", () => {
colorPalette.setColors();
colorPalette.setCustomProperties();
orbs.forEach((orb) => {
orb.fill = colorPalette.randomColor();
});
});
Execute all this not "as soon as possible" (as you do now - which causes page speed to count it as page load time) - but after page is loaded. Additionally you can also add "setTimeout" there with 1 second delay - so it will be: "after page load plus 1 second".
See some proposed solutions like these: Run a certain script JS after x seconds that the page is loaded
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
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.
How can I change the HTML background color automatically every 2 seconds? HTML5 with CSS3 fade in or fadeout?
I tried to use transition with timer and CSS target without any success
input[type=checkbox] {
position: absolute;
top: -9999px;
left: -9999px;
}
label {
display: block;
background: #08C;
padding: 5px;
border: 1px solid rgba(0,0,0,.1);
border-radius: 2px;
color: white;
font-weight: bold;
}
input[type=checkbox]:checked ~ .to-be-changed {
color: red;
}
A few changes A variation on this should work in modern browsers, if you know the colors and the number of colors in advance:
.animate-me {
-webkit-animation: bgcolorchange 4s infinite; /* Chrome, Safari, Opera */
animation: 4s infinite bgcolorchange;
}
#keyframes bgcolorchange {
0% {
background-color: red;
}
25% {
background-color: green;
}
50% {
background-color: yellow;
}
75% {
background-color: yellow;
}
100% {
background-color: red;
}
}
/* Chrome, Safari, Opera */
#-webkit-keyframes bgcolorchange {
0% {background: red;}
25% {background: yellow;}
75% {background: green;}
100% {background: blue;}
}
<div class="animate-me">Trippy! Give me a headache!</div>
http://jsfiddle.net/nnw7xza2/1/
Click to demohere!
Figure it up with:
-css3
-html5
-javascript timer
var arrColor = ["#45c1bf", "#f0593e", "#aeacd4", "#bdd630", "#4479bd", "#f5b11e"];
var footer = document.getElementById("footer");
var header = document.getElementById("header");
//helper function - get dark or lighter color
function LightenDarkenColor(col, amt) {
var usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
var num = parseInt(col, 16);
var r = (num >> 16) + amt;
if (r > 255) r = 255;
else if (r < 0) r = 0;
var b = ((num >> 8) & 0x00FF) + amt;
if (b > 255) b = 255;
else if (b < 0) b = 0;
var g = (num & 0x0000FF) + amt;
if (g > 255) g = 255;
else if (g < 0) g = 0;
return (usePound ? "#" : "") + (g | (b << 8) | (r << 16)).toString(16);
}
//random new color
function GetNewColor() {
var index = Math.floor((Math.random() * 5) + 1);
return arrColor[index];
}
// set new color
function SetNewColor(color) {
document.body.style.background = color;
var NewColor = LightenDarkenColor(color, -20);
footer.style.backgroundColor = NewColor;
header.style.backgroundColor = NewColor;
//footer.style.opacity = 1.2;
}
// on document load function start
(function() {
var colorSelected = GetNewColor();
SetNewColor(colorSelected);
})();
//change color timer
window.setInterval(function() {
var colorSelected = GetNewColor();
SetNewColor(colorSelected);
}, 2000);
* {
margin: 0;
padding: 0;
}
body {
background: #bdd630;
transition: background-color 0.5s ease;
color: #fff;
}
#header {
background: #000;
height: 40px;
text-align: center;
}
#content {
/* Now, to activate scrollbars
and compensate #footer height: */
padding-bottom: 40px;
}
#footer {
background: #000;
position: fixed;
bottom: 0px;
width: 100%;
/* cause of fixed pos */
height: 40px;
text-align: center;
}
<div id="header">header</div>
<div id="content">
<p>content here</p>
</div>
<div id="footer">footer</div>
Enjoy
If you are looking for an easy to understand way to do this, check out Basecacti. Of course, Basecacti as of now does not include embedding of the background on to your own html page, so just look at the source code behind it. Here's an example if you need it:
var clr1 = renderColors("clr1");
var clr2 = renderColors("clr2");
var clr3 = renderColors("clr3");
var speed = renderColors("speed");
var deb = document.body;
var circle = 0;
deb.style.backgroundColor = clr1;
setInterval(function(){
if (circle == 0) {
deb.style.backgroundColor = clr2;
circle = 1;
}
else if (circle == 1) {
deb.style.backgroundColor = clr3;
circle = 2;
}
else {
deb.style.backgroundColor = clr1;
circle = 0;
}
}, speed);
To make this work for you, define 3 different colors as clr1, clr2, and clr3. Then set the speed variable to 2000 for 2 secs, and it should work. (The renderColors function that defines these values in the above code is what Basecacti uses to get the colors that users define from a different webpage.) Also, Basecacti is Open-Source for now, so you might want to hurry over to their site and get this code ASAP. If you only want the background to change once after 2 seconds, change the function from setInterval to setTimeout, but don't change anything else. Please comment on this post if the Basecacti website shuts down or stops working, or if I have an error in the code.