create account

A Hive Clock and Some Ripples with CSS and JavaScript by anomadsoul

View this thread on: hive.blogpeakd.comecency.com
· @anomadsoul ·
$55.88
A Hive Clock and Some Ripples with CSS and JavaScript
Grinding, grinding, grinding. When we are talking code, it all comes down to grinding. The more you grind, practice your skills, find out about new tweaks, practice what you should already know by heart, and just putting your code to work... the more you will learn.

So that's what I am doing. I am already pretty efficient with my HTML and CSS code, I still need a lot of practice and a hell lot to learn regarding JavaScript - and don't worry, I've been taking my lessons religiously even though I haven't posted about JavaScript for something like two weeks; but the point is, these little projects are mainly about HTML and CSS and grinding my skills.

This is fifth post already about little projects based off online ideas and my plan is to join all of these ideas once I am ready and create my own CV website of my own where I'll showcase all that I've learned in a sort of interactive website. Heads up, I'm getting there, slowly but steady, more slowly than steady to be honest but what the hell, slow and steady wins the race.

In the meantime, I will leave you with two project Ideas based off on Brad Traversy's paid course on Udemy. Credit at the bottom and links btw.

<sub>Note: The code is based off someone else's ideas, all the comments and doc are mine as this is supposed to be a teaching kind of post, and even though the main concept is not mine, all the code has been tweaked and modified to align with my own ideas.</sub>

## <center>Theme Clock</center>

<center>![image.png](https://images.ecency.com/DQmPQcZJPj8SmZ3tqTEJ2N3n44ixgGrsiRzWijrxku69EXX/image.png)</center>
<center><sub> This is the first project, but I'm putting the image here to show it as thumbnail</sub></center>



This is a cool looking project that anybody can give it a use case. I'll use CSS to style the hands of the clock, and then a little bit of JavaScript to power it and have it show the current time and that it updates every second so that it ticks, there will a digital readout of the date and time, and it will be able to toggle from dark and light themes.

### HTML Code

````
  </head>
  <body>
    <button class="toggle">Dark mode</button>
    <div class="clock-container">
      <div class="clock">
        <img src="logo.png" />
        <div class="needle hour"></div>
        <div class="needle minute"></div>
        <div class="needle second"></div>
        <div class="center-point"></div>
      </div>
````

The following classes' values will come from the JS, but for now I'll hardcode the time here for showing purposes.

````
      <div class="time"></div>
      <div class="date"></div>
    </div>

    <script src="script.js"></script>
  </body>
</html>
````
As always, the HTML file doesn't render much, but you can at least see all the elements we need to make this little project:

<center>![image.png](https://images.ecency.com/DQmNszTiFrjcY3h4te7QWMGnbdXBPsiq17uwXAxAMXvxDXn/image.png)</center>

### CSS Code

````
@import url("https://fonts.googleapis.com/css?family=Heebo:300&display=swap");

* {
  box-sizing: border-box;
}
````
These two will be used in the light and dark modes:
````
:root {
  --primary-color: #000;
  --secondary-color: #fff;
}
````

Whenever we click the button to toggle between dark and light mode, everything will change colors in a glimpse, but to add more aesthetic we can add a transition to that.

````
html {
  transition: all 0.5s ease-in;
}

html.dark {
  /* The primary color are the hands of the clock, the text and such */
  --primary-color: #fff;
  --secondary-color: #333;
}

html.dark {
  background-color: #111;
  color: var(--primary-color);
}
````

We used custom properties, so that we can modify them just once and use them at any point. Also to simplify the CSS by having *default* colors.

````
body {
  font-family: "Heebo", sans-serif;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  overflow: hidden;
  margin: 0;
}

.toggle {
  background-color: var(--primary-color);
  color: var(--secondary-color);
  border: 0;
  border-radius: 4px;
  padding: 8px 12px;
  position: absolute;
  top: 100px;
  cursor: pointer;
}

.toggle:focus {
  outline: none;
}

.clock-container {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
}

.clock {
  background-position: fit;
  /* We position the clock relative so we can position things inside as absolute*/
  position: relative;
  width: 200px;
  height: 200px;
      // I'll get rid of the background later, but for showing purposes
      // I'm giving it a background, so I can see what I am doing.
  border-radius: 25%;
}

img {
  opacity: 0.25;
  height: 100%;
  width: 100%;
}
````

With the next class, we target all the three hands (needles) of the clock.

````
.needle {
  background-color: var(--primary-color);
  position: absolute;
  top: 50%;
  left: 50%;
````

Each hand or needle will have a different height, but we use this one for all of them to base off of that height.

````
  height: 60px;
  width: 3px;
````

If you think of what I have now, I can now make each hand rotate to point to a certain time, but they will rotate from the middle of the hand/needle, so in order to make them rotate from the bottom center I have to:
````
  transform-origin: bottom center;
  transition: all 1s ease-in;
}
````

I want the bottom of each needle to be positioned right at the center of the clock, where the three hands will meet (but not intersect).
````
.needle.hour {
  transform: translate(-50%, -100%) rotate(0deg);
       // The transform property will begin at these values.
  width: 4px;
}

.needle.minute {
  transform: translate(-50%, -100%) rotate(0deg);
  height: 100px;
}

.needle.second {
  transform: translate(-50%, -100%) rotate(0deg);
  height: 100px;
  width: 2px;
  background-color: #e74c3c;
}
````
I'm not even finished with the CSS code, but you can already see where I'm heading, pretty cool huh? But not yet finished, stay with me.

<center>![image.png](https://images.ecency.com/DQmU6ZPXaRfT5FeaXjZywNoskbZNkcdhGaqGb3WnnVvkML4/image.png)</center>

````
.center-point {
  background-color: #e74c3c;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
````

But this will not position the dot in the exact center, but the top and left side at the center, we have to transform the button's position.

````
  transform: translate(-50%, -50%);
}

.center-point::after {
  content: "";
  background-color: var(--primary-color);
  width: 5px;
  height: 5px;
  border-radius: 50%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.time {
  font-size: 60px;
}

.date {
  color: #aaa;
  font-size: 14px;
  letter-spacing: 0.3px;
  text-transform: uppercase;
}

.date .circle {
  background-color: var(--primary-color);
  color: var(--secondary-color);
  border-radius: 50%;
  height: 20px;
  width: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 20px;
  transition: 0.5s ease-in;
}
````

This is the image I used for Thumbnail, as you can see the clock is ready, we just need to make it tick every second to show the exact time, at every moment.

<center> ![image.png](https://images.ecency.com/DQmVbgtfWuWLjKk134vTQC6vJNx4xF7EmnQsQik8Yn47vzx/image.png)</center>

### JavaScript Code

First we bring in all the classes that we want to play with and store them in variables:

````
const hourEl = document.querySelector(".hour");
const minuteEl = document.querySelector(".minute");
const secondEl = document.querySelector(".second");
const timeEl = document.querySelector(".time");
const dateEl = document.querySelector(".date");
const toggle = document.querySelector(".toggle");
````

We have to create two arrays, one for days and the other one for months, so we can then pull them to display them correctly and they change according to the actual date:

````
const days = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
];

const months = [
  "Jan",
  "Feb",
  "Mar",
  "Apr",
  "May",
  "Jun",
  "Jul",
  "Aug",
  "Sep",
  "Oct",
  "Nov",
  "Dec",
];
````

To handle the dark mode I need to add or remove the class of *dark* to the HTML element, and change the text of the button.

````
toggle.addEventListener("click", (e) => {
  const html = document.querySelector("html");
  if (html.classList.contains("dark")) {
    html.classList.remove("dark");
    e.target.innerHTML = "Dark mode";
````

*e.target* means that the element we click on (the toggle button), we will set it to dark mode (Because we are in light mode since we removed the class of dark right above).

````
  } else {
    html.classList.add("dark");
    e.target.innerHTML = "Light mode";
  }
});

function setTime() {
  const time = new Date();
  const month = time.getMonth();
  const day = time.getDay();
  const date = time.getDate();
  const hours = time.getHours();
  const hoursTwelve = hours >= 13 ? hours % 12 : hours;
  const minutes = time.getMinutes();
  const seconds = time.getSeconds();
  const amOrPm = hours >= 12 ? "PM" : "AM";
````

All of these *get* above are methods within JS.

````
  hourEl.style.transform = `translate(-50%, -100%) rotate(${scale(
    hoursTwelve,  0,  11,  0, 360 )}deg)`;
````

To make the hands move dynamically depending on the time, we use *scale* function, which basically maps a range of numbers (1 to 24 hours) to another range of numbers (1 to 360 degrees).

````
  minuteEl.style.transform = `translate(-50%, -100%) rotate(${scale(
    minutes,
    0,
    59,
    0,
    360
  )}deg)`;

  secondEl.style.transform = `translate(-50%, -100%) rotate(${scale(
    seconds,
    0,
    59,
    0,
    360
  )}deg)`;

  timeEl.innerHTML = `${hoursTwelve}:${
    minutes < 10 ? `0${minutes}` : minutes
  } ${amOrPm}`;

  dateEl.innerHTML = `${days[day]}, ${months[month]} <span class="circle">${date}</span>`;
}

const scale = (num, in_min, in_max, out_min, out_max) => {
  return ((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min;
};

setTime();

setInterval(setTime, 1000);
````

And voila! The date, time and clock work perfectly and they update every second thanks to the setInterval function, now we have a Hive personalized clock.

<center>![gif1.gif](https://images.ecency.com/DQmVAYGatkmcW6fMQ1rAb39ivxjT4fpHA1roQkUwmugQPSc/gif1.gif)</center>



## <center>Ripple effect on buttons</center>

I will have buttons, give them a specific class and give them an effect exactly wherever the user clicks on the button.

Basically I'll have a button and when the user clicks on it, the JavaScript create a span with the class of *circle* and I'll style it so it has an animation that scales out. I have to set the position of *top* and *left* exactly where the user clicks.

### HTML Code

````
  </head>
  <body>
    <button>
      Click anywhere on me!

// The class and style of the following will be given by
// the JS, I'm putting it here for showing purposes.

      <span class="circle" style="
         top: 27px;
         left: 382px">
      </span>
    </button>

    <script src="script.js"></script>
  </body>
</html>
````

As you can see, there's nothing to be shown yet, this is a simple HTML file:

 <center>![image.png](https://images.ecency.com/DQmY81BAvQKAmFE3QL43vRmU28vYBpVkFH9uAXerrNC1hfJ/image.png)</center>

### CSS Code

````
@import url("https://fonts.googleapis.com/css?family=Heebo:300&display=swap");

* {
  box-sizing: border-box;
}

body {
  background-image: linear-gradient(90deg, #f51c1c, #ca6c65);
  font-family: "Heebo", sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  overflow: hidden;
  margin: 0;
}
````
The button is positioned relative so the circle (of the ripple effect) is positioned absolute.
````
button {
  position: relative;
  background-color: black;
  color: #fff;
  border: 1px rgb(83, 32, 32) solid;
  height: 30%;
  width: 50%;
  font-size: 30px;
  text-transform: uppercase;
  letter-spacing: 2px;
  padding: 20px 30px;
  overflow: hidden;
  margin: 10px 0;
}

button:focus {
  outline: none;
}

````
This position will change with the JS code. The position will depend on where the user clicks the button.
````
button .circle {
  position: absolute;
  background-color: #fff;
  width: 100px;
  height: 100px;
  border-radius: 50%;
      // I want the circle to have an animation where it
      // scales up and disappears: I'll use *scale*
  transform: translate(-50%, -50%) scale(0);
  animation: scale 3.5s ease-out;
}
````
Now I need to create the animation.
````
@keyframes scale {
  to {
    transform: translate(-50%, -50%) scale(3);
    opacity: 0;
  }
}
````
Right now the webpage doesn't have any interaction with the user, but every time I reload the page, the ripple effect is shown starting on the coordinate I specified (for showing purposes) on the HTML file, but you can already see how it works!:

<center>![gif1.gif](https://images.ecency.com/DQmTnn52FuVd5vzS3kWPfbETXVWgBipJ73Z2VRugFWWRrkV/gif1.gif)</center>

### JavaScript Code

````
const buttons = document.querySelectorAll("button");

buttons.forEach((button) => {
  button.addEventListener("click", function (e) {
````
I want to get two different values, the first is where we click on the viewport (and it will only be triggered if the click was inside the button).
````
    const x = e.clientX;
    const y = e.clientY;
````
These two variables will return the coordinates where the click was made (in the entire viewport).

These coordinates need to be inside the button for the ripple effect to work, so we need to tell JS where the button is located.
````
    const buttonTop = e.target.offsetTop;
         // *e* is always the element that the
         // event fires off of (the button click)
    const buttonLeft = e.target.offsetLeft;
````
Now when the user clicks inside the button, it will give them always the same coordinates: the position of the button.

So now, I want to calculate where in the button we are clicking, and get the coordinates inside the button.
````
    const xInside = x - buttonLeft;
    const yInside = y - buttonTop;
````
*X* and *Y* above represent the coordinate where we click in the entire viewport.

Now I'm going to create the span from the HTML
````
    const circle = document.createElement("span");
    circle.classList.add("circle");
````
Now for the positions of said circle, I want to get them from the values of *xinside* and *yInside*
````
    circle.style.top = yInside + "px";
    circle.style.left = xInside + "px";

    this.appendChild(circle);
````

But I also have to remove every circle this code creates, otherwise it stays in the DOM (cause I'm using Vanilla JS, this would be easier with React, but I don't know React... yet)

````
    setTimeout(() => circle.remove(), 5000);
  });
});
````

And the ripple effect is created, and it will be triggered from where the user clicks on the button, as long as they click inside the button: 

<center>![gif2.gif](https://images.ecency.com/DQme2Use7TkjuMgWXEJ5etDGZVk2C5tTjttEdEhF22MsvQt/gif2.gif)</center>


***
***
***
<sub>These projects are based on a CSS, HTML and JS paid course I got on Udemy by [Brad Traversy](https://www.udemy.com/course/50-projects-50-days/learn/lecture/23595546#content). I made my own changes and tweaks but the template so to speak, is his idea and I do not claim them to be my own. If you are looking to practice CSS and JavaScript, his courses are the way to go, check them out on Udemy.</sub>
👍  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and 52 others
properties (23)
authoranomadsoul
permlinka-hive-clock-and-some
categoryhive-169321
json_metadata"{"links": ["https://fonts.googleapis.com/css?family=Heebo:300&display=swap", "https://fonts.googleapis.com/css?family=Heebo:300&display=swap", "https://www.udemy.com/course/50-projects-50-days/learn/lecture/23595546#content"], "image": ["https://images.ecency.com/DQmPQcZJPj8SmZ3tqTEJ2N3n44ixgGrsiRzWijrxku69EXX/image.png", "https://images.ecency.com/DQmNszTiFrjcY3h4te7QWMGnbdXBPsiq17uwXAxAMXvxDXn/image.png", "https://images.ecency.com/DQmU6ZPXaRfT5FeaXjZywNoskbZNkcdhGaqGb3WnnVvkML4/image.png", "https://images.ecency.com/DQmVbgtfWuWLjKk134vTQC6vJNx4xF7EmnQsQik8Yn47vzx/image.png", "https://images.ecency.com/DQmVAYGatkmcW6fMQ1rAb39ivxjT4fpHA1roQkUwmugQPSc/gif1.gif", "https://images.ecency.com/DQmY81BAvQKAmFE3QL43vRmU28vYBpVkFH9uAXerrNC1hfJ/image.png", "https://images.ecency.com/DQmTnn52FuVd5vzS3kWPfbETXVWgBipJ73Z2VRugFWWRrkV/gif1.gif", "https://images.ecency.com/DQme2Use7TkjuMgWXEJ5etDGZVk2C5tTjttEdEhF22MsvQt/gif2.gif"], "thumbnails": ["https://images.ecency.com/DQmPQcZJPj8SmZ3tqTEJ2N3n44ixgGrsiRzWijrxku69EXX/image.png", "https://images.ecency.com/DQmNszTiFrjcY3h4te7QWMGnbdXBPsiq17uwXAxAMXvxDXn/image.png", "https://images.ecency.com/DQmU6ZPXaRfT5FeaXjZywNoskbZNkcdhGaqGb3WnnVvkML4/image.png", "https://images.ecency.com/DQmVbgtfWuWLjKk134vTQC6vJNx4xF7EmnQsQik8Yn47vzx/image.png", "https://images.ecency.com/DQmVAYGatkmcW6fMQ1rAb39ivxjT4fpHA1roQkUwmugQPSc/gif1.gif", "https://images.ecency.com/DQmY81BAvQKAmFE3QL43vRmU28vYBpVkFH9uAXerrNC1hfJ/image.png", "https://images.ecency.com/DQmTnn52FuVd5vzS3kWPfbETXVWgBipJ73Z2VRugFWWRrkV/gif1.gif", "https://images.ecency.com/DQme2Use7TkjuMgWXEJ5etDGZVk2C5tTjttEdEhF22MsvQt/gif2.gif"], "users": ["import", "import", "keyframes"], "tags": ["hive-169321", "javascript", "css", "html", "coding", "developing", "projects"], "app": "ecency/3.0.20-vision", "format": "markdown+html"}"
created2021-12-21 10:51:24
last_update2021-12-21 10:51:24
depth0
children3
last_payout2021-12-28 10:51:24
cashout_time1969-12-31 23:59:59
total_payout_value27.994 HBD
curator_payout_value27.890 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length15,578
author_reputation1,681,171,138,068,684
root_title"A Hive Clock and Some Ripples with CSS and JavaScript"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id108,735,792
net_rshares24,079,301,129,430
author_curate_reward""
vote details (116)
@felixxx ·
$0.11
At least it should show UTC time, which is what Hive uses :P
👍  
properties (23)
authorfelixxx
permlinkre-anomadsoul-r4gx7e
categoryhive-169321
json_metadata{"tags":["hive-169321"],"app":"peakd/2021.09.1"}
created2021-12-21 13:52:27
last_update2021-12-21 13:52:27
depth1
children2
last_payout2021-12-28 13:52:27
cashout_time1969-12-31 23:59:59
total_payout_value0.053 HBD
curator_payout_value0.053 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length60
author_reputation214,424,157,741,799
root_title"A Hive Clock and Some Ripples with CSS and JavaScript"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id108,739,451
net_rshares46,850,448,438
author_curate_reward""
vote details (1)
@anomadsoul ·
Nah, it's my clock, Mexi time all the time hahaha
properties (22)
authoranomadsoul
permlinkre-felixxx-20211224t112831665z
categoryhive-169321
json_metadata{"tags":["hive-169321"],"app":"ecency/3.0.20-vision","format":"markdown+html"}
created2021-12-24 17:28:33
last_update2021-12-24 17:28:33
depth2
children1
last_payout2021-12-31 17:28:33
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length49
author_reputation1,681,171,138,068,684
root_title"A Hive Clock and Some Ripples with CSS and JavaScript"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id108,830,287
net_rshares0
@felixxx ·
That means: 30 mins late, always? XD
properties (22)
authorfelixxx
permlinkre-anomadsoul-r4n3ti
categoryhive-169321
json_metadata{"tags":["hive-169321"],"app":"peakd/2021.09.1"}
created2021-12-24 22:01:00
last_update2021-12-24 22:01:00
depth3
children0
last_payout2021-12-31 22:01:00
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length36
author_reputation214,424,157,741,799
root_title"A Hive Clock and Some Ripples with CSS and JavaScript"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id108,835,275
net_rshares0