Inspired by https://www.kickstarter.com/projects/1294391907/haptica-braille-watch.
Animation technique inspired from mbostock's Digital Clock.
- D3.js (v.4)
- blockbuilder.org
license: gpl-3.0 | |
height: 150 | |
border: no |
Inspired by https://www.kickstarter.com/projects/1294391907/haptica-braille-watch.
Animation technique inspired from mbostock's Digital Clock.
<!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> |