├── Mouse-Event ├── index.js ├── index.html └── style.css ├── Quote-Generator ├── Stopwatch │ ├── index.html │ ├── style.css │ └── index.js ├── index.html ├── index.js └── style.css ├── Clock ├── index.js ├── index.html └── style.css ├── Dice-Roll-Simulator ├── index.html ├── index.js └── style.css ├── Emoji-Rating ├── index.js ├── style.css └── index.html ├── Age-Calculator ├── index.html ├── index.js └── style.css ├── Anime-Pic-Generator ├── index.html ├── index.js └── style.css ├── Basic-Calculator ├── script.js ├── index.html └── style.css ├── BMI-Calculator ├── script.js ├── index.html └── style.css ├── Currency-Converter ├── style.css ├── index.js └── index.html ├── I Love You Animation ├── style.css ├── index.html └── script.js ├── Recipe-Book-App ├── index.js ├── index.html └── style.css └── README.md /Mouse-Event/index.js: -------------------------------------------------------------------------------- 1 | const containerEl = document.querySelector(".container"); 2 | 3 | window.addEventListener("mousemove", (event) => { 4 | containerEl.innerHTML = ` 5 |
6 | ${event.clientX} 7 |

Mouse X Prosition(px)

8 |
9 |
10 | ${event.clientY} 11 |

Mouse Y Prosition(px)

12 |
13 | `; 14 | }); -------------------------------------------------------------------------------- /Quote-Generator/Stopwatch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Stopwatch 8 | 9 | 10 | 11 | 12 |
00:00:00
13 |
14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /Mouse-Event/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Mouse Event 8 | 9 | 10 | 11 |
12 |
13 | 20 14 |

Mouse X Prosition(px)

15 |
16 |
17 | 20 18 |

Mouse Y Prosition(px)

19 |
20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Clock/index.js: -------------------------------------------------------------------------------- 1 | const hour = document.querySelector(".hour"); 2 | const minute = document.querySelector(".minute"); 3 | const second = document.querySelector(".second"); 4 | 5 | function setDate() { 6 | const now = new Date(); 7 | 8 | const getSecond = now.getSeconds(); 9 | const getMinute = now.getMinutes(); 10 | const getHour = now.getHours(); 11 | 12 | const secondDegree = (getSecond / 60) * 360; 13 | const minuteDegree = (getMinute / 60) * 360; 14 | const hourDegree = (getHour / 12) * 360; 15 | 16 | second.style.transform = `rotate(${secondDegree}deg)`; 17 | minute.style.transform = `rotate(${minuteDegree}deg)`; 18 | hour.style.transform = `rotate(${hourDegree}deg)`; 19 | } 20 | 21 | setInterval(setDate, 1000); -------------------------------------------------------------------------------- /Dice-Roll-Simulator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dice Roll Simulator 8 | 9 | 10 | 11 |

Dice Roll Simulator

12 |
13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Emoji-Rating/index.js: -------------------------------------------------------------------------------- 1 | const starsEl = document.querySelectorAll(".fa-star"); 2 | const emojisEl = document.querySelectorAll(".far"); 3 | const colorsArray = ["red", "orange", "lightblue", "lightgreen", "green"]; 4 | 5 | updateRating(0); 6 | 7 | starsEl.forEach((starEl, index) => { 8 | starEl.addEventListener("click", () => { 9 | updateRating(index); 10 | }); 11 | }); 12 | 13 | function updateRating(index) { 14 | starsEl.forEach((starEl, idx) => { 15 | if (idx < index + 1) { 16 | starEl.classList.add("active"); 17 | } else { 18 | starEl.classList.remove("active"); 19 | } 20 | }); 21 | 22 | emojisEl.forEach((emojiEl) => { 23 | emojiEl.style.transform = `translateX(-${index * 50}px)`; 24 | emojiEl.style.color = colorsArray[index]; 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /Age-Calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Age Calculator 8 | 9 | 10 | 11 |
12 |

Age Calculator

13 |
14 | 15 | 16 | 17 |

Your age is 21 years old

18 |
19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Anime-Pic-Generator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Anime Pics Generator 8 | 9 | 10 | 11 |
12 |

Anime Pics Generator

13 | 14 |
15 | 16 |

Anime Name

17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Basic-Calculator/script.js: -------------------------------------------------------------------------------- 1 | const buttonsEl = document.querySelectorAll("button"); 2 | 3 | const inputFieldEl = document.getElementById("result"); 4 | 5 | for (let i = 0; i < buttonsEl.length; i++) { 6 | buttonsEl[i].addEventListener("click", () => { 7 | const buttonValue = buttonsEl[i].textContent; 8 | if (buttonValue === "C") { 9 | clearResult(); 10 | } else if (buttonValue === "=") { 11 | calculateResult(); 12 | } else { 13 | appendValue(buttonValue); 14 | } 15 | }); 16 | } 17 | 18 | function clearResult() { 19 | inputFieldEl.value = ""; 20 | } 21 | 22 | function calculateResult() { 23 | inputFieldEl.value = eval(inputFieldEl.value); 24 | } 25 | 26 | function appendValue(buttonValue) { 27 | inputFieldEl.value += buttonValue; 28 | // inputFieldEl.value = inputFieldEl.value + buttonValue; 29 | } -------------------------------------------------------------------------------- /BMI-Calculator/script.js: -------------------------------------------------------------------------------- 1 | const btnEl = document.getElementById("btn"); 2 | const bmiInputEl = document.getElementById("bmi-result"); 3 | const weightConditionEl = document.getElementById("weight-condition"); 4 | 5 | function calculateBMI() { 6 | const heightValue = document.getElementById("height").value / 100; 7 | const weightValue = document.getElementById("weight").value; 8 | 9 | const bmiValue = weightValue / (heightValue * heightValue); 10 | 11 | bmiInputEl.value = bmiValue; 12 | 13 | if (bmiValue < 18.5) { 14 | weightConditionEl.innerText = "Under weight"; 15 | } else if (bmiValue >= 18.5 && bmiValue <= 24.9) { 16 | weightConditionEl.innerText = "Normal weight"; 17 | } else if (bmiValue >= 25 && bmiValue <= 29.9) { 18 | weightConditionEl.innerText = "Overweight"; 19 | } else if (bmiValue >= 30) { 20 | weightConditionEl.innerText = "Obesity"; 21 | } 22 | } 23 | 24 | btnEl.addEventListener("click", calculateBMI); -------------------------------------------------------------------------------- /Currency-Converter/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background-color: yellow; 3 | display: flex; 4 | height: 100vh; 5 | justify-content: center; 6 | align-items: center; 7 | font-family: 'Courier New', Courier, monospace; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | 12 | .container{ 13 | background-color: darkcyan; 14 | color: aliceblue; 15 | padding: 10px; 16 | border-radius: 5px; 17 | text-align: center; 18 | } 19 | 20 | 21 | .currency-container{ 22 | padding: 20px; 23 | display: flex; 24 | justify-content: space-between; 25 | } 26 | 27 | .currency-container select{ 28 | padding: 10px; 29 | } 30 | 31 | .currency-container input{ 32 | border: 0; 33 | background: transparent; 34 | font-size: 25px; 35 | text-align: right; 36 | color: aliceblue; 37 | } 38 | 39 | .exchange-rate{ 40 | font-size: 16px; 41 | font-weight: 600; 42 | } 43 | 44 | select:focus, 45 | input:focus{ 46 | outline: 0; 47 | } 48 | -------------------------------------------------------------------------------- /Clock/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Clock 9 | 10 | 11 |
12 |
13 |
12
14 |
3
15 |
6
16 |
9
17 |
18 |
19 |
20 |
21 |
22 |
23 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /Age-Calculator/index.js: -------------------------------------------------------------------------------- 1 | const btnEl = document.getElementById("btn"); 2 | const birthdayEl = document.getElementById("birthday"); 3 | const resultEl = document.getElementById("result"); 4 | 5 | function calculateAge() { 6 | const birthdayValue = birthdayEl.value; 7 | if (birthdayValue === "") { 8 | alert("Please enter your birthday"); 9 | } else { 10 | const age = getAge(birthdayValue); 11 | resultEl.innerText = `Your age is ${age} ${age > 1 ? "years" : "year"} old`; 12 | } 13 | } 14 | 15 | function getAge(birthdayValue) { 16 | const currentDate = new Date(); 17 | const birthdayDate = new Date(birthdayValue); 18 | let age = currentDate.getFullYear() - birthdayDate.getFullYear(); 19 | const month = currentDate.getMonth() - birthdayDate.getMonth(); 20 | 21 | if ( 22 | month < 0 || 23 | (month === 0 && currentDate.getDate() < birthdayDate.getDate()) 24 | ) { 25 | age--; 26 | } 27 | 28 | return age; 29 | } 30 | 31 | btnEl.addEventListener("click", calculateAge); -------------------------------------------------------------------------------- /Emoji-Rating/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | display: flex; 4 | justify-content: center; 5 | height: 100vh; 6 | align-items: center; 7 | background-color: yellow; 8 | } 9 | 10 | .feedback-container { 11 | background-color: white; 12 | width: 400px; 13 | height: 200px; 14 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); 15 | border-radius: 10px; 16 | position: relative; 17 | } 18 | 19 | .emoji-container { 20 | position: absolute; 21 | left: 50%; 22 | transform: translateX(-50%); 23 | top: 20%; 24 | width: 50px; 25 | height: 50px; 26 | border-radius: 50%; 27 | display: flex; 28 | overflow: hidden; 29 | } 30 | 31 | .far { 32 | margin: 1px; 33 | transform: translateX(0); 34 | transition: transform 0.2s; 35 | } 36 | 37 | .rating-container { 38 | position: absolute; 39 | left: 50%; 40 | transform: translateX(-50%); 41 | bottom: 20%; 42 | } 43 | 44 | .fa-star { 45 | color: lightgray; 46 | cursor: pointer; 47 | } 48 | 49 | .fa-star.active { 50 | color: gold; 51 | } 52 | -------------------------------------------------------------------------------- /Mouse-Event/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | display: flex; 4 | justify-content: center; 5 | height: 100vh; 6 | align-items: center; 7 | background-color: rgb(90, 85, 79); 8 | color: white; 9 | font-family: cursive; 10 | } 11 | 12 | .container { 13 | display: flex; 14 | text-align: center; 15 | } 16 | 17 | .mouse-event { 18 | border: solid 1px rgb(165, 130, 130); 19 | margin: 40px; 20 | min-width: 180px; 21 | min-height: 100px; 22 | position: relative; 23 | justify-content: center; 24 | align-items: center; 25 | display: flex; 26 | font-size: 30px; 27 | } 28 | 29 | .mouse-event h4 { 30 | position: absolute; 31 | top: -50px; 32 | font-size: 14px; 33 | left: 50%; 34 | transform: translateX(-50%); 35 | white-space: nowrap; 36 | font-weight: 100; 37 | color: rgb(245, 243, 243); 38 | } 39 | 40 | @media (max-width: 500px) { 41 | .container { 42 | flex-direction: column; 43 | } 44 | } -------------------------------------------------------------------------------- /Anime-Pic-Generator/index.js: -------------------------------------------------------------------------------- 1 | const btnEl = document.getElementById("btn"); 2 | const animeContainerEl = document.querySelector(".anime-container"); 3 | const animeImgEl = document.querySelector(".anime-img"); 4 | const amineNameEl = document.querySelector(".anime-name"); 5 | 6 | btnEl.addEventListener("click", async function () { 7 | try { 8 | btnEl.disabled = true; 9 | btnEl.innerText = "Loading..."; 10 | amineNameEl.innerText = "Updating..."; 11 | animeImgEl.src = "spinner.svg"; 12 | const response = await fetch("https://api.catboys.com/img"); 13 | const data = await response.json(); 14 | btnEl.disabled = false; 15 | btnEl.innerText = "Get Anime"; 16 | animeContainerEl.style.display = "block"; 17 | animeImgEl.src = data.url; 18 | amineNameEl.innerText = data.artist; 19 | } catch (error) { 20 | console.log(error); 21 | btnEl.disabled = false; 22 | btnEl.innerText = "Get Anime"; 23 | amineNameEl.innerText = "An error happened, please try again"; 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /BMI-Calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BMI Calculator 8 | 9 | 10 | 11 |
12 |

Body Mass Index (BMI) Calculator

13 | Your Height (cm): 14 | 15 | Your Weight (kg): 16 | 17 | 18 | 19 |

Weight Condition:

20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /Quote-Generator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Random Quote Generator 8 | 9 | 10 | 11 | 12 |
13 |

Random Quote Generator

14 |

15 | 16 | Quote 17 | 18 | 19 |

20 |

~ Author

21 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /Quote-Generator/index.js: -------------------------------------------------------------------------------- 1 | const btnEl = document.getElementById("btn"); 2 | const quoteEl = document.getElementById("quote"); 3 | const authorEl = document.getElementById("author"); 4 | 5 | const apiURL = "https://api.quotable.io/random"; 6 | 7 | async function getQuote() { 8 | try { 9 | btnEl.innerText = "Loading..."; 10 | btnEl.disabled = true; 11 | quoteEl.innerText = "Updating..."; 12 | authorEl.innerText = "Updating..."; 13 | const response = await fetch(apiURL); 14 | const data = await response.json(); 15 | const quoteContent = data.content; 16 | const quoteAuthor = data.author; 17 | quoteEl.innerText = quoteContent; 18 | authorEl.innerText = "~ " + quoteAuthor; 19 | btnEl.innerText = "Get a quote"; 20 | btnEl.disabled = false; 21 | console.log(data); 22 | } catch (error) { 23 | console.log(error); 24 | quoteEl.innerText = "An error happened, try again later"; 25 | authorEl.innerText = "An error happened"; 26 | btnEl.innerText = "Get a quote"; 27 | btnEl.disabled = false; 28 | } 29 | } 30 | 31 | getQuote() 32 | 33 | btnEl.addEventListener("click", getQuote); -------------------------------------------------------------------------------- /Currency-Converter/index.js: -------------------------------------------------------------------------------- 1 | const currencyFirstEl = document.getElementById("currency-first"); 2 | 3 | const worthFirstEl = document.getElementById("worth-first"); 4 | 5 | const currencySecondEl = document.getElementById("currency-second"); 6 | 7 | const worthSecondEl = document.getElementById("worth-second"); 8 | 9 | const exchangeRateEl = document.getElementById("exchange-rate"); 10 | 11 | updateRate() 12 | 13 | function updateRate() { 14 | fetch( 15 | `https://v6.exchangerate-api.com/v6/5f9d1c87f7250159c9c9b17d/latest/${currencyFirstEl.value}` 16 | ) 17 | .then((res) => res.json()) 18 | .then((data) => { 19 | const rate = data.conversion_rates[currencySecondEl.value]; 20 | console.log(rate); 21 | exchangeRateEl.innerText = `1 ${currencyFirstEl.value} = ${ 22 | rate + " " + currencySecondEl.value 23 | }`; 24 | 25 | worthSecondEl.value = (worthFirstEl.value * rate).toFixed(2) 26 | }); 27 | } 28 | 29 | currencyFirstEl.addEventListener("change", updateRate); 30 | 31 | currencySecondEl.addEventListener("change", updateRate); 32 | 33 | worthFirstEl.addEventListener("input", updateRate); -------------------------------------------------------------------------------- /BMI-Calculator/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | margin: 0; 3 | background: linear-gradient(to left bottom, lightgreen, lightblue); 4 | display: flex; 5 | min-height: 100vh; 6 | justify-content: center; 7 | align-items: center; 8 | font-family: 'Courier New', Courier, monospace; 9 | } 10 | 11 | .container{ 12 | background: rgba(255,255,255, .3); 13 | padding: 20px; 14 | display: flex; 15 | flex-direction: column; 16 | border-radius: 5px; 17 | box-shadow: 0 10px 10px rgba(0,0,0,.3); 18 | margin: 5px; 19 | } 20 | 21 | .heading{ 22 | font-size: 30px; 23 | } 24 | 25 | .input{ 26 | padding: 10px 20px; 27 | font-size: 18px; 28 | background: rgba(255,255,255, .4); 29 | border-color: rgba(255,255,255, .5); 30 | margin: 10px; 31 | } 32 | 33 | .btn{ 34 | background-color: lightgreen; 35 | border: none; 36 | padding: 10px 20px; 37 | border-radius: 5px; 38 | margin: 10px; 39 | font-size: 20px; 40 | box-shadow: 0 0 4px rgba(0,0,0,.3); 41 | cursor: pointer; 42 | } 43 | 44 | .btn:hover{ 45 | box-shadow: 0 0 8px rgba(0,0,0,.3); 46 | transition: all 300ms ease; 47 | } 48 | 49 | .info-text{ 50 | font-size: 20px; 51 | font-weight: 500; 52 | } -------------------------------------------------------------------------------- /Anime-Pic-Generator/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | margin: 0; 3 | background: linear-gradient(to right, lightblue, yellow); 4 | display: flex; 5 | height: 100vh; 6 | justify-content: center; 7 | align-items: center; 8 | font-family: 'Courier New', Courier, monospace; 9 | } 10 | 11 | .container{ 12 | background: aliceblue; 13 | border-radius: 10px; 14 | box-shadow: 0 10px 20px rgba(0,0,0,0.3); 15 | text-align: center; 16 | padding: 10px; 17 | width: 450px; 18 | margin: 5px; 19 | } 20 | 21 | .btn{ 22 | background-color: green; 23 | color: aliceblue; 24 | padding: 10px 30px; 25 | font-size: 16px; 26 | margin-bottom: 30px; 27 | border-radius: 6px; 28 | cursor: pointer; 29 | 30 | } 31 | 32 | .btn:disabled{ 33 | background-color: gray; 34 | cursor: not-allowed; 35 | } 36 | 37 | .anime-img{ 38 | height: 300px; 39 | width: 300px; 40 | border-radius: 50%; 41 | border: 3px solid green; 42 | } 43 | 44 | .anime-name{ 45 | margin: 20px; 46 | background-color: green; 47 | color: aliceblue; 48 | padding: 10px; 49 | border-radius: 6px; 50 | font-size: 17px; 51 | font-weight: 600; 52 | } 53 | 54 | .anime-container{ 55 | display: none; 56 | } 57 | -------------------------------------------------------------------------------- /I Love You Animation/style.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | html, body { 8 | min-height: 100vh; 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | background-color: #ec64e6; 13 | font-size: 62.5%; 14 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 15 | } 16 | @media screen and (max-width: 520px) { 17 | html, body { 18 | /* don't know how to set default units to rem in mojs :(( */ 19 | } 20 | } 21 | 22 | .container { 23 | width: 50rem; 24 | height: 20rem; 25 | position: relative; 26 | } 27 | 28 | .svg-container { 29 | z-index: 2; 30 | position: absolute; 31 | } 32 | 33 | .mo-container { 34 | width: 100%; 35 | height: 100%; 36 | } 37 | 38 | .line { 39 | fill: none; 40 | stroke: #FFFFFF; 41 | stroke-width: 8; 42 | stroke-linecap: round; 43 | stroke-miterlimit: 10; 44 | } 45 | 46 | .lttr { 47 | fill: #763C8C; 48 | } 49 | 50 | .sound { 51 | position: fixed; 52 | color: #763C8C; 53 | font-size: 1.6rem; 54 | bottom: 1rem; 55 | right: 1rem; 56 | text-decoration: underline; 57 | cursor: default; 58 | } 59 | .sound--off { 60 | text-decoration: line-through; 61 | } 62 | -------------------------------------------------------------------------------- /Quote-Generator/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | display: flex; 4 | min-height: 100vh; 5 | justify-content: center; 6 | align-items: center; 7 | font-family: "Courier New", Courier, monospace; 8 | background: linear-gradient(to left bottom, lightgreen, lightblue); 9 | } 10 | 11 | .container { 12 | background-color: rgba(255, 255, 255, 0.1); 13 | box-shadow: 0 6px 10px rgba(0, 0, 0, 0.3); 14 | padding: 30px; 15 | border-radius: 15px; 16 | width: 90%; 17 | margin: 10px; 18 | text-align: center; 19 | } 20 | 21 | .heading { 22 | font-size: 35px; 23 | font-weight: 700; 24 | } 25 | 26 | .quote { 27 | font-size: 30px; 28 | font-weight: 600; 29 | } 30 | 31 | .author { 32 | font-size: 25px; 33 | margin: 10px; 34 | font-style: italic; 35 | } 36 | 37 | .btn { 38 | font-size: 18px; 39 | border-radius: 5px; 40 | cursor: pointer; 41 | padding: 10px; 42 | margin-top: 15px; 43 | background-color: rgba(255, 255, 255, 0.3); 44 | border-color: rgba(255, 255, 255, 0.6); 45 | text-transform: uppercase; 46 | width: 300px; 47 | } 48 | 49 | .btn:hover{ 50 | background-color: rgba(255,255,255,.6); 51 | box-shadow: 0 4px 4px rgba(0,0,0,.3); 52 | transition: all 300ms ease-in-out; 53 | color: green; 54 | } -------------------------------------------------------------------------------- /Age-Calculator/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 20px; 4 | font-family: "Montserrat", sans-serif; 5 | background-color: #f7f7f7; 6 | } 7 | 8 | .container { 9 | background-color: white; 10 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); 11 | padding: 20px; 12 | max-width: 600px; 13 | margin: 0 auto; 14 | border-radius: 5px; 15 | margin-top: 50px; 16 | } 17 | 18 | h1 { 19 | font-size: 36px; 20 | text-align: center; 21 | margin-top: 0; 22 | margin-bottom: 20px; 23 | } 24 | 25 | .form { 26 | display: flex; 27 | flex-direction: column; 28 | align-items: center; 29 | } 30 | 31 | label { 32 | font-weight: bold; 33 | margin-bottom: 10px; 34 | } 35 | 36 | input { 37 | padding: 8px; 38 | border: 1px solid #ccc; 39 | border-radius: 5px; 40 | width: 100%; 41 | max-width: 300px; 42 | } 43 | 44 | button { 45 | background-color: #007bff; 46 | color: white; 47 | border: none; 48 | padding: 10px 20px; 49 | border-radius: 5px; 50 | margin-top: 10px; 51 | cursor: pointer; 52 | transition: background-color 0.3s ease; 53 | } 54 | 55 | button:hover { 56 | background-color: #0062cc; 57 | } 58 | 59 | #result { 60 | margin-top: 20px; 61 | font-size: 24px; 62 | font-weight: bold; 63 | } -------------------------------------------------------------------------------- /Quote-Generator/Stopwatch/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f0f0f0; 3 | font-family: "Poppins", sans-serif; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | min-height: 100vh; 8 | overflow: hidden; 9 | align-items: center; 10 | } 11 | 12 | #timer { 13 | font-size: 7rem; 14 | font-weight: 700; 15 | text-shadow: 2px 2px #f8a5c2; 16 | color: #f92672; 17 | width: 600px; 18 | text-align: center; 19 | margin: 40px auto; 20 | } 21 | 22 | #buttons { 23 | display: flex; 24 | justify-content: center; 25 | } 26 | 27 | button { 28 | background-color: #f92672; 29 | color: white; 30 | border: none; 31 | font-size: 2rem; 32 | font-weight: bold; 33 | padding: 1.5rem 4rem; 34 | margin: 1rem; 35 | border-radius: 30px; 36 | cursor: pointer; 37 | box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.3); 38 | transition: all 0.2s; 39 | } 40 | 41 | button:hover { 42 | background-color: #f44583; 43 | box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.5); 44 | } 45 | 46 | button[disabled] { 47 | opacity: 0.5; 48 | cursor: default; 49 | } 50 | 51 | @media (max-width: 800px) { 52 | #timer { 53 | font-size: 4rem; 54 | width: 350px; 55 | } 56 | 57 | button { 58 | font-size: 1.5rem; 59 | padding: 1rem 2rem; 60 | } 61 | } -------------------------------------------------------------------------------- /Emoji-Rating/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Emoji Rating 8 | 15 | 16 | 17 | 18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 |
33 |
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Basic-Calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Basic Calculator 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 | -------------------------------------------------------------------------------- /Dice-Roll-Simulator/index.js: -------------------------------------------------------------------------------- 1 | const buttonEl = document.getElementById("roll-button"); 2 | 3 | const diceEl = document.getElementById("dice"); 4 | 5 | const rollHistoryEl = document.getElementById("roll-history"); 6 | 7 | let historyList = []; 8 | 9 | function rollDice() { 10 | const rollResult = Math.floor(Math.random() * 6) + 1; 11 | const diceFace = getDiceFace(rollResult); 12 | diceEl.innerHTML = diceFace; 13 | historyList.push(rollResult); 14 | updateRollHistory(); 15 | } 16 | 17 | function updateRollHistory() { 18 | rollHistoryEl.innerHTML = ""; 19 | for (let i = 0; i < historyList.length; i++) { 20 | const listItem = document.createElement("li"); 21 | listItem.innerHTML = `Roll ${i + 1}: ${getDiceFace( 22 | historyList[i] 23 | )}`; 24 | rollHistoryEl.appendChild(listItem); 25 | } 26 | } 27 | 28 | function getDiceFace(rollResult) { 29 | switch (rollResult) { 30 | case 1: 31 | return "⚀"; 32 | case 2: 33 | return "⚁"; 34 | case 3: 35 | return "⚂"; 36 | case 4: 37 | return "⚃"; 38 | case 5: 39 | return "⚄"; 40 | case 6: 41 | return "⚅"; 42 | default: 43 | return ""; 44 | } 45 | } 46 | 47 | buttonEl.addEventListener("click", () => { 48 | diceEl.classList.add("roll-animation"); 49 | setTimeout(() => { 50 | diceEl.classList.remove("roll-animation"); 51 | rollDice(); 52 | }, 1000); 53 | }); -------------------------------------------------------------------------------- /Dice-Roll-Simulator/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Open Sans", sans-serif; 3 | text-align: center; 4 | margin: 0; 5 | } 6 | 7 | h1 { 8 | font-size: 3rem; 9 | margin-top: 2rem; 10 | } 11 | 12 | .dice { 13 | font-size: 7rem; 14 | margin: 5px; 15 | animation-duration: 1s; 16 | animation-fill-mode: forwards; 17 | } 18 | 19 | .roll-animation { 20 | animation-name: roll; 21 | } 22 | 23 | @keyframes roll { 24 | 0% { 25 | transform: rotateY(0deg) rotateX(0deg); 26 | } 27 | 28 | 100% { 29 | transform: rotateY(720deg) rotateX(720deg); 30 | } 31 | } 32 | 33 | button { 34 | background-color: #47a5c4; 35 | color: white; 36 | font-size: 1.5rem; 37 | padding: 1rem 2rem; 38 | border: none; 39 | border-radius: 1rem; 40 | cursor: pointer; 41 | transition: background-color 0.3s ease; 42 | } 43 | 44 | button:hover { 45 | background-color: #2e8baf; 46 | } 47 | 48 | ul { 49 | list-style: none; 50 | padding: 0; 51 | max-width: 600px; 52 | margin: 2rem auto; 53 | } 54 | 55 | li { 56 | font-size: 1.5rem; 57 | padding: 0.5rem; 58 | margin: 0.5rem; 59 | background-color: #f2f2f2; 60 | border-radius: 0.5rem; 61 | box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3); 62 | display: flex; 63 | justify-content: space-between; 64 | align-items: center; 65 | } 66 | 67 | li span { 68 | font-size: 3rem; 69 | margin-right: 1rem; 70 | } -------------------------------------------------------------------------------- /Basic-Calculator/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | } 5 | 6 | .calculator { 7 | background-color: #f2f2f2; 8 | padding: 20px; 9 | max-width: 400px; 10 | margin: 0 auto; 11 | border: solid 1px #ccc; 12 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); 13 | border-radius: 5px; 14 | margin-top: 40px; 15 | } 16 | 17 | #result{ 18 | width: 100%; 19 | padding: 10px; 20 | font-size: 24px; 21 | border: none; 22 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3) inset; 23 | border-radius: 5px; 24 | } 25 | 26 | .buttons{ 27 | display: grid; 28 | grid-template-columns: repeat(4, 1fr); 29 | grid-gap: 10px; 30 | margin-top: 20px; 31 | } 32 | 33 | button{ 34 | padding: 10px; 35 | font-size: 24px; 36 | border: none; 37 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); 38 | border-radius: 5px; 39 | cursor: pointer; 40 | transition: background-color 0.3s ease; 41 | 42 | } 43 | 44 | button:hover{ 45 | background-color: #ddd; 46 | } 47 | 48 | .clear{ 49 | background-color: #ff4136; 50 | color: #fff; 51 | } 52 | 53 | .number, .decimal{ 54 | background-color: #fff; 55 | color: #333; 56 | 57 | } 58 | 59 | .operator{ 60 | background-color: #0074d9; 61 | color: #fff; 62 | } 63 | 64 | .equals{ 65 | background-color: #01ff70; 66 | grid-row: span 3; 67 | color: #fff; 68 | } -------------------------------------------------------------------------------- /Recipe-Book-App/index.js: -------------------------------------------------------------------------------- 1 | const API_KEY = "275d58779ccf4e22af03e792e8819fff"; 2 | const recipeListEl = document.getElementById("recipe-list"); 3 | 4 | function displayRecipes(recipes) { 5 | recipeListEl.innerHTML = ""; 6 | recipes.forEach((recipe) => { 7 | const recipeItemEl = document.createElement("li"); 8 | recipeItemEl.classList.add("recipe-item"); 9 | recipeImageEl = document.createElement("img"); 10 | recipeImageEl.src = recipe.image; 11 | recipeImageEl.alt = "recipe image"; 12 | 13 | recipeTitleEl = document.createElement("h2"); 14 | recipeTitleEl.innerText = recipe.title; 15 | 16 | recipeIngredientsEl = document.createElement("p"); 17 | recipeIngredientsEl.innerHTML = ` 18 | Ingredients: ${recipe.extendedIngredients 19 | .map((ingredient) => ingredient.original) 20 | .join(", ")} 21 | `; 22 | 23 | recipeLinkEl = document.createElement("a"); 24 | recipeLinkEl.href = recipe.sourceUrl; 25 | recipeLinkEl.innerText = "View Recipe"; 26 | 27 | recipeItemEl.appendChild(recipeImageEl); 28 | recipeItemEl.appendChild(recipeTitleEl); 29 | recipeItemEl.appendChild(recipeIngredientsEl); 30 | recipeItemEl.appendChild(recipeLinkEl); 31 | recipeListEl.appendChild(recipeItemEl); 32 | }); 33 | } 34 | 35 | async function getRecipes() { 36 | const response = await fetch( 37 | `https://api.spoonacular.com/recipes/random?number=10&apiKey=${API_KEY}` 38 | ); 39 | 40 | const data = await response.json(); 41 | 42 | return data.recipes; 43 | } 44 | 45 | async function init() { 46 | const recipes = await getRecipes(); 47 | displayRecipes(recipes); 48 | } 49 | 50 | init(); 51 | -------------------------------------------------------------------------------- /Currency-Converter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Currency Converter 8 | 9 | 10 | 11 |
12 |

Currency Converter

13 |
14 | 23 | 24 |
25 |
26 | 35 | 36 |
37 |

1 USD = 138.5802 JPY

38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Quote-Generator/Stopwatch/index.js: -------------------------------------------------------------------------------- 1 | const timerEl = document.getElementById("timer"); 2 | const startButtonEl = document.getElementById("start"); 3 | const stopButtonEl = document.getElementById("stop"); 4 | const resetButtonEl = document.getElementById("reset"); 5 | 6 | let startTime = 0; 7 | let elapsedTime = 0; 8 | let timerInterval; 9 | 10 | function startTimer() { 11 | startTime = Date.now() - elapsedTime; 12 | 13 | timerInterval = setInterval(() => { 14 | elapsedTime = Date.now() - startTime; 15 | timerEl.textContent = formatTime(elapsedTime); 16 | }, 10); 17 | 18 | startButtonEl.disabled = true; 19 | stopButtonEl.disabled = false; 20 | } 21 | 22 | function formatTime(elapsedTime) { 23 | const milliseconds = Math.floor((elapsedTime % 1000) / 10); 24 | const seconds = Math.floor((elapsedTime % (1000 * 60)) / 1000); 25 | const minutes = Math.floor((elapsedTime % (1000 * 60 * 60)) / (1000 * 60)); 26 | const hours = Math.floor(elapsedTime / (1000 * 60 * 60)); 27 | return ( 28 | (hours ? (hours > 9 ? hours : "0" + hours) : "00") + 29 | ":" + 30 | (minutes ? (minutes > 9 ? minutes : "0" + minutes) : "00") + 31 | ":" + 32 | (seconds ? (seconds > 9 ? seconds : "0" + seconds) : "00") + 33 | "." + 34 | (milliseconds > 9 ? milliseconds : "0" + milliseconds) 35 | ); 36 | } 37 | function stopTimer() { 38 | clearInterval(timerInterval); 39 | startButtonEl.disabled = false; 40 | stopButtonEl.disabled = true; 41 | } 42 | function resetTimer() { 43 | clearInterval(timerInterval); 44 | 45 | elapsedTime = 0; 46 | timerEl.textContent = "00:00:00"; 47 | 48 | startButtonEl.disabled = false; 49 | stopButtonEl.disabled = true; 50 | } 51 | 52 | startButtonEl.addEventListener("click", startTimer); 53 | stopButtonEl.addEventListener("click", stopTimer); 54 | resetButtonEl.addEventListener("click", resetTimer); -------------------------------------------------------------------------------- /Recipe-Book-App/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Recipe Book App 8 | 9 | 10 | 11 |
12 |

Recipe Book App

13 |
14 | 15 |
16 | 54 |
55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Recipe-Book-App/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: Arial, sans-serif; 5 | } 6 | 7 | header { 8 | background: #0c2461; 9 | color: #fff; 10 | padding: 20px; 11 | text-align: center; 12 | } 13 | 14 | h1 { 15 | margin: 0; 16 | font-size: 36px; 17 | } 18 | 19 | .container { 20 | margin: 0 auto; 21 | max-width: 1200px; 22 | padding: 20px; 23 | } 24 | 25 | .recipe-list { 26 | list-style: none; 27 | margin: 0; 28 | padding: 0; 29 | } 30 | 31 | .recipe-item { 32 | display: flex; 33 | align-items: center; 34 | justify-content: space-between; 35 | margin-bottom: 20px; 36 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); 37 | border-radius: 5px; 38 | overflow: hidden; 39 | } 40 | 41 | .recipe-item img { 42 | width: 150px; 43 | height: 150px; 44 | object-fit: cover; 45 | } 46 | 47 | .recipe-item h2 { 48 | margin: 0; 49 | font-size: 20px; 50 | padding: 10px; 51 | min-width: 200px; 52 | } 53 | 54 | .recipe-item p { 55 | margin: 0; 56 | padding: 10px; 57 | color: #777; 58 | } 59 | 60 | .recipe-item a { 61 | background: #0c2461; 62 | color: #fff; 63 | min-width: 150px; 64 | padding: 10px; 65 | text-decoration: none; 66 | text-transform: uppercase; 67 | font-size: 14px; 68 | transition: background 0.3s ease; 69 | } 70 | 71 | .recipe-item a:hover { 72 | background: #1e3799; 73 | } 74 | 75 | @media screen and (max-width: 768px) { 76 | .container { 77 | max-width: 90%; 78 | } 79 | .recipe-item { 80 | flex-direction: column; 81 | } 82 | 83 | .recipe-item img { 84 | width: 100%; 85 | height: auto; 86 | margin-bottom: 10px; 87 | } 88 | 89 | .recipe-item h2 { 90 | font-size: 20px; 91 | padding: 0; 92 | margin-bottom: 10px; 93 | } 94 | 95 | .recipe-item p { 96 | font-size: 14px; 97 | margin-bottom: 10px; 98 | } 99 | 100 | .recipe-item a { 101 | width: 100%; 102 | text-align: center; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Clock/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Courier New', Courier, monospace; 5 | height: 100vh; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | background-color: salmon; 10 | } 11 | 12 | img { 13 | position: absolute; 14 | top: 60px; 15 | left: 50%; 16 | transform: translateX(-50%); 17 | width: 70px; 18 | z-index: 2; 19 | } 20 | 21 | .clock { 22 | width: 350px; 23 | height: 350px; 24 | background-color: lightgray; 25 | border-radius: 100%; 26 | border: 5px solid darkgrey; 27 | box-shadow: 1px 1px 4px rgba(0,0,0,.7); 28 | position: relative; 29 | } 30 | 31 | 32 | 33 | .numbers div { 34 | position: absolute; 35 | font-size: 27px; 36 | font-weight: bold; 37 | color: lightgoldenrodyellow; 38 | text-shadow: 1px 1px 2px rgba(0,0,0,.7); 39 | } 40 | 41 | .twelve { 42 | top: 6px; 43 | left: 50%; 44 | transform: translateX(-50%); 45 | } 46 | 47 | .three { 48 | right: 6px; 49 | top: 50%; 50 | transform: translateY(-50%); 51 | } 52 | 53 | .six { 54 | bottom: 6px; 55 | left: 50%; 56 | transform: translateX(-50%); 57 | } 58 | 59 | .nine { 60 | left: 6px; 61 | top: 50%; 62 | transform: translateY(-50%); 63 | } 64 | 65 | .arrows { 66 | width: 100%; 67 | height: 100%; 68 | display: flex; 69 | justify-content: center; 70 | align-items: center; 71 | } 72 | 73 | .arrows::before { 74 | content: ""; 75 | width: 25px; 76 | height: 25px; 77 | background-color: darkgreen; 78 | border-radius: 50%; 79 | box-shadow: 1px 1px 2px rgba(0,0,0,.7); 80 | z-index: 4; 81 | } 82 | 83 | .arrows div { 84 | width: 7px; 85 | height: 120px; 86 | background-color: white; 87 | position: absolute; 88 | bottom: 50%; 89 | box-shadow: 1px 1px 2px rgba(0,0,0,.7); 90 | border-radius: 50% 50% 0 0; 91 | transform-origin: bottom center; 92 | z-index: 3; 93 | } 94 | 95 | .arrows .hour { 96 | height: 80px; 97 | transform: rotate(30deg); 98 | } 99 | 100 | .arrows .second { 101 | background-color: goldenrod; 102 | transform: rotate(250deg); 103 | 104 | } -------------------------------------------------------------------------------- /I Love You Animation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | I Love You 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 | 31 | 34 | 37 |
sound
38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web-Development-Projects ===> 2 | 3 | ## Link of all projects :👉 [Click Here](https://link-to-repo.netlify.app/)👈 4 | 5 | 6 | 1) [Age-Calculator](https://github.com/Shubham-Bhoite/Web-Development-Project/tree/main/Age-Calculator) : 👉 [Live-Demo](https://person-age-calculator.netlify.app/) 7 | 8 | 2) [BMI-Calculator](https://github.com/Shubham-Bhoite/Web-Development-Project/tree/main/BMI-Calculator) : 👉 [Live-Demo](https://quetelet-index-calculator.netlify.app/) 9 | 10 | 3) [Basic-Calculator](https://github.com/Shubham-Bhoite/Web-Development-Project/tree/main/Basic-Calculator) : 👉 [Live-Demo](https://finance-machine.netlify.app/) 11 | 12 | 4) [Clock](https://github.com/Shubham-Bhoite/Web-Development-Project/tree/main/Clock) : 👉[Live-Demo](https://live-wallclock.netlify.app/) 13 | 14 | 5) [Quote-Generator](https://github.com/Shubham-Bhoite/Web-Development-Project/tree/main/Quote-Generator) : 👉[Live-Demo](https://ordered-quote-generator.netlify.app/) 15 | 16 | 6) [StopWatch](https://github.com/Shubham-Bhoite/Web-Development-Project/tree/main/Quote-Generator/Stopwatch) : 17 | 👉[Live-Demo](https://stop-watchtimer.netlify.app/) 18 | 19 | 7) [Mouse-Event](https://github.com/Shubham-Bhoite/Web-Development-Project/tree/main/Mouse-Event) : 20 | 👉[Live-Demo](https://event-mouse.netlify.app/) 21 | 22 | 8) [Dice-Roll-Simulator](https://github.com/Shubham-Bhoite/Web-Development-Project-For-Beginners/tree/main/Dice-Roll-Simulator) : 23 | 👉[Live-Demo](https://dice-roll-simulator.netlify.app/) 24 | 25 | 9) [Anime-Pic-Generator](https://github.com/Shubham-Bhoite/Web-Development-Project-For-Beginners/tree/main/Anime-Pic-Generator) : 26 | 👉[Live-Demo](https://anime-pics.netlify.app/) 27 | 28 | 10) [Currency-Converter](https://github.com/Shubham-Bhoite/Web-Development-Project-For-Beginners/tree/main/Currency-Converter) : 29 | 👉[Live-Demo](https://currency-converting-web.netlify.app/) 30 | 31 | 11) [Emoji-Rating](https://github.com/Shubham-Bhoite/Web-Development-Project-For-Beginners/tree/main/Emoji-Rating) : 32 | 👉[Live-Demo](https://emotion-rating.netlify.app/) 33 | 34 | 12) [I Love You Animation](https://github.com/Shubham-Bhoite/Web-Development-Project-For-Beginners/tree/main/I%20Love%20You%20Animation) : 35 | 👉[Live Demo](https://i-love-you-animation.netlify.app/) 36 | 37 | 13) [Recipe Book App](https://github.com/Shubham-Bhoite/Web-Development-Project-For-Beginners/tree/main/Recipe-Book-App) : 38 | 👉[Live-Demo](https://recipe-booking-app.netlify.app/) 39 | 40 | 14) [To-do List](https://github.com/Shubham-Bhoite/OIBGRIP/tree/main/To-Do-App) : 41 | 👉[Live-Demo](https://shubham-bhoite-to-do-app.netlify.app/) 42 | 43 | 15) [Flipcart-Landing-page-Clone](https://github.com/Shubham-Bhoite/Flipkart-Landing-page-Clone/tree/main/Flipcart-Landing-page-Clone) : 44 | 👉[Live-Demo](https://flip-landing-page-clone.netlify.app/) 45 | 46 | 16) [Memory Card Game](https://github.com/Shubham-Bhoite/Memory-Card-Game) : 47 | 👉[Live-Demo](https://onlinematching-game.netlify.app/) 48 | 49 | 17) [Dictionary App](https://github.com/Shubham-Bhoite/Dictionary-App) : 50 | 👉[Live-Demo](https://dictionary-app-online.netlify.app/) 51 | 52 | 18) [My Portfolio](https://github.com/Shubham-Bhoite/My-Portfolio) : 53 | 👉[Live-Demo](https://portfolio-shubhu.netlify.app/) 54 | 55 | 19) [GateMasters ](https://github.com/Shubham-Bhoite/GateMasters) : 56 | 👉[Live-Demo](https://www.gatemasters.tech/) 57 | 58 | 20) [Link To Repo](https://github.com/Shubham-Bhoite/Link-to-Repo) : 59 | 👉[Live-Demo](https://link-to-repo.netlify.app/) 60 | -------------------------------------------------------------------------------- /I Love You Animation/script.js: -------------------------------------------------------------------------------- 1 | const qs = document.querySelector.bind(document); 2 | const easingHeart = mojs.easing.path('M0,100C2.9,86.7,33.6-7.3,46-7.3s15.2,22.7,26,22.7S89,0,100,0'); 3 | 4 | const el = { 5 | container: qs('.mo-container'), 6 | 7 | i: qs('.lttr--I'), 8 | l: qs('.lttr--L'), 9 | o: qs('.lttr--O'), 10 | v: qs('.lttr--V'), 11 | e: qs('.lttr--E'), 12 | y: qs('.lttr--Y'), 13 | o2: qs('.lttr--O2'), 14 | u: qs('.lttr--U'), 15 | 16 | lineLeft: qs('.line--left'), 17 | lineRight: qs('.line--rght'), 18 | 19 | colTxt: "#763c8c", 20 | colHeart: "#fa4843", 21 | 22 | blup: qs('.blup'), 23 | blop: qs('.blop'), 24 | sound: qs('.sound') }; 25 | 26 | 27 | class Heart extends mojs.CustomShape { 28 | getShape() { 29 | return ''; 30 | } 31 | getLength() {return 200;}} 32 | 33 | mojs.addShape('heart', Heart); 34 | 35 | const crtBoom = (delay = 0, x = 0, rd = 46) => { 36 | parent = el.container; 37 | const crcl = new mojs.Shape({ 38 | shape: 'circle', 39 | fill: 'none', 40 | stroke: el.colTxt, 41 | strokeWidth: { 5: 0 }, 42 | radius: { [rd]: [rd + 20] }, 43 | easing: 'quint.out', 44 | duration: 500 / 3, 45 | parent, 46 | delay, 47 | x }); 48 | 49 | 50 | const brst = new mojs.Burst({ 51 | radius: { [rd + 15]: 110 }, 52 | angle: 'rand(60, 180)', 53 | count: 3, 54 | timeline: { delay }, 55 | parent, 56 | x, 57 | children: { 58 | radius: [5, 3, 7], 59 | fill: el.colTxt, 60 | scale: { 1: 0, easing: 'quad.in' }, 61 | pathScale: [.8, null], 62 | degreeShift: ['rand(13, 60)', null], 63 | duration: 1000 / 3, 64 | easing: 'quint.out' } }); 65 | 66 | 67 | 68 | return [crcl, brst]; 69 | }; 70 | 71 | const crtLoveTl = () => { 72 | const move = 1000; 73 | const boom = 200; 74 | const easing = 'sin.inOut'; 75 | const easingBoom = 'sin.in'; 76 | const easingOut = 'sin.out'; 77 | const opts = { duration: move, easing, opacity: 1 }; 78 | const delta = 150; 79 | 80 | return new mojs.Timeline().add([ 81 | new mojs.Tween({ 82 | duration: move, 83 | onStart: () => { 84 | [el.i, el.l, el.o, el.v, el.e, el.y, el.o2, el.u].forEach(el => { 85 | el.style.opacity = 1; 86 | el.style = 'transform: translate(0px, 0px) rotate(0deg) skew(0deg, 0deg) scale(1, 1); opacity: 1;'; 87 | }); 88 | }, 89 | onComplete: () => { 90 | [el.l, el.o, el.v, el.e].forEach(el => el.style.opacity = 0); 91 | el.blop.play(); 92 | } }), 93 | 94 | 95 | new mojs.Tween({ 96 | duration: move * 2 + boom, 97 | onComplete: () => { 98 | [el.y, el.o2].forEach(el => el.style.opacity = 0); 99 | el.blop.play(); 100 | } }), 101 | 102 | 103 | new mojs.Tween({ 104 | duration: move * 3 + boom * 2 - delta, 105 | onComplete: () => { 106 | el.i.style.opacity = 0; 107 | el.blop.play(); 108 | } }), 109 | 110 | 111 | new mojs.Tween({ 112 | duration: move * 3 + boom * 2, 113 | onComplete: () => { 114 | el.u.style.opacity = 0; 115 | el.blup.play(); 116 | } }), 117 | 118 | 119 | new mojs.Tween({ 120 | duration: 50, 121 | delay: 4050, 122 | onUpdate: progress => { 123 | [el.i, el.l, el.o, el.v, el.e, el.y, el.o2, el.u].forEach(el => { 124 | el.style = `transform: translate(0px, 0px) rotate(0deg) skew(0deg, 0deg) scale(1, 1); opacity: ${1 * progress};`; 125 | }); 126 | }, 127 | onComplete: () => { 128 | [el.i, el.l, el.o, el.v, el.e, el.y, el.o2, el.u].forEach(el => { 129 | el.style.opacity = 1; 130 | el.style = 'transform: translate(0px, 0px) rotate(0deg) skew(0deg, 0deg) scale(1, 1); opacity: 1;'; 131 | }); 132 | } }), 133 | 134 | 135 | new mojs.Html({ 136 | ...opts, 137 | el: el.lineLeft, 138 | x: { 0: 52 } }). 139 | then({ 140 | duration: boom + move, 141 | easing, 142 | x: { to: 52 + 54 } }). 143 | then({ 144 | duration: boom + move, 145 | easing, 146 | x: { to: 52 + 54 + 60 } }). 147 | then({ 148 | duration: 150, // 3550 149 | easing, 150 | x: { to: 52 + 54 + 60 + 10 } }). 151 | then({ 152 | duration: 300 }). 153 | then({ 154 | duration: 350, 155 | x: { to: 0 }, 156 | easing: easingOut }), 157 | 158 | 159 | new mojs.Html({ 160 | ...opts, 161 | el: el.lineRight, 162 | x: { 0: -52 } }). 163 | then({ 164 | duration: boom + move, 165 | easing, 166 | x: { to: -52 - 54 } }). 167 | then({ 168 | duration: boom + move, 169 | easing, 170 | x: { to: -52 - 54 - 60 } }). 171 | then({ 172 | duration: 150, 173 | easing, 174 | x: { to: -52 - 54 - 60 - 10 } }). 175 | then({ 176 | duration: 300 }). 177 | then({ 178 | duration: 350, 179 | x: { to: 0 }, 180 | easing: easingOut }), 181 | 182 | 183 | new mojs.Html({ // [I] LOVE YOU 184 | ...opts, 185 | el: el.i, 186 | x: { 0: 34 } }). 187 | then({ 188 | duration: boom, 189 | easing: easingBoom, 190 | x: { to: 34 + 19 } }). 191 | then({ 192 | duration: move, 193 | easing, 194 | x: { to: 34 + 19 + 40 } }). 195 | then({ 196 | duration: boom, 197 | easing: easingBoom, 198 | x: { to: 34 + 19 + 40 + 30 } }). 199 | then({ 200 | duration: move, 201 | easing, 202 | x: { to: 34 + 19 + 40 + 30 + 30 } }), 203 | 204 | 205 | new mojs.Html({ // I [L]OVE YOU 206 | ...opts, 207 | el: el.l, 208 | x: { 0: 15 } }), 209 | 210 | 211 | new mojs.Html({ // I L[O]VE YOU 212 | ...opts, 213 | el: el.o, 214 | x: { 0: 11 } }), 215 | 216 | 217 | new mojs.Html({ // I LO[V]E YOU 218 | ...opts, 219 | el: el.v, 220 | x: { 0: 3 } }), 221 | 222 | 223 | new mojs.Html({ // I LOV[E] YOU 224 | ...opts, 225 | el: el.e, 226 | x: { 0: -3 } }), 227 | 228 | 229 | new mojs.Html({ // I LOVE [Y]OU 230 | ...opts, 231 | el: el.y, 232 | x: { 0: -20 } }). 233 | then({ 234 | duration: boom, 235 | easing: easingBoom, 236 | x: { to: -20 - 33 } }). 237 | then({ 238 | duration: move, 239 | easing, 240 | x: { to: -20 - 33 - 24 } }), 241 | 242 | 243 | new mojs.Html({ // I LOVE Y[O]U 244 | ...opts, 245 | el: el.o2, 246 | x: { 0: -27 } }). 247 | then({ 248 | duration: boom, 249 | easing: easingBoom, 250 | x: { to: -27 - 27 } }). 251 | then({ 252 | duration: move, 253 | easing, 254 | x: { to: -27 - 27 - 30 } }), 255 | 256 | 257 | new mojs.Html({ // I LOVE YO[U] 258 | ...opts, 259 | el: el.u, 260 | x: { 0: -32 } }). 261 | then({ 262 | duration: boom, 263 | easing: easingBoom, 264 | x: { to: -32 - 21 } }). 265 | then({ 266 | duration: move, 267 | easing, 268 | x: { to: -32 - 21 - 36 } }). 269 | then({ 270 | duration: boom, 271 | easing: easingBoom, 272 | x: { to: -32 - 21 - 36 - 31 } }). 273 | then({ 274 | duration: move, 275 | easing, 276 | x: { to: -32 - 21 - 36 - 31 - 27 } }), 277 | 278 | 279 | new mojs.Shape({ 280 | parent: el.container, 281 | shape: 'heart', 282 | delay: move, 283 | fill: el.colHeart, 284 | x: -64, 285 | scale: { 0: 0.95, easing: easingHeart }, 286 | duration: 500 }). 287 | then({ 288 | x: { to: -62, easing }, 289 | scale: { to: 0.65, easing }, 290 | duration: boom + move - 500 }). 291 | then({ 292 | duration: boom - 50, 293 | x: { to: -62 + 48 }, 294 | scale: { to: 0.90 }, 295 | easing: easingBoom }). 296 | then({ 297 | duration: 125, 298 | scale: { to: 0.8 }, 299 | easing: easingOut }). 300 | then({ 301 | duration: 125, 302 | scale: { to: 0.85 }, 303 | easing: easingOut }). 304 | then({ 305 | duration: move - 200, 306 | scale: { to: 0.45 }, 307 | easing }). 308 | then({ 309 | delay: -75, 310 | duration: 150, 311 | x: { to: 0 }, 312 | scale: { to: 0.90 }, 313 | easing: easingBoom }). 314 | then({ 315 | duration: 125, 316 | scale: { to: 0.8 }, 317 | easing: easingOut }). 318 | then({ 319 | duration: 125, // 3725 320 | scale: { to: 0.85 }, 321 | easing: easingOut }). 322 | then({ 323 | duration: 125 // 3850 324 | }).then({ 325 | duration: 350, 326 | scale: { to: 0 }, 327 | easing: easingOut }), 328 | 329 | 330 | ...crtBoom(move, -64, 46), 331 | ...crtBoom(move * 2 + boom, 18, 34), 332 | ...crtBoom(move * 3 + boom * 2 - delta, -64, 34), 333 | ...crtBoom(move * 3 + boom * 2, 45, 34)]); 334 | 335 | }; 336 | 337 | const loveTl = crtLoveTl().play(); 338 | setInterval(() => {loveTl.replay();}, 4300); 339 | 340 | const volume = 0.2; 341 | el.blup.volume = volume; 342 | el.blop.volume = volume; 343 | 344 | const toggleSound = () => { 345 | let on = true; 346 | return () => { 347 | if (on) { 348 | el.blup.volume = 0.0; 349 | el.blop.volume = 0.0; 350 | el.sound.classList.add('sound--off'); 351 | } else 352 | { 353 | el.blup.volume = volume; 354 | el.blop.volume = volume; 355 | el.sound.classList.remove('sound--off'); 356 | } 357 | on = !on; 358 | }; 359 | }; 360 | el.sound.addEventListener('click', toggleSound()); 361 | --------------------------------------------------------------------------------