├── 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 |
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 |
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 |
17 | -
18 |
22 | Recipe 1
23 |
24 | Ingredients: Ingredient 1, Ingredient 2, Ingredient
25 | 3
26 |
27 | View Recipe
28 |
29 | -
30 |
34 | Recipe 2
35 |
36 | Ingredients: Ingredient 1, Ingredient 2, Ingredient
37 | 3
38 |
39 | View Recipe
40 |
41 | -
42 |
46 | Recipe 3
47 |
48 | Ingredients: Ingredient 1, Ingredient 2, Ingredient
49 | 3
50 |
51 | View Recipe
52 |
53 |
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 |
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 |
--------------------------------------------------------------------------------