block by Kcnarf 19ac3bbfd6569140b8ad73e723a8443d

Braille Clock

Full Screen

Inspired by https://www.kickstarter.com/projects/1294391907/haptica-braille-watch.

Animation technique inspired from mbostock‘s Digital Clock.

Acknowledgments to:

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  #container {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  
  #background {
    fill: url("#linear-gradient");
    filter: url(#virtual-light);
  }

  .cell {
    fill: url("#radial-gradient");
    fill-opacity: 1;
    stroke: grey;
    stroke-width: 0.2;
  }

  .unlit {
    fill-opacity: 0;
  }

</style>
<div id="container">
  <svg>
    <defs>
      <linearGradient id="linear-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="25%" stop-color="#eee"></stop>
        <stop offset="75%" stop-color="silver"></stop>
      </linearGradient>
      
      <radialGradient id="radial-gradient" cx="40%" cy="40%" r="35%">
        <stop offset="25%" stop-color="white"></stop>
        <stop offset="75%" stop-color="silver"></stop>
      </radialGradient>
      
      <filter id="virtual-light" filterUnits="objectBoundingBox"
      x="-0.1" y="-0.1" width="1.2" height="1.2">
        <feGaussianBlur in="SourceAlpha" stdDeviation="1" result="alpha_blur"/>
        <feSpecularLighting in="alpha_blur" surfaceScale="20" specularConstant="1"
          specularExponent="5" lighting-color="white" result="spec_light">
          <fePointLight x="-400" y="-400" z="250"/>
        </feSpecularLighting>
        <feComposite in="SourceGraphic" in2="spec_light" operator="out"/>
      </filter>
    </defs>
    <path id="background"/>
    <g transform="translate(20,20)">
    </g>
  </svg>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  
	var digitHeight = 80,
      digitWidth = 80,
      interDigitWidth = 20,
      numberWidth = digitWidth*2+interDigitWidth;
  		interNumberWidth = 80,
  		cellRadius = 10,
      cornerRadius = cellRadius+interDigitWidth,
      width = 3*numberWidth + 2*interNumberWidth + 2*interDigitWidth,
      height = digitHeight + 2*interDigitWidth;
  
  d3.select("svg").attr("width", width).attr("height", height);
  d3.select("#background")
    	.attr("width", width)
    	.attr("height", height)
  		.attr("d","M"+[0,cornerRadius]+
            		"a"+[cornerRadius, cornerRadius, 0, 0, 1, cornerRadius, -cornerRadius]+
            		"h"+(width-2*cornerRadius)+
            		"a"+[cornerRadius, cornerRadius, 0, 0, 1, cornerRadius, cornerRadius]+
            		"v"+(height-2*cornerRadius)+
            		"a"+[cornerRadius, cornerRadius, 0, 0, 1, -cornerRadius, cornerRadius]+
            		"h"+(-(width-2*cornerRadius))+
            		"a"+[cornerRadius, cornerRadius, 0, 0, 1, -cornerRadius, -cornerRadius]+
            		"z");
  
  svg = d3.select("svg g");
  
  //begin: build digits and Braille cells
  var newNumber, newnumberDx,
      newDigit, newDigitDx,
  		newCell, newCellDx, newCellDy;
  d3.range(3).forEach(function(number){
    newNumberDx = number*(numberWidth+interNumberWidth);
    d3.range(2).forEach(function(digit){
      newDigitDx = newNumberDx+digit*(digitWidth+interDigitWidth);
      newDigit = svg.append("g")
        .classed("digit", true)
      	.attr("transform", "translate("+newDigitDx+",0)");
      d3.range(2).forEach(function(hCellIndex){
        newCellDx = (hCellIndex===0)? cellRadius : digitWidth-cellRadius;
        d3.range(2).forEach(function(vCellIndex){
        	newCellDy = (vCellIndex===0)? cellRadius : digitHeight-cellRadius;
          newDigit.append("circle")
          	.classed("cell", true)
          	.attr("r", cellRadius)
          	.attr("cx", newCellDx)
          	.attr("cy", newCellDy);
        })
      })
    })
  })
  //end: build digits and Braille cells

  var digit = svg.selectAll(".digit"),
      separator = svg.selectAll(".separator circle");

  var unlitPattern = [
  //[0,1,2,3,4,5,6,7,8,9]
    [1,0,0,0,0,0,0,0,0,1],
    [0,1,0,1,1,1,0,0,0,0],
    [0,1,1,0,0,1,0,0,1,0],
    [0,1,1,1,0,0,1,0,0,1],
  ];
  
  var translatePattern = [
  //[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    [-1, 0, 0, 0, 0, 0, 0, 0, 0,-1],
    [ 0, 1, 0, 1, 1, 1, 0, 0, 0, 0],
    [ 0,-1,-1, 0, 0,-1, 0, 0,-1, 0],
    [ 0, 1, 1, 1, 0, 0, 1, 0, 0, 1],
  ];

  (function tick() {
    var now = new Date,
        hours = now.getHours(),
        minutes = now.getMinutes(),
        seconds = now.getSeconds()
    		updateType = "lit";
		
    digit = digit.data([hours / 10 | 0, hours % 10, minutes / 10 | 0, minutes % 10, seconds / 10 | 0, seconds % 10]);
      
    if (updateType==="lit") {
      digit.select("circle:nth-child(1)").classed("unlit", function(d) { return unlitPattern[0][d]; });
      digit.select("circle:nth-child(2)").classed("unlit", function(d) { return unlitPattern[1][d]; });
      digit.select("circle:nth-child(3)").classed("unlit", function(d) { return unlitPattern[2][d]; });
      digit.select("circle:nth-child(4)").classed("unlit", function(d) { return unlitPattern[3][d]; });
    } else if (updateType==="translate") {
      digit.select("circle:nth-child(1)").transition().attr("transform", function(d) { return "translate(0,"+2*cellRadius*translatePattern[0][d]+")"; });
      digit.select("circle:nth-child(2)").transition().attr("transform", function(d) { return "translate(0,"+2*cellRadius*translatePattern[1][d]+")"; });
      digit.select("circle:nth-child(3)").transition().attr("transform", function(d) { return "translate(0,"+2*cellRadius*translatePattern[2][d]+")"; });
      digit.select("circle:nth-child(4)").transition().attr("transform", function(d) { return "translate(0,"+2*cellRadius*translatePattern[3][d]+")"; });
    }

    setTimeout(tick, 1000 - now % 1000);
  })();

</script>