DEV Community

Cover image for Clock
Matt 🦕
Matt 🦕

Posted on

Clock

TL;DR Like clocks? I built a clock made of clocks, click below to play with it.


Clock Gif

Or read on for a more in depth look at building clocks out of clocks (with some React).

Inspiration from strange places

Have you something like this before?

Me too, it's cool huh! I really enjoy the way the time seems to gradually appear and take shape.

Actually, the more I looked at it the more I appreciated the way it was put together, they are not individual clocks in the traditional sense as one arm is not simply following the other. Both arms of the clock are moving freely allowing for the various interesting shapes and movements and, of course, the time.

I bet that wouldn't be too difficult to put together, a RaspberryPi to control the "clocks" with a mechanism to specify an angle for the first and second hands...

Big Think

Creativity intensifies

Ok, so I don't have the motors to build the physical clocks and I'm not sure what I need to buy or how to wire them into a RaspberryPi. However, what I can do to prototype the idea is build a web app.

The quickest way to get started is to use create-react-app, this provides the basic setup for a React app.

The first thing I wanted to do was to create a simple clock, requirements were simple; two hands that can move independently of each other and a face. Throwing together some div's, a bit of CSS and voila, I had the makings of a clock, I can specify the angle in degrees, of each hand using the a CSS transform rotate and there's a white face.

An image of the first clock with its arms at odd angles

Making things move

From here I wanted to see what the best way to animating things could be. I spent a bit of time looking into the various ways I could animate components in React. Then I thought, nah, why not just see what I can I achieve without a library, surely I should be able to prototype something just using React and a bit of CSS knowhow.

Using create-react-app meant that I get a modern version of React out of the box, which means hooks! I've been looking for an excuse to try out hooks and this seemed a good a time as any to try.

I imagined a basic rendering loop like this:

  1. inital state sets start / end positions
  2. render clock hands at initial position
  3. setTimeout or requestAnimationFrame to increment and set new position
  4. render clock hands at new position
  5. repeat

Keeping the hand positions in state meant I could "animate" the hands by updating the state, incrementally, and causing a re-render which would update the hands to their new position.

const [angle, setAngle] = useState({
   hour: getRandomStartingAngle(), 
   minute: getRandomStartingAngle()
});
Enter fullscreen mode Exit fullscreen mode

In the new world of React hooks there's a hook perfect for the job of triggering the increment: useEffect which, amongst other things, is run after every render (for more details check out the docs).

To increment I needed to create an effect with would trigger at a reasonable rate, for this I used requestAnimationFrame which is a suitable API to schedule an update on as it's usually called 60 times per second (generally considered the threshold for smooth animation).

useEffect(()=> {
   requestAnimationFrame(()=>{
      const { hour, minute } = angle;
      setAngle({
         hour: hour + 1, 
         minute: minute + 1
      });
   });
}, [angle]);
Enter fullscreen mode Exit fullscreen mode

Putting it all together and I had a clock that animated around and around, and around, and around, and never stop.

A gif of the first clock with its arms moving around

It seemed to work pretty well. However, I made a couple of mistakes which won't become apparent until I start trying to create numbers from the clocks.

Drawing numbers, with clocks

Next was to put a bunch of these little clocks onto the screen and see how they animate, using a small bit of flexbox to define rows / columns and create 2x3 grids for a single number.

2x3 grid of little clocks with their hands at random positions

So it's starting to look a lot more like it could resemble a number. In order to animate to the number I needed to work out all the various positions that could go into a clock number and then tell the smaller clocks to animate to those positions.

The approach to drawing a number was to pass a targetAngle to each of the smaller clocks. The basic idea was that for a given target angle the clock would continue increment the position of the hands until they'd reached it, then stop.

function getIncrementValue(angle, targetAngle) {
   if(angle === targetAngle){
      return angle;
   } else { 
      return resetAngle(angle + 1);
   }
}
Enter fullscreen mode Exit fullscreen mode

Incremental by 1 each time means the target angle would eventually be achieved, however the first mistake I made in the sub clock logic rears its head.

As the hands increment around they can reach an angle above 360deg which breaks for situations where the hand has to travel around the whole clock to reach the target angle. This would mean some of the sub clocks would stop at the right place but others would continue rotating, an awkward bug.

To solve the endless rotation bug I added a resetAngle function which keeps the numbers between 0 < 359 allowing the target angle to always be reached.

Next was the job of actually figuring out these angles. The approach initially was to write, by hand, each angle, for each number, for each clock in the 2x3 grid... I quickly grew tired of this. Instead it's easier to specify a a number of set positions which are the building blocks of the number.

const createAngleSet = (hour, minute) => ({hour, minute});
const bothLeft = createAngleSet(270, 90);
const bothRight = createAngleSet(90, 270);
const bothTop = createAngleSet(0, 180);
const bothBottom = createAngleSet(180, 0);
const topAndBottom = createAngleSet(0, 0);
const rightAndLeft = createAngleSet(90, 90);
const topLeftCorner = createAngleSet(90, 0);
const topRightCorner = createAngleSet(270, 0);
const bottomLeftCorner = createAngleSet(0, 270);
const bottomRightCorner = createAngleSet(0, 90);
const emptySpace = createAngleSet(225, 45);
Enter fullscreen mode Exit fullscreen mode

Above is the list of all the positions needed to "draw" the numbers 0-9 and they're used in a number config that looks something like this:

TWO: {
  a1: { ...bothRight },
  a2: { ...topLeftCorner },
  a3: { ...bottomLeftCorner },
  b1: { ...topRightCorner },
  b2: { ...bottomRightCorner },
  b3: { ...bothLeft }
}
Enter fullscreen mode Exit fullscreen mode

The result of all this work was the realisation of the numbers. The effect was captured almost exactly as I wanted it, with the number appearing out of the randomness of the single clock faces.

2x3 grid of little clocks with their hands at random positions moving to the positions of the number 2

The full NumberGrid is available for preview and illustrates the whole number set that is used in building the clock.

Animations, animations, animations

Earlier I mentioned I had made a mistake when creating the mini-clock, did you catch it?

Well, my first go with useEffect was more on feeling than careful study of the documentation. As a result when I first attempted to draw the numbers 0-9, at the same time, there was pretty terrible performance.

Turns out useEffect is expected to be triggered and then torn down, as a result it's supposed to provide a cleanup function to do any bits of cleanup that are needed if an in progress effect needs to be cancelled. This caused a subtle issue as everything seems to animate smoothly however it slowed way down as I scaled up from the 1 mini-clock to the 54 that I needed in order to display the full 0-9 numbers.

useEffect(()=> {
   const increment = requestAnimationFrame(()=> {
      const { hour, minute } = angle;
      const { hour: targetHour, minute: targetMinute } = targetAngle;
      setAngle({
         hour: getIncrementValue(hour, targetHour, speed), 
         minute: getIncrementValue(minute, targetMinute, speed)
      });
  }
  return () => cancelAnimationFrame(increment);
}, [angle, targetAngle, speed]);
Enter fullscreen mode Exit fullscreen mode

I corrected this within my effect with cancelAnimationFrame and added a speed value, via useContext to give me some control over the mini-clock animations (necessary for building a stopwatch).

Clock

I now have all the pieces to build the clock. Using the Date object and updating the target time every time the hours or seconds changed. The DigitalClock would then work out the individual parts of the time string and pass them to the ClockNumbers which would, in turn, pass the individual parts to each mini clock.

I am so happy with the result 🕑🕤🕜😬

Thanks for reading and check out the clock below 👇

Clock Gif

First appeared on my blog, published 3rd November 2019

Top comments (11)

Collapse
 
jessicagarson profile image
Jessica Garson • Edited

OMG, these clocks are so amazing. We should make this into a Twitter bot!

Collapse
 
mattjbones profile image
Matt 🦕

We could do some really cool things with displaying likes or follower counts?

Orrrr we could make a Twitter bot that Tweets out the time using the Twemoji clocks! 😁😬

Collapse
 
lepinse profile image
lepinse

Great work!
I think the original project is The ClockClock by Human since 1982

Collapse
 
mattjbones profile image
Matt 🦕

Thanks! Yeah I did a bit of research later and found clockclock.com, it’s very cool. I hope they don’t mind my little project, though imitation being a form of flattery 😬

Collapse
 
dmdesai_ profile image
Div • Edited
Collapse
 
mattjbones profile image
Matt 🦕

Oh wow, this is cool. Thanks for sharing!

Collapse
 
blackmamba profile image
The Black Mamba🔥

This is amazing !!🔥

Collapse
 
vishalraj232 profile image
vishal kumar

nice.

Collapse
 
andrescampuzano profile image
Andres Campuzano Garzon

Amazing project

Collapse
 
sushantrahate profile image
Sushant Rahate

This is really cool!

Collapse
 
bsara profile image
Brandon Sarà

This is just straight up awesome! I love it! Mad props to you, man.