create clock face with CSS

Discussion in 'CSS' started by iago111, Nov 17, 2020.

  1. #1
    Hallo,

    I would like to create a clock face with CSS.
    Right now I have a png for that. I would like to rebuild that with CSS. Does anybody have a hint how to start with that? (like with psuedo elements, position: absolute etc.) Here is the picture:

    [​IMG]

    Here is the CSS code that I have up to now.

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background: #091921;
    }
    
    .clock {
      width: 350px;
      height: 350px;
      display: flex;
      justify-content: center;
      align-items: center;
      background: url(clock.png);
      background-size: cover;
      border: 4px solid #091921;
      border-radius: 50%;
    
      /* box-shadow */
      /* offset-x | offset-y | blur-radius | color */
    
      box-shadow: 0 -15px 15px rgba(255, 255, 255, 0.05),
        inset 0 -15px 15px rgba(255, 255, 255, 0.05), 0 15px 15px rgba(0, 0, 0, 0.3),
        inset 0 15px 15px rgba(0, 0, 0, 0.3);
    
    /* CSS for the hands */
    
    .clock:before {
    content: "";
    position: absolute;
    width: 15px;
    height: 15px;
    background: #fff;
    border-radius: 50%;
    z-index: 10000;
    }
    
    .clock .hour,
    .clock .min,
    .clock .sec {
    position: absolute;
    }
    
    .clock .hour,
    .hr {
    width: 160px;
    height: 160px;
    /* border: 3px solid #ff105e; */
    }
    
    .clock .min,
    .mn {
    width: 190px;
    height: 190px;
    /* border: 3px solid #eee2e6; */
    }
    
    .clock .sec,
    .sc {
    width: 230px;
    height: 230px;
    /* border: 3px solid #d7f336; */
    }
    
    .hr,
    .mn,
    .sc {
    display: flex;
    justify-content: center;
    position: absolute;
    /* align-items: center; */
    /* border: 3px solid #ff105e; */
    border-radius: 50%;
    }
    
    .hr:before {
    content: "";
    position: absolute;
    width: 8px;
    height: 80px;
    background: #ff105e;
    z-index: 10;
    border-radius: 6px 6px 0 0;
    }
    
    .mn:before {
    content: "";
    position: absolute;
    width: 4px;
    height: 90px;
    background: #fff;
    z-index: 11;
    border-radius: 6px 6px 0 0;
    }
    
    .sc:before {
    content: "";
    position: absolute;
    width: 2px;
    height: 150px;
    background: #fff;
    z-index: 12;
    border-radius: 6px 6px 0 0;
    }
    
    }
    Code (CSS):
    HTML:

     <body>
        <div class="clock">
          <div class="hour">
            <div class="hr" id="hr"></div>
          </div>
          <div class="min">
            <div class="mn" id="mn"></div>
          </div>
          <div class="sec">
            <div class="sc" id="sc"></div>
          </div>
        </div>
    </body>
    HTML:
    Thanks a lot!
     
    Solved! View solution.
    iago111, Nov 17, 2020 IP
  2. qwikad.com

    qwikad.com Illustrious Member Affiliate Manager

    Messages:
    7,263
    Likes Received:
    1,693
    Best Answers:
    31
    Trophy Points:
    475
    #2
    With javascript you can have a clock that shows the current time (on refresh):

    https://jsfiddle.net/1n8fxhvo/

    You can remove some elements if you just want the face.
     
    qwikad.com, Nov 17, 2020 IP
  3. iago111

    iago111 Member

    Messages:
    99
    Likes Received:
    4
    Best Answers:
    1
    Trophy Points:
    33
    #3
    Thanks a lor for this! I already have a JS solution that I can use, but I can compare the solutions. Does anybody have an idea how to do this with CSS? (I would like to do as much as possible with CSS)
     
    Last edited: Nov 19, 2020
    iago111, Nov 19, 2020 IP
  4. iago111

    iago111 Member

    Messages:
    99
    Likes Received:
    4
    Best Answers:
    1
    Trophy Points:
    33
    #4
    I stumbled over border: dashed in order to create these dashes in the circle but I think this is not very useful. Does anybody have experience with SVG? (stoke property in this case)?
     
    iago111, Nov 20, 2020 IP
  5. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,999
    Best Answers:
    253
    Trophy Points:
    515
    #5
    Markup's a little heavy for my tastes, but this should get the job done.

    
    <div class="clockFace">
    	<div></div>
    	<span></span>
    	<span></span>
    <!-- .clockFace --></div>
    
    Code (markup):
    
    .clockFace {
    	position:relative;
    	width:10em;
    	height:10em;
    }
    
    .clockFace:before,
    .clockFace:after,
    .clockFace div:before,
    .clockFace div:after {
    	position:absolute;
    	width:1.5em;
    	line-height:1.5em;
    	text-align:center;
    }
    
    .clockFace:before,
    .clockFace:after {
    	left:50%;
    	margin-left:-0.75em;
    }
    
    .clockFace:before {
    	content:"12";
    	top:0;
    }
    
    .clockFace:after {
    	content:"6";
    	bottom:0;
    }
    
    .clockFace div:before,
    .clockFace div:after {
    	top:50%;
    	margin-top:-0.75em;
    }
    
    .clockFace div:before {
    	content:"9";
    	left:0;
    }
    
    .clockFace div:after {
    	content:"3";
    	right:0;
    }
    
    .clockFace span:after,
    .clockFace span:before {
    	content:"";
    	box-sizing:border-box;
    	position:absolute;
    	left:50%;
    	width:0.125em;
    	height:100%;
    	margin-left:-0.06125em;
    	border:solid #000;
    	border-width:1em 0;
    	transform:rotate(30deg);
    }
    
    .clockFace span:after {
    	transform:rotate(60deg);
    }
    
    .clockFace span + span:before {
    	transform:rotate(-30deg);
    }
    
    .clockFace span + span:after {
    	transform:rotate(-60deg);
    }
    
    Code (markup):
    That about what you want? Note, done in dynamic sizes since px is a giant middle finger to usability and accessibility.
     
    deathshadow, Nov 23, 2020 IP
  6. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,999
    Best Answers:
    253
    Trophy Points:
    515
    #6
    Live demo here with working clock:

    https://cutcodedown.com/for_others/iago111/clock/

    You might find the clock code a bit simpler than most. Note that the hours are grabbed as 120ths of a day so they "tick" the 6 degrees the other elements do.
     
    deathshadow, Nov 23, 2020 IP
    malky66, kk5st and sarahk like this.
  7. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,999
    Best Answers:
    253
    Trophy Points:
    515
    #7
    Update, I fixed a bug in the math, and made the 12th ticks smaller to match the original image more closely... so be sure to ctrl-f5 to get the latest version.

    Oh, and if you want to control the size, just change the font-size on .clockFace, it's fully scalable off that one value.

    Side note, I made it behave like a proper old-fashioned clock, where EVERY hand "ticks" to 60ths, not just the second hand. It's funny how "proper" analog clocks are actually digital in function.
     
    Last edited: Nov 24, 2020
    deathshadow, Nov 24, 2020 IP
  8. qwikad.com

    qwikad.com Illustrious Member Affiliate Manager

    Messages:
    7,263
    Likes Received:
    1,693
    Best Answers:
    31
    Trophy Points:
    475
    #8
    You need to get it out there. I've not seen ANY html/css based clocks till now.
     
    qwikad.com, Nov 24, 2020 IP
  9. iago111

    iago111 Member

    Messages:
    99
    Likes Received:
    4
    Best Answers:
    1
    Trophy Points:
    33
    #9
    #deathshadow, thanks a lot for that solution, it works quite well!

    My ideas was to build up some kind of reusable blueprints/templates/components.

    E.g. I have a clock face with numbers and strokes, so I can build a simple clock or a watch,
    then I can built a chronograph, a compass (adding 360 strokes e.g.), finally I can build a artificial horizon tool for a plane e.g.

    Therefore these components have to be simple and flexible at the same time.
    I recede from JS since I really do not want to use it for simple graphic elements. On the other hand I do not want to use
    pictures since I cannot modify them.
     
    iago111, Nov 24, 2020 IP
  10. #10
    Good idea. Made a medium article out of it.

    https://deathshadow.medium.com/html-css-analog-clock-with-minimal-javascript-9dcf9a16e079

    A more fleshed out version, with more text explaining the how/what/why of it. I switched up how the ticks and hands are drawn slightly, and by mistake found that border-radius does some 'funky' but attractive things to the hands.
     
    deathshadow, Nov 24, 2020 IP
  11. iago111

    iago111 Member

    Messages:
    99
    Likes Received:
    4
    Best Answers:
    1
    Trophy Points:
    33
    #11
    iago111, Nov 25, 2020 IP
  12. qwikad.com

    qwikad.com Illustrious Member Affiliate Manager

    Messages:
    7,263
    Likes Received:
    1,693
    Best Answers:
    31
    Trophy Points:
    475
    #12
    Unreal! What's truly unreal is how little amount of code it's taken.
     
    qwikad.com, Nov 25, 2020 IP
  13. deathshadow

    deathshadow Acclaimed Member

    Messages:
    9,732
    Likes Received:
    1,999
    Best Answers:
    253
    Trophy Points:
    515
    #13
    It's actually more code than the canvas version you linked to, it's just the code is in different spots, the CSS being the lion's share of it.

    With a little optimization that canvas version could be dropped down to just 1.82k of scripting, almost 1k less than the CSS approach if you add the CSS and JS together.

    
    (function(d) {
    
    	var
    		canvas = d.getElementById("canvas"),
    		ctx = canvas.getContext("2d"),
    		radius = canvas.height / 2;
    
    	ctx.translate(radius, radius);
    	radius *= 0.90;
    	drawClock();
    
    	function drawClock() {
    		drawFace();
    		drawNumbers();
    		drawTime();
    	}
    
    	function drawFace() {
    		var grad = ctx.createRadialGradient(0,0,radius*0.95, 0,0,radius*1.05);
    		grad.addColorStop(0, '#333');
    		grad.addColorStop(0.5, 'white');
    		grad.addColorStop(1, '#333');
    		ctx.beginPath();
    		ctx.arc(0, 0, radius, 0, 2 * Math.PI);
    		ctx.fillStyle = 'white';
    		ctx.fill();
    		ctx.strokeStyle = grad;
    		ctx.lineWidth = radius * 0.1;
    		ctx.stroke();
    		ctx.beginPath();
    		ctx.arc(0, 0, radius * 0.1, 0, 2 * Math.PI);
    		ctx.fillStyle = '#333';
    		ctx.fill();
    	}
    
    	function drawNumbers() {
    		ctx.font = radius * 0.15 + "px arial";
    		ctx.textBaseline = "middle";
    		ctx.textAlign ="center";
    		var
    			pos = radius * 0.85,
    			inc = Math.PI / 6,
    			ang = inc * 7;
    		for (var num = 1; num < 13; num++) {
    			ctx.save();
    			ctx.rotate(ang);
    			ctx.translate(0, pos);
    			ctx.rotate(-ang);
    			ctx.fillText(num.toString(), 0, 0);
    			ctx.restore();
    			ang += inc;
    		}
    	} // drawNumbers
    
    	function drawTime(){
    		var
    			sxd2Rad = Math.PI / 30,
    			now = new Date();
    		drawHand(
    			(now.getHours() * 5 + Math.floor(now.getMinutes() / 12)) * sxd2Rad,
    			0.5,
    			0.07
    		);
    		drawHand(
    			now.getMinutes() * sxd2Rad,
    			0.8,
    			0.07
    		);
    		drawHand(
    			now.getSeconds() * sxd2Rad,
    			0.9,
    			0.02
    		);
    	} // drawTime
    
    	function drawHand(pos, length, width) {
    		ctx.save();
    		ctx.beginPath();
    		ctx.lineWidth = width * radius;
    		ctx.lineCap = "round";
    		ctx.moveTo(0,0);
    		ctx.rotate(pos);
    		ctx.lineTo(0, -length * radius);
    		ctx.stroke();
    		ctx.restore();
    	} // drawHand
    
    	setInterval(drawClock, 1000);
    
    })(document);
    Code (markup):
    And that's with leveling the playing field by having it update on the second.

    Forked pen here:
    https://jsfiddle.net/Jason_Knight/1h0us4o7/26/

    The biggest optimizations there being moving calculations out of a loop, removal of unnecessary arguments creating stack overhead, and understanding radians killing off a few "variables for nothing".

    More than one way to fry a fish. The big issue is that if you want it dynamic size you need to hook onresize to recalc the size, or if you want EM that's a bit tricky to implement... and really we should ALL be working in EM for most everything since it's the only "true" accessible screen media measurement.
     
    Last edited: Nov 25, 2020
    deathshadow, Nov 25, 2020 IP
  14. iago111

    iago111 Member

    Messages:
    99
    Likes Received:
    4
    Best Answers:
    1
    Trophy Points:
    33
    #14
    O.k. guys, I implemented #deathshadow 's CSS tutorial:
    https://levelup.gitconnected.com/html-css-analog-clock-with-minimal-javascript-9dcf9a16e079

    My original idea behind all that was to create a stopwatch. Things getting more complicated now, when you have clockfaces withing the clockface.

    I started with the CSS, without JS- functionality: Here is a picture:
    chronograph.JPG
    https://drive.google.com/file/d/1_51gfHjMLH8KjUsv_AxBkgl3tY5SpGO-/view?usp=sharing

    The 3 small clockfaces represent the minutes (at 12), seconds (at 9) and 10-nth seconds (at 6). I used #deathshadow 's technique of drawing the tick-marks
    by drawing lines within the circle where the border of these lines are the tick-marks. Rotating them draws all the tick marks.
    That works perfectly fine for the main clock, however I get problems positioning these lines in the small clocks. I made it by try-and-error, what is always bad!

    There are also 2 further problems. For the seconds I need 15 containers. (15 * 2 :)before/:after) * 2 (rotation)) That should be refactored somehow. And concerning the 10nth seconds, I do not have tick marks on opposite sites: The numerical series is: [36,72,108,144,180,216,252,288,324,360].

    HTML:
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Deathshadow watch</title>
    
        <link rel="stylesheet" type="text/css" href="style.css" />
        <link rel="stylesheet" type="text/css" href="chrono.css" />
      </head>
      <body>
        <div id="clock-wrapper">
          <div id="clock">
            <span></span>
            <span></span>
            <span></span>
            <!-- <i></i>
            <i></i>
            <i></i> -->
          </div>
          <div id="chrono-min">
            <span></span>
            <span></span>
            <span></span>
          </div>
    
          <div id="chrono-tenth-sec"></div>
          <div id="chrono-sec">
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
          </div>
        </div>
    
    HTML:
    style.css:
    /
    * CLOCK WRAPPER */
    
    #clock-wrapper {
      font-size: 4em;
      position: relative;
      width: 10em;
      height: 10em;
      line-height: 1.5em;
      margin: 1em auto;
      padding: 0;
    }
    
    /* END OF CLOCK WRAPPER */
    
    #clock {
      position: relative;
      width: 10em;
      height: 10em;
      border: 0.0625em solid #444;
      border-radius: 50%;
      box-shadow: inset 0.125em 0.25em 0.5em #0005, -0.25em -0.5em 0.5em #fffc,
        0 0 0.0625em 0.5em #777, 0 0 0.0625em 0.5625em #000,
        0.125em 0.25em 0.5em 0.5em #0004;
    }
    
    #clock *,
    #clock *:before,
    #clock *:after {
      box-sizing: border-box;
    
    #clock span {
      position: absolute;
      top: 0;
      left: 4.9375em; /* 10/2 - width/2 */
      width: 0.125em;
      height: 100%;
    }
    
    #clock i,
    #clock span:before,
    #clock span:after {
      position: absolute;
      top: 0; /*  (10-height) / 2 */
      left: 0;
      width: 100%;
      height: 10em;
    }
    
    #clock span:nth-child(2) {
      transform: rotate(60deg);
    }
    
    #clock span:nth-child(3) {
      transform: rotate(120deg);
    }
    
    #clock span:before,
    #clock span:after {
      content: "";
      border-style: solid;
      border-color: #000;
      border-width: 0.5em 0;
      /* background-color: #0f0; */
    }
    
    #clock span:before {
      transform: rotate(0deg);
    }
    
    #clock span:after {
      transform: rotate(30deg);
    }
    
    
    
    Code (CSS):
    chrono.css

    /* stopwatch :before + :after */
    
    #chrono-min,
    #chrono-tenth-sec,
    #chrono-sec {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      width: 3em;
      height: 3em;
    }
    
    #chrono-min {
      position: absolute;
      left: 3.5em; /* 10/2 - width/2 */
      top: 1em;
    }
    
    #chrono-sec {
      position: absolute;
      top: 3.5em;
      left: 1em;
    }
    
    #chrono-tenth-sec {
      position: absolute;
      left: 3.5em; /* 10/2 - width/2 */
      bottom: 1em;
    }
    
    #chrono-min:before {
      content: "";
      display: inline-block;
      width: 3em;
      height: 3em;
      -moz-border-radius: 50%;
      -webkit-border-radius: 50%;
      border-radius: 50%;
      background-color: #fff;
      border: 0.05em solid gray;
    }
    
    #chrono-tenth-sec:before {
      content: "";
      display: inline-block;
      width: 3em;
      height: 3em;
      -moz-border-radius: 50%;
      -webkit-border-radius: 50%;
      border-radius: 50%;
      background-color: #fff;
      border: 0.05em solid gray;
    }
    
    #chrono-sec:before {
      content: "";
      display: inline-block;
      width: 3em;
      height: 3em;
      -moz-border-radius: 50%;
      -webkit-border-radius: 50%;
      border-radius: 50%;
      background-color: #fff;
      border: 0.05em solid gray;
    }
    
    /* stopwatch MIN tick-marks */
    
    #chrono-min span {
      position: absolute;
      top: 0.05em;
      left: 1.55em; /* 3/2 - width/2 */
      width: 0.05em;
      height: 100%;
    }
    
    #chrono-sec span {
      position: absolute;
      top: 0.05em;
      left: 1.55em; /* 3/2 - width/2 */
      width: 0.02em;
      height: 100%;
    }
    
    #chrono-min i,
    #chrono-min span:before,
    #chrono-min span:after,
    #chrono-sec i,
    #chrono-sec span:before,
    #chrono-sec span:after {
      position: absolute;
      top: 0; /*  (3-height) / 2 */
      left: 0;
      width: 100%;
      height: 2.63em;
    }
    
    #chrono-min span:before,
    #chrono-min span:after,
    #chrono-sec span:before,
    #chrono-sec span:after {
      content: "";
      border-style: solid;
      border-color: #000;
      border-width: 0.2em 0;
      /* background-color: #0f0; */
    }
    
    /* ROTATE MIN */
    
    #chrono-min span:before {
      transform: rotate(0deg);
    }
    
    #chrono-min span:after {
      transform: rotate(30deg);
    }
    
    #chrono-min span:nth-child(2) {
      transform: rotate(60deg);
    }
    
    #chrono-min span:nth-child(3) {
      transform: rotate(120deg);
    }
    
    /* ROTATE SEC */
    
    #chrono-sec span:before {
      transform: rotate(0deg);
    }
    
    #chrono-sec span:before {
      transform: rotate(6deg);
    }
    
    #chrono-sec span:nth-child(2) {
      transform: rotate(12deg);
    }
    
    #chrono-sec span:nth-child(3) {
      transform: rotate(24deg);
    }
    
    #chrono-sec span:nth-child(4) {
      transform: rotate(36deg);
    }
    
    #chrono-sec span:nth-child(5) {
      transform: rotate(48deg);
    }
    
    #chrono-sec span:nth-child(6) {
      transform: rotate(60deg);
    }
    
    #chrono-sec span:nth-child(7) {
      transform: rotate(72deg);
    }
    
    #chrono-sec span:nth-child(8) {
      transform: rotate(84deg);
    }
    
    #chrono-sec span:nth-child(9) {
      transform: rotate(96deg);
    }
    
    #chrono-sec span:nth-child(10) {
      transform: rotate(108deg);
    }
    
    #chrono-sec span:nth-child(11) {
      transform: rotate(120deg);
    }
    
    #chrono-sec span:nth-child(12) {
      transform: rotate(132deg);
    }
    
    #chrono-sec span:nth-child(13) {
      transform: rotate(144deg);
    }
    
    #chrono-sec span:nth-child(14) {
      transform: rotate(156deg);
    }
    
    #chrono-sec span:nth-child(15) {
      transform: rotate(168deg);
    }
    
    Code (CSS):
     
    iago111, Dec 30, 2020 IP
  15. iago111

    iago111 Member

    Messages:
    99
    Likes Received:
    4
    Best Answers:
    1
    Trophy Points:
    33
    #15
    Any suggestions?
     
    iago111, Jan 7, 2021 IP