cpu intense js animation, can it be the setInterval? - javascript

I'm making a simple background animation: basically I'd like to have 3 shapes smoothly bouncing around the screen. my reference is the well-known "dvd screensaver effect".
Starting from this tuorial I built my version with 3 pngs bouncing around and this is the result.
The problem is that when I run it I've noticed that my laptop fans immediately starts to run and sometimes it jerks, since I wanted to add this effect to a website I'm making (with some other content already), I'm afraid the animation would be even more laggy.
I wanted to ask you if you recommend any way, to make this animation run smoother and less cpu intense. I was wondering if the problem could be se function setInterval and if there could be some other function lighter for the cpu to run.
I've tried also to reduce the png size or use svg but I guess that the blur effect on the svg made the animation even more heavy
Thank you!
const section = document.querySelector(".bg-animation");
const logo = document.querySelector(".shape-bg-1");
const logo2 = document.querySelector(".shape-bg-2");
const logo3 = document.querySelector(".shape-bg-3");
const FPS = 60;
var widthLimite = $('.bg-animation').outerWidth();
var heightLimite = $('.bg-animation').outerHeight();
section.style.height = heightLimite;
section.style.width = widthLimite;
// Logo moving velocity Variables
let xPosition = 10;
let yPosition = 10;
let x2Position = 20;
let y2Position = 30;
let x3Position = 70;
let y3Position = 20;
$(".shape-bg-1").css('opacity',1);
$(".shape-bg-2").css('opacity',1);
$(".shape-bg-3").css('opacity',1);
//velocità
let xSpeed = 0.8;
let ySpeed = 0.8;
let x2Speed = 0.9;
let y2Speed = 0.6;
let x3Speed = 0.7;
let y3Speed = 0.9;
function update() {
logo.style.left = xPosition + "px";
logo.style.top = yPosition + "px";
logo2.style.left = x2Position + "px";
logo2.style.top = y2Position + "px";
logo3.style.left = x3Position + "px";
logo3.style.top = y3Position + "px";
}
setInterval(() => {
if (xPosition + logo.clientWidth >= widthLimite || xPosition <= 0) {
xSpeed = -xSpeed;
}
if (yPosition + logo.clientHeight >= heightLimite || yPosition <= 0) {
ySpeed = -ySpeed;
}
//shape2
if (x2Position + logo2.clientWidth >= widthLimite || x2Position <= 0) {
x2Speed = -x2Speed;
}
if (y2Position + logo2.clientHeight >= heightLimite || y2Position <= 0) {
y2Speed = -y2Speed;
}
//shape3
if (x3Position + logo3.clientWidth >= widthLimite || x3Position <= 0) {
x3Speed = -x3Speed;
}
if (y3Position + logo3.clientHeight >= heightLimite || y3Position <= 0) {
y3Speed = -y3Speed;
}
xPosition += xSpeed;
yPosition += ySpeed;
x2Position += x2Speed;
y2Position += y2Speed;
x3Position += x3Speed;
y3Position += y3Speed;
update();
}, 1000 / FPS);
body {
margin: 0;
padding: 0;
}
.container {
padding:0;
margin: 0;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.title {
font-family: 'arial','helvetica',sans-serif;
font-weight: 400;
font-size: 42px;
color: white;
mix-blend-mode: difference;
}
.home-bg-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
background-color: white;
}
.bg-animation {
width: 150%;
height: 150%;
position: absolute;
}
.shape-bg-1,
.shape-bg-2,
.shape-bg-3 {
position: absolute;
top: 10px;
left: 10px;
width: 80vw;
opacity: 0;
mix-blend-mode: difference;
transition: opacity 1s ease-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="container">
<div class="home-bg-container">
<div class="bg-animation">
<img class="shape-bg-1" src="https://i.ibb.co/HgpGbh4/shape1-min.png" alt="">
<img class="shape-bg-2" src="https://i.ibb.co/Pm6bmHj/shape2-min.png" alt="">
<img class="shape-bg-3" src="https://i.ibb.co/NCrH4GJ/shape3-min.png" alt="">
</div>
</div>
<p class="title">SOME TEXT</p>
</div>

Related

How to add a smooth animation to the progress bar

When I click I want to smoothly add segments to the progress bar. They are added but instantly. What could be the problem?
I tried to implement a smooth animation with setInterval, but nothing comes out. Percentages are also added instantly.
let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");
let progressBarStartValue = 0;
let progressBarEndValue = 100;
let speed = 50;
body.addEventListener("click", function(e) {
if (progressBarStartValue === progressBarEndValue) {
alert("you have completed all the tasks");
} else {
let progress = setInterval(() => {
if (progressBarStartValue != 100) {
progressBarStartValue += 10;
clearInterval(progress);
}
progressBarValue.textContent = `${progressBarStartValue}%`;
progressBar.style.background = `conic-gradient(
#FFF ${progressBarStartValue * 3.6}deg,
#262623 ${progressBarStartValue * 3.6}deg
)`;
}, speed);
}
});
.progressbar {
position: relative;
height: 150px;
width: 150px;
background-color: #262623;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.progressbar::before {
content: "";
position: absolute;
height: 80%;
width: 80%;
background-color: #0f0f0f;
border-radius: 50%;
}
.progressbar__value {
color: #fff;
z-index: 9;
font-size: 25px;
font-weight: 600;
}
<main class="main">
<section class="statistic">
<div class="container">
<div class="statistic__inner">
<div class="statistic__text">
<h2 class="statistic__title">You're almost there!</h2>
<p class="statistic__subtitle">keep up the good work</p>
</div>
<div class="progressbar"><span class="progressbar__value">0%</span></div>
</div>
</div>
</section>
</main>
This may not be exactly what you're looking for, but with the conic-gradient() implementation you're using, I'd recommend checking out a library call anime.js.
Here's an example with your implementation (same html and css):
// your.js
let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");
// Switched to object for target in anime()
let progressBarObject = {
progressBarStartValue: 0,
progressBarEndValue: 100,
progressBarAnimationValue: 0 * 3.6 // New value needed for smoothing the progress bar, since the progress value needs to be multiplied by 3.6
}
// Not necessary, but I recommend changing the event listener to pointerup for better support
// Also not necessary, I changed function to arrow function for my own preference
body.addEventListener("pointerup", e => {
e.preventDefault()
if (progressBarObject.progressBarStartValue === progressBarObject.progressBarEndValue) {
alert("you have completed all the tasks");
} else {
let newValue = 0 // Needed so we can set the value, before it's applied in anime()
if (progressBarObject.progressBarStartValue != 100) {
// Math.ceil() allows us to round to the nearest 10 to guarantee the correct output
newValue = Math.ceil((progressBarObject.progressBarStartValue + 10) / 10) * 10;
}
// Optional: Prevents accidentally going over 100 somehow
if (newValue > 100) {
newValue = 100
}
anime({
targets: progressBarObject,
progressBarStartValue: newValue,
progressBarAnimationValue: newValue * 3.6,
easing: 'easeInOutExpo',
round: 1, // Rounds to nearest 1 so you don't have 0.3339...% displayed in progressBarValue
update: () => {
progressBar.style.backgroundImage = `conic-gradient(
#FFF ${progressBarObject.progressBarAnimationValue}deg,
#262623 ${progressBarObject.progressBarAnimationValue}deg)`;
progressBarValue.textContent = `${progressBarObject.progressBarStartValue}%`;
},
duration: 500
});
}
});
Here's a CodePen using the anime.js CDN: Circular Progress Bar Smoothing
If you don't want to use a javascript library, then I'd recommend switching from the conic-gradient() to something else. I hear using an .svg circle with stroke and stroke-dasharray can work great with CSS transition.
You shouldn't setInterval your progress variable like this. instead, put it as a global variable outside the function then use it to gradually add 1 as long as the start value is less than progress, and you still can control the speed with your speed variable.
let progressBar = document.querySelector(".progressbar");
let progressBarValue = document.querySelector(".progressbar__value");
const body = document.querySelector("body");
let progressBarStartValue = 0;
let progressBarEndValue = 100;
let speed = 50;
let progress = 0;
body.addEventListener("click", function(e) {
if (progressBarStartValue === progressBarEndValue) {
alert("you have completed all the tasks");
} else {
progress += 10;
setInterval(() => {
if (progressBarStartValue < progress) {
progressBarStartValue += 1;
clearInterval();
}
progressBarValue.textContent = `${progressBarStartValue}%`;
progressBar.style.background = `conic-gradient(
#FFF ${progressBarStartValue * 3.6}deg,
#262623 ${progressBarStartValue * 3.6}deg
)`;
}, speed);
}
});
.progressbar {
position: relative;
height: 150px;
width: 150px;
background-color: #262623;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 3px solid red;
}
.progressbar::before {
content: "";
position: absolute;
height: 80%;
width: 80%;
background-color: #0f0f0f;
border-radius: 50%;
border: 3px solid blue;
}
.progressbar__value {
color: #fff;
z-index: 9;
font-size: 25px;
font-weight: 600;
}
<main class="main">
<section class="statistic">
<div class="container">
<div class="statistic__inner">
<div class="statistic__text">
<h2 class="statistic__title">You're almost there!</h2>
<p class="statistic__subtitle">keep up the good work</p>
</div>
<div class="progressbar"><span class="progressbar__value">0%</span></div>
</div>
</div>
</section>
</main>

How do I fix my code such that the linear interpolation doesn't cross over?

I'm trying to make an infinite marquee (scrolling horizontal text), but on scroll it speeds up the translation of the HTML elements. I am achieving this effect by using a linear interpolation function.
You can see the effect on this site that I'm trying to remake: https://altsdigital.com/ It says "Not your usual SEO agency"
Mine almost works - the problem is that when my HTML resets its position - my text overlaps and briefly translates to the left before correcting. Keep your eyes on the left side of the page. You will see that the text overlaps at one brief moment then translates left (during it's movement to the right), it eventually corrects itself as it plays retaining the original gap. You can see in this screenshot the "t" and "I" are overlapping. Shortly after this, the text on the left translates left and there is a gap between the letters. I want it to have a gap and not briefly translate left.
I have no idea how to fix this - I've tried calling the lerp function on scroll but nothing seems to change. Thanks in advance.
Here's the code:
const lerp = (current, target, factor) => {
let holder = current * (1 - factor) + target * factor;
holder = parseFloat(holder).toFixed(3);
return holder;
};
class LoopingText {
constructor(DOMElements) {
this.DOMElements = DOMElements;
this.lerpingData = {
counterOne: { current: 0, target: 0 },
counterTwo: { current: 100, target: 100 },
};
this.interpolationFactor = 0.1;
this.direction = true;
this.speed = 0.2;
this.render();
this.onScroll();
}
onScroll() {
window.addEventListener("scroll", () => {
this.lerpingData["counterOne"].target += this.speed * 5;
this.lerpingData["counterTwo"].target += this.speed * 5;
});
}
lerp() {
for (const counter in this.lerpingData) {
this.lerpingData[counter].current = lerp(
this.lerpingData[counter].current,
this.lerpingData[counter].target,
this.interpolationFactor
);
}
this.lerpingData["counterOne"].target += this.speed;
this.lerpingData["counterTwo"].target += this.speed;
if (this.lerpingData["counterOne"].target < 100) {
this.DOMElements[0].style.transform = `translate(${this.lerpingData["counterOne"].current}%, 0%)`;
} else {
this.lerpingData["counterOne"].current = -100;
this.lerpingData["counterOne"].target = -100;
}
if (this.lerpingData["counterTwo"].target < 100) {
this.DOMElements[1].style.transform = `translate(${this.lerpingData["counterTwo"].current}%, 0%)`;
} else {
this.lerpingData["counterTwo"].current = -100;
this.lerpingData["counterTwo"].target = -100;
}
}
render() {
this.lerp();
window.requestAnimationFrame(() => this.render());
}
}
let textArray = document.getElementsByClassName("item");
new LoopingText(textArray);
#import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Poppins";
}
.hero-section {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
position: relative;
width: 100%;
}
.loop-container {
position: relative;
width: 100%;
display: flex;
/* padding-right: 24px; */
}
.item {
position: absolute;
font-size: 15rem;
white-space: nowrap;
margin: 0;
}
span {
transition: all 0.2s;
cursor: default;
}
.hover:hover {
color: gray;
transition: all 0.2s;
}
<body>
<section class="hero-section">
<div class="loop-container">
<div class="item">Infinite Horizontal Looping Text</div>
<div class="item">Infinite Horizontal Looping Text</div>
</div>
</section>
<section class="hero-section">
</section>
</body>

How to make the element stop falling?

Below this text is my code - I am trying to create the chrome dino game, and so far everything is going well, but I have one problem - after the red block jumps, the dino doesn't stop falling down - while it should. I don't understand where my problem is, as I wrote about the same code yesterday and everything worked just fine. The gravity function can be found in the JavaScript under // Gravity.
Notice how it's code is very similar to the jump function, but it doesn't work as good as the jump function. Any help would be appreciated!!!!!
// HTML Elements + Other Variables
const floor1 = document.getElementById("floor1");
const floor2 = document.getElementById("floor2");
const floor3 = document.getElementById("floor3");
const floor4 = document.getElementById("floor4");
const floor5 = document.getElementById("floor5");
const floor6 = document.getElementById("floor6");
const floor7 = document.getElementById("floor7");
const dino = document.getElementById("dino");
const highBird = document.getElementById("highBird");
const lowBird = document.getElementById("lowBird");
const wideCactus = document.getElementById("wideCactus");
const thinCactus = document.getElementById("thinCactus");
let jump = 0;
// Floor Function
setTimeout(function () {
floor1.classList.add("floor1Animation");
}, 0);
setTimeout(function () {
floor2.classList.add("floor2Animation");
}, 1000);
setTimeout(function () {
floor3.classList.add("floor3Animation");
}, 2000);
setTimeout(function () {
floor4.classList.add("floor4Animation");
}, 3000);
setTimeout(function () {
floor5.classList.add("floor5Animation");
}, 4000);
setTimeout(function () {
floor6.classList.add("floor6Animation");
}, 5000);
setTimeout(function () {
floor7.classList.add("floor7Animation");
}, 6000);
// Jump
document.onkeydown = function (event) {
let key = event.key;
if (key == "ArrowUp") {
let jumpCount = 0;
if (dino.offsetTop == 95) {
let jumpInterval = setInterval(function () {
dino.style.top = (dino.offsetTop - 5) + "px";
jumpCount += 1;
jump = true;
if (jumpCount == 20) {
clearInterval(jumpInterval);
jump = false;
jumpCount = 0;
}
}, 10);
}
}
}
// Gravity
setInterval(function () {
if (jump == false) {
let jumpGravity = setInterval(function () {
dino.style.top = (dino.offsetTop + 5) + "px";
}, 10);
if (dino.offsetTop == 95) {
clearInterval(jumpGravity);
}
}
}, 10);
body {
margin: 0;
padding: 0;
justify-content: center;
align-items: center;
display: flex;
width: 100vw;
height: 100vh;
}
#gameBoard {
width: 1000px;
height: 150px;
border: 2px solid black;
overflow: hidden;
margin: auto;
position: relative;
background-color: white;
}
#dino {
width: 30px;
height: 50px;
background-color: red;
left: 10px;
top: 95px;
position: absolute;
}
.floorBackground {
position: relative;
height: 10px;
width: 200px;
display: flex;
justify-content: center;
align-items: center;
}
.floor {
position: absolute;
top: 140px;
height: 10px;
width: 200px;
}
#floor1 {
right: -200px;
background-color: red;
}
.floor1Animation {
animation: floorAnimation 6s infinite linear;
}
#floor2 {
right: -200px;
background-color: blue;
}
.floor2Animation {
animation: floorAnimation 6s infinite linear;
}
#floor3 {
right: -200px;
background-color: green;
}
.floor3Animation {
animation: floorAnimation 6s infinite linear;
}
#floor4 {
right: -200px;
background-color: purple;
}
.floor4Animation {
animation: floorAnimation 6s infinite linear;
}
#floor5 {
right: -200px;
background-color: brown;
}
.floor5Animation {
animation: floorAnimation 6s infinite linear;
}
#floor6 {
right: -200px;
background-color: orange;
}
.floor6Animation {
animation: floorAnimation 6s infinite linear;
}
#floor7 {
right: -200px;
background-color: yellow;
}
.floor7Animation {
animation: floorAnimation 6s infinite linear;
}
#keyframes floorAnimation {
from {
right: -200px;
}
to {
right: 1000px;
}
}
#keyframes jumping {
}
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<title>
Dino Game
</title>
<link rel = "stylesheet" type = "text/css" href = "DinoCSS.css">
</head>
<body>
<div id = "gameBoard">
<div id = "floor1" class = "floor">
<img src = "Pictures/dinoBackground.PNG" class = "floorBackground">
</div>
<div id = "floor2" class = "floor">
<img src = "Pictures/dinoBackground.PNG" class = "floorBackground">
</div>
<div id = "floor3" class = "floor">
<img src = "Pictures/dinoBackground.PNG" class = "floorBackground">
</div>
<div id = "floor4" class = "floor">
<img src = "Pictures/dinoBackground.PNG" class = "floorBackground">
</div>
<div id = "floor5" class = "floor">
<img src = "Pictures/dinoBackground.PNG" class = "floorBackground">
</div>
<div id = "floor6" class = "floor">
<img src = "Pictures/dinoBackground.PNG" class = "floorBackground">
</div>
<div id = "floor7" class = "floor">
<img src = "Pictures/dinoBackground.PNG" class = "floorBackground">
</div>
<div id = "dino"></div>
<div id = "highBird"></div>
<div id = "lowBird"></div>
<div id = "wideCactus"></div>
<div id = "thinCactus"></div>
</div>
<script type = "text/javascript" src = "DinoJS.js"></script>
</body>
</html>
You should try simplifying your code, it may make the issue easier to find or it may even remove it entirely.
I'd suggest using a velocity system, where every frame you adjust the dino's offsetTop by the dino's current velocity, and then subtract the gravity amount from the dino's current velocity.
This causes the velocity to decrease at a constant rate, but the dino's offsetTop position to decrease at an exponential pace, mimicking real gravity.
Then, to make collision work, just test if the dino is at or above the correct "offsetTop" value before subtracting the gravity. If you find that the dino is below the ground, just set the offsetTop to the ground level and clear the velocity.
I'd also suggest moving your game over to the HTML5 Canvas API, since then you just have to deal with properties of objects rather than DOM element style attributes.
Velocity example with canvas:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 10;
var y = 10;
var VelocityX = 0;
var VelocityY = 0;
const gravity = 0.75;
// width and height of the square
var width = 10;
var height = 10;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
x += VelocityX;
if( x > 500) {
x = 0;
}
if ( y >= 450 ) {
y = 450
VelocityY = 0
} else {
y += VelocityY;
VelocityY += gravity; // Higher values = lower position on canvas
}
ctx.fillRect(x, y, width, height);
}
setInterval(animate, 1000 / 60); // 60 FPS
#canvas {
border: 1px solid black;
}
<canvas id="canvas" width="500" height="500"></canvas>
JSFiddle

make the video fill the screen width and and remove the frame

I have created this frame for playing the movie which resizes based on the user's screen size.
Now I want to be able to remove the frame and fill the whole width of the user screen using another function but each time I failed.
Now I'm using injectViewportSizes() function. I want a new function to do this:
remove the frame completely without affecting anything else.
stretch the width of the movie until it fills the whole width of the screen.
of course stretching the movie is much simpler for me than removing the frame ... each time I've tried to remove it the whole movie removes or a distortion occurs for the rest of the elements.
Here is the code:
const clipSource = `https://langfox.ir/movie/movieclip/My_name_is_Edward_Bloom.mp4`;
const content = document.querySelector('.content');
const box = document.getElementById("box");
let video = document.createElement('video');
content.appendChild(video);
video.id = 'clip';
let clip = document.getElementById("clip");
clip.currentTime = 0;
let source = document.createElement('source');
source.src = clipSource;
source.type = 'video/mp4';
video.appendChild(source);
video.load();
setTimeout(() => {
injectViewportSizes(); // or goFull();
clip.play();
}, 3000);
function goFull(){
// Remove the frame and make the video fill the while 'Width' of the screen
}
function injectViewportSizes(){
let screenWidth = screen.width;
let screenHeight = screen.height;
let vwPixels = screenWidth / 100;
let clipWidth = clip.videoWidth;
let clipHeight = clip.videoHeight;
screenWidth = screenWidth - (screenWidth * 0.10); // available space to put the clip inside
screenHeight = screenHeight - (screenHeight * 0.10);
let clipWidthNew;
let clipHeightNew;
if(clipWidth > clipHeight){
clipWidthNew = clipWidth;
let ratio = clipWidth / clipHeight;
while(screenWidth < clipWidthNew) {
clipWidthNew--;
}
clipHeightNew = clipWidthNew / ratio;
} else {
clipHeightNew = clipHeight;
let ratio = clipWidth / clipHeight;
while(screenHeight < clipHeightNew) {
clipHeightNew--;
}
clipWidthNew = clipHeightNew * ratio;
}
let viewPortClipWidth = clipWidthNew * (100 / document.body.clientWidth);
let viewPortClipHeight = clipHeightNew / vwPixels;
document.querySelector('.box .content').style.width = `${viewPortClipWidth}vw`;
document.querySelector('.box .content').style.height = `${viewPortClipHeight}vw`;
}
video {
position: absolute;
top: 0;
left: 0;
max-width: 100%;
max-height: 100%;
width: 100%;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
}
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #0b0e12;
}
.box {
border-radius: 0.31vh;
position: relative;
overflow: hidden;
}
.box::after {
content: '';
position: absolute;
z-index: -1;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: repeating-linear-gradient(-45deg, white 0 0.48828125vw, #f00c36 0 0.9765625vw) 0 0/1.380859375vw 1.380859375vw;
width: calc(100vw + 1.380859375vw);
height: calc(100vh + 1.380859375vw);
}
.box .content {
position: relative;
max-width: 100vw;
max-height: 100vh;
box-shadow: 0 0 0.262vh black, 0 0 0.6553vh rgba(0, 0, 0, 1), inset 0 0 0.6553vh rgba(0, 0, 0, 1);
margin: 0.45vh;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id='box' class="box">
<div class="content"></div>
</div>
</body>
</html>
Note: it seems that this code snippet can't show the original frame correctly but it works in a raw HTML file locally.

Animating the percent of a "percentage bar" during the transition

I have created a very basic animated percentage bar with HTML, CSS, and JS the only problem is that I am trying to devise a way to also animate the increase and/or decrease of the percentage output to go along with an animated percentage bar. In the example below and in this JsFiddle I have successfully created that with the only problem being that it doesn't seem to be the most efficient or effective way of doing it.
In the code snippet directly below I'm creating this animated effect by...
Setting x equal to setInterval
Capturing the width of percent bar on the left and removing the px from the end of the string.
Capturing the width of percent bar on the right and removing the px from the end of the string.
Displays the percent value for the left (blue) bar inside the tooltip that can be seen when hovered over.
Displays the percent value for the right (red) bar inside the tooltip that can be seen when hovered over.
Displays the percent value of the left (blue) bar below the percent bar.
Displays the percent value of the right (red) bar below the percent bar.
All of this code below will run every 64 Milliseconds.
This code will only run for 2000 Milliseconds which is the same amount of time that I have set the transition for the percent bars.
Note: The whole point of the code below is to give the illusion that the percent values are increasing as either of the percent bars are increasing. In short, the goal is to make it seem more animated rather than the number all of a sudden seeing the number jump from one number to the next.
There just has to be a better way of achieving the same effect (or better) rather than pulling data from the DOM every 64 Milliseconds. There are tons of real-time graph's out on the web that achieve the same effect but I can't figure out how so I came up with my own and don't really think that they do it this way either. Any ideas??? I would only like to use pure Javascript with no libraries such as jQuery.
var x = setInterval(function() {
var left = parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
var right = parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
p_bar_left.querySelector('.percent-value').innerText = left.toFixed(2) + '%';
document.querySelector('#blue').querySelector('.percent-amount').innerText = left.toFixed(2) + '%';
p_bar_right.querySelector('.percent-value').innerText = right.toFixed(2) + '%';
document.querySelector('#red').querySelector('.percent-amount').innerText = right.toFixed(2) + '%';
}, 64);
setTimeout(function() {
clearInterval(x)
}, 2000);
var good = document.querySelector('#good');
var bad = document.querySelector('#bad');
var p_bar_left = document.querySelector('#progressbar-left');
var p_bar_right = document.querySelector('#progressbar-right');
var counter_left = 0;
var counter_right = 0;
var percent_left = 0;
var percent_right = 0;
function changePercent(increment, which) {
if (which == 'left') {
counter_left += increment;
} else if (which == 'right') {
counter_right += increment;
} else {
throw "Don't know which value to increase.";
}
percent_left = (counter_left / (counter_left + counter_right)) * 100;
percent_right = (counter_right / (counter_left + counter_right)) * 100;
p_bar_left.style.width = percent_left + '%';
p_bar_right.style.width = percent_right + '%';
document.querySelector('#total-amount').innerText = counter_right + counter_left;
var x = setInterval(function() {
var left = parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
var right = parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, '')) / (parseInt(window.getComputedStyle(p_bar_left).getPropertyValue('width').replace(/px/i, '')) + parseInt(window.getComputedStyle(p_bar_right).getPropertyValue('width').replace(/px/i, ''))) * 100;
p_bar_left.querySelector('.percent-value').innerText = left.toFixed(2) + '%';
document.querySelector('#blue').querySelector('.percent-amount').innerText = left.toFixed(2) + '%';
p_bar_right.querySelector('.percent-value').innerText = right.toFixed(2) + '%';
document.querySelector('#red').querySelector('.percent-amount').innerText = right.toFixed(2) + '%';
}, 64);
setTimeout(function() {
clearInterval(x)
}, 2000);
}
good.addEventListener('click', function() {
changePercent(1, 'left');
});
bad.addEventListener('click', function() {
changePercent(1, 'right');
});
var tooltip = document.querySelectorAll('.tooltip');
var tooltipelement = document.querySelectorAll('#progressbar-left, #progressbar-right');
for (var x = tooltipelement.length; x--;) {
tooltipelement[x].addEventListener('mousemove', function(e) {
for (var i = tooltip.length; i--;) {
tooltip[i].style.left = e.pageX + 20 + 'px';
tooltip[i].style.top = e.pageY + 'px';
}
});
}
#progressbar-container {
display: flex;
position: relative;
width: 50vw;
height: 32px;
border: 2px solid black;
background-color: #ccc;
justify-content: space-between;
}
#progressbar-left {
position: relative;
height: 100%;
background-color: blue;
transition: width 2s;
align-items: center;
justify-content: center;
}
#progressbar-right {
position: relative;
height: 100%;
background-color: red;
transition: width 2s;
align-items: center;
justify-content: center;
}
.tooltip {
display: none;
position: fixed;
width: auto;
height: auto;
padding: 6px;
background-color: black;
text-align: center;
border-radius: 6px;
z-index: 1;
}
.object {
display: inline-block;
color: #fff;
}
.percent-value {
display: inline-block;
color: #fff;
}
#progressbar-left:hover .tooltip {
display: block;
}
#progressbar-right:hover .tooltip {
display: block;
}
#total {
display: block;
font-weight: bold;
}
#total-amount {
display: inline-block;
font-weight: normal;
}
#blue,
#red {
display: block;
font-weight: bold;
}
.percent-amount {
display: inline-block;
font-weight: normal;
}
<body>
<input type="button" value="Good" id="good">
<input type="button" value="Bad" id="bad">
<div id="progressbar-container">
<div id="progressbar-left">
<div class="tooltip">
<span class="tooltiptext">
<span class="object">Blue</span>
<span class="percent-value"></span>
</span>
</div>
</div>
<div id="progressbar-right">
<div class="tooltip">
<span class="tooltiptext">
<span class="object">Red</span>
<span class="percent-value"></span>
</span>
</div>
</div>
</div>
<span id="total">Total: <p id="total-amount">0</p></span>
<span id="blue">Percent Blue: <p class="percent-amount">0%</p></span>
<span id="red">Percent Red: <p class="percent-amount">0%</p></span>
</body>
JsFiddle

Categories