Skip to content

Commit 38eabc9

Browse files
committed
feat: day 74
1 parent ba35b6e commit 38eabc9

File tree

3 files changed

+152
-17
lines changed

3 files changed

+152
-17
lines changed

074-relaxer app/index.html

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,24 @@ <h1>Relaxer</h1>
1111
<div class="container" id="container">
1212
<div class="circle"></div>
1313
<p id="text"></p>
14-
<div class="pointer-container">
14+
<div class="pointer-container" id="pointer-container">
1515
<span class="pointer"></span>
1616
</div>
1717
<div class="gradient-circle"></div>
1818
</div>
19+
<div class="controls">
20+
<!-- Add a Start/Stop Button -->
21+
<button id="toggle-btn" class="btn">Stop</button>
22+
<div>
23+
<!-- Make Breathing Presets Selectable -->
24+
<select id="preset-select" class="btn">
25+
<option value="4-7-8">Deep relaxation</option>
26+
<option value="4-4-4">Balance</option>
27+
<option value="4-4-6">Stress reduction</option>
28+
<option value="6-3-6">Alertness</option>
29+
</select>
30+
</div>
31+
</div>
1932
<script src="script.js"></script>
2033
</body>
2134
</html>

074-relaxer app/script.js

Lines changed: 97 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,116 @@
11
const container = document.getElementById("container");
22
const text = document.getElementById("text");
3+
const toggle = document.getElementById("toggle-btn");
4+
const pointerContainer = document.getElementById("pointer-container");
5+
const presetSelect = document.getElementById("preset-select");
36

47
// The 4-7-8 breathing technique: https://www.medicalnewstoday.com/articles/324417
5-
const totalTime = 19000;
6-
const breatheTime = 4000;
7-
const holdTime = 7000;
8+
// const totalTime = 19000;
9+
// const breatheTime = 4000;
10+
// const holdTime = 7000;
11+
12+
// Make Breathing Presets Selectable
13+
const presets = {
14+
"4-7-8": { breathe: 4000, hold: 7000, exhale: 8000 },
15+
"4-4-4": { breathe: 4000, hold: 4000, exhale: 4000 },
16+
"4-4-6": { breathe: 4000, hold: 4000, exhale: 6000 },
17+
"6-3-6": { breathe: 6000, hold: 3000, exhale: 6000 },
18+
};
19+
20+
let breatheTime = presets["4-7-8"].breathe;
21+
let holdTime = presets["4-7-8"].hold;
22+
let exhaleTime = presets["4-7-8"].exhale;
23+
let totalTime = breatheTime + holdTime + exhaleTime;
24+
25+
let breathInterval = null;
26+
let holdTimeout = null;
27+
let shrinkTimeout = null;
28+
29+
// Synchronize Pointer Animation
30+
// document.documentElement.style.setProperty(
31+
// "--total-time",
32+
// `${totalTime / 1000}s`
33+
// );
34+
35+
function updateTimings(presetKey) {
36+
const preset = presets[presetKey];
37+
breatheTime = preset.breathe;
38+
holdTime = preset.hold;
39+
exhaleTime = preset.exhale;
40+
totalTime = breatheTime + holdTime + exhaleTime;
41+
document.documentElement.style.setProperty(
42+
"--total-time",
43+
`${totalTime / 1000}s`
44+
);
45+
updateGradientStops();
46+
}
47+
48+
// Sync Conic Gradient with Preset Timings
49+
function updateGradientStops() {
50+
const breathePercent = (breatheTime / totalTime) * 100;
51+
const holdPercent = (holdTime / totalTime) * 100;
52+
const breatheEnd = breathePercent;
53+
const holdEnd = breathePercent + holdPercent;
54+
document.documentElement.style.setProperty("--breathe-end", `${breatheEnd}%`);
55+
document.documentElement.style.setProperty("--hold-end", `${holdEnd}%`);
56+
}
857

958
function breatheAnimation() {
59+
// Refactor Animation Durations
60+
container.style.setProperty("--grow-duration", `${breatheTime / 1000}s`);
1061
text.innerText = "Breathe In!";
1162
container.className = "container grow";
1263

13-
setTimeout(() => {
64+
holdTimeout = setTimeout(() => {
1465
text.innerText = "Hold";
1566

16-
setTimeout(() => {
67+
shrinkTimeout = setTimeout(() => {
68+
container.style.setProperty(
69+
"--shrink-duration",
70+
// `${(totalTime - breatheTime - holdTime) / 1000}s`
71+
`${exhaleTime / 1000}s`
72+
);
1773
text.innerText = "Breathe Out!";
1874
container.className = "container shrink";
1975
}, holdTime);
2076
}, breatheTime);
2177
}
2278

23-
setInterval(breatheAnimation, totalTime);
79+
// Add a Start/Stop Button
80+
function startBreathing() {
81+
pointerContainer.classList.remove("paused");
82+
pointerContainer.style.animation = "none";
83+
void pointerContainer.offsetWidth;
84+
pointerContainer.style.animation = "";
85+
breatheAnimation();
86+
breathInterval = setInterval(breatheAnimation, totalTime);
87+
}
88+
89+
function stopBreathing() {
90+
clearInterval(breathInterval);
91+
clearTimeout(holdTimeout);
92+
clearTimeout(shrinkTimeout);
93+
breathInterval = null;
94+
holdTimeout = null;
95+
shrinkTimeout = null;
96+
pointerContainer.classList.add("paused");
97+
}
98+
99+
toggle.addEventListener("click", () => {
100+
if (breathInterval) {
101+
stopBreathing();
102+
toggle.textContent = "Start";
103+
} else {
104+
startBreathing();
105+
toggle.textContent = "Stop";
106+
}
107+
});
108+
109+
presetSelect.addEventListener("change", (e) => {
110+
stopBreathing();
111+
updateTimings(e.target.value);
112+
startBreathing();
113+
});
24114

25115
// Init
26-
breatheAnimation();
116+
startBreathing();

074-relaxer app/style.css

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
}
1616

1717
body {
18-
background: var(--background-color) url('https://images.unsplash.com/photo-1601142754133-ae7aa279c3d5?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=80') no-repeat center center/cover;
18+
background: var(--background-color)
19+
url("https://images.unsplash.com/photo-1601142754133-ae7aa279c3d5?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=80")
20+
no-repeat center center/cover;
1921
color: var(--light-color);
2022
font-family: "Montserrat", sans-serif;
2123
display: flex;
@@ -57,12 +59,13 @@ h1 {
5759
}
5860

5961
.gradient-circle {
62+
/* Sync Conic Gradient with Preset Timings */
6063
background: conic-gradient(
6164
var(--gradient-color-light) 0%,
62-
var(--gradient-color-light-border) 21%,
63-
var(--light-color) 21%,
64-
var(--light-color) 58%,
65-
var(--gradient-color-dark-border) 58%,
65+
var(--gradient-color-light-border) var(--breathe-end, 21%),
66+
var(--light-color) var(--breathe-end, 21%),
67+
var(--light-color) var(--hold-end, 58%),
68+
var(--gradient-color-dark-border) var(--hold-end, 58%),
6669
var(--gradient-color-dark) 100%
6770
);
6871
width: 320px;
@@ -88,7 +91,8 @@ h1 {
8891
left: 140px;
8992
width: 20px;
9093
height: 190px;
91-
animation: rotate 19s linear forwards infinite;
94+
/* Synchronize Pointer Animation */
95+
animation: rotate var(--total-time, 19s) linear forwards infinite;
9296
transform-origin: bottom center;
9397
}
9498

@@ -101,8 +105,36 @@ h1 {
101105
}
102106
}
103107

108+
.pointer-container.paused {
109+
animation-play-state: paused;
110+
}
111+
112+
/* Make Breathing Presets Selectable */
113+
.controls {
114+
position: absolute;
115+
bottom: 20px;
116+
left: 50%;
117+
transform: translateX(-50%);
118+
display: flex;
119+
align-items: center;
120+
gap: 10px;
121+
}
122+
123+
/* Add a Start/Stop Button */
124+
.btn {
125+
padding: 10px 20px;
126+
background-color: var(--gradient-color-dark);
127+
color: var(--light-color);
128+
border: none;
129+
border-radius: 5px;
130+
cursor: pointer;
131+
font-family: inherit;
132+
font-size: 1rem;
133+
}
134+
104135
.container.grow {
105-
animation: grow 4s linear forwards;
136+
/* Refactor Animation Durations */
137+
animation: grow var(--grow-duration, 4s) linear forwards;
106138
}
107139

108140
@keyframes grow {
@@ -115,7 +147,7 @@ h1 {
115147
}
116148

117149
.container.shrink {
118-
animation: shrink 8s linear forwards;
150+
animation: shrink var(--shrink-duration, 8s) linear forwards;
119151
}
120152

121153
@keyframes shrink {
@@ -125,4 +157,4 @@ h1 {
125157
to {
126158
transform: scale(1);
127159
}
128-
}
160+
}

0 commit comments

Comments
 (0)