I am making a feature on an app which allows the user to perform breathing exercises to do this I am using CSS animations. My issue is that I want to display exhale and inhale when an animation of a circle is shrinking and enlarging but my animation does start from the same point each time so this causes issues with the word that is displayed.
function toggleDropdown() {
var dropdownContent = document.getElementById("dropdown-content");
dropdownContent.classList.toggle("show");
}
var timer;
function selectExercise(exerciseId) {
// Hide all exercises
var exercises = document.getElementsByClassName("exercise");
for (var i = 0; i < exercises.length; i++) {
exercises[i].style.display = "none";
}
// Reset animation for all circles
var circles = document.getElementsByClassName("circle");
for (var i = 0; i < circles.length; i++) {
circles[i].style.animationDuration = "0ms";
circles[i].innerHTML = "Inhale";
circles[i].classList.remove("reset-animation");
void circles[i].offsetWidth; // Trigger reflow to apply the class immediately
circles[i].classList.add("reset-animation");
}
// Show selected exercise
var selectedExercise = document.getElementById(exerciseId);
selectedExercise.style.display = "block";
var dropdownContent = document.getElementById("dropdown-content");
dropdownContent.classList.remove("show");
clearInterval(timer);
var timerElement = document.getElementById("timer" + exerciseId.slice(-1));
timerElement.innerHTML = "";
}
function startAnimation(circleId, duration, totalCycles, timerId) {
var circle = document.getElementById(circleId);
var cycles = 0;
var remainingTime = duration * totalCycles;
var inhaleTime = duration / 2;
var exhaleTime = duration / 2;
clearInterval(timer);
animate();
function animate() {
circle.innerHTML = "Inhale";
setTimeout(function() {
circle.innerHTML = "Exhale";
setTimeout(function() {
circle.innerHTML = "Inhale";
cycles++;
if (cycles < totalCycles) {
animate(); // Start the next cycle
} else {
circle.style.animationDuration = "0ms"; // Stop the animation
}
}, inhaleTime);
}, exhaleTime);
}
circle.style.animationDuration = duration + "ms";
var timerElement = document.getElementById(timerId);
timerElement.innerHTML = "Time left: " + (remainingTime / 1000) + " seconds";
timer = setInterval(function() {
remainingTime -= 1000;
if (remainingTime <= 0) {
clearInterval(timer);
timerElement.innerHTML = "Time left: 0 seconds";
} else {
timerElement.innerHTML = "Time left: " + (remainingTime / 1000) + " seconds";
}
}, 1000);
}
selectExercise('exercise1', 4000);
body {
background: #005eb8;
margin: 0;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
font-family: "Helvetica Neue", Arial, sans-serif;
}
.container {
max-width: 350px;
margin: 0 auto;
border-radius: 5px;
overflow: hidden;
background: #ffffff;
padding: 20px;
}
h1 {
margin-bottom: 20px;
text-align: center;
color: #003087;
}
.dropdown {
position: relative;
}
.button {
width: 100%;
padding: 10px;
margin-bottom: 1px;
color: #ffffff;
background: #0072ce;
border: none;
cursor: pointer;
border-radius: 5px;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #ffffff;
width: 100%;
border-radius: 5px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.15);
z-index: 1;
}
.dropdown-content a {
display: block;
padding: 10px;
color: #003087;
text-decoration: none;
}
.dropdown:hover .dropdown-content {
display: block;
}
.exercise {
display: none;
padding: 2px;
background-color: #ffffff;
border-radius: 5px;
margin-top: 0px;
text-align: center;
color: #003087;
}
.exercise h2 {
margin-top: 10px;
margin-bottom: 10px;
}
.exercise p {
margin-top: 10px;
margin-bottom: 20px;
}
.circle-wrapper {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-bottom: 20px;
}
.circle {
width: 200px;
height: 200px;
background-color: #4BC0C0;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 24px;
font-weight: bold;
animation-name: breathe;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
margin-bottom: 5px;
}
@keyframes breathe {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
.start-button {
padding: 10px;
color: #ffffff;
background: #0072ce;
width: 100%;
border: none;
cursor: pointer;
border-radius: 5px;
font-weight: bold;
margin-top: 5px;
}
.start-button:hover {
background: #003087;
}
<div class="container">
<h1>Breathing Exercises</h1>
<div class="dropdown">
<button class="button" onclick="toggleDropdown()">Select Exercise</button>
<div id="dropdown-content" class="dropdown-content">
<a href="#" onclick="selectExercise('exercise1')">Exercise 1</a>
<a href="#" onclick="selectExercise('exercise2')">Exercise 2</a>
<a href="#" onclick="selectExercise('exercise3')">Exercise 3</a>
<a href="#" onclick="selectExercise('exercise4')">Exercise 4</a>
</div>
</div>
<div id="exercise1" class="exercise">
<h2>Exercise 1</h2>
<p>This is the description for Exercise 1.</p>
<div class="circle-wrapper">
<div id="circle1" class="circle"></div>
</div>
<div id="timer1" class="timer"></div>
<button class="start-button" onclick="startAnimation('circle1', 10000, 5, 'timer1')">Start</button>
</div>
<div id="exercise2" class="exercise" style="display: none;">
<h2>Exercise 2</h2>
<p>This is the description for Exercise 2.</p>
<div class="circle-wrapper">
<div id="circle2" class="circle"></div>
</div>
<div id="timer2" class="timer"></div>
<button class="start-button" onclick="startAnimation('circle2', 6000, 3, 'timer2')">Start</button>
</div>
<div id="exercise3" class="exercise" style="display: none;">
<h2>Exercise 3</h2>
<p>This is the description for Exercise 3.</p>
<div class="circle-wrapper">
<div id="circle3" class="circle"></div>
</div>
<div id="timer3" class="timer"></div>
<button class="start-button" onclick="startAnimation('circle3', 8000, 2, 'timer3')">Start</button>
</div>
<div id="exercise4" class="exercise" style="display: none;">
<h2>Exercise 4</h2>
<p>This is the description for Exercise 4.</p>
<div class="circle-wrapper">
<div id="circle4" class="circle"></div>
</div>
<div id="timer4" class="timer"></div>
<button class="start-button" onclick="startAnimation('circle4', 4000, 10, 'timer4')">Start</button>
</div>
</div>
I have tried different methods for resetting the animation each time a new exercise is selected however I’ve been unsuccessful.
2
Answers
This can be done quite easily using a css transition, manipulating a css custom property for the transition time duration, and adding/removing a class.
https://css-tricks.com/updating-a-css-variable-with-javascript/
I would do this by listening to the animationiteration event, and toggling your text when it fires.