skip to Main Content

Here is a first attempt at creating a clock with Vega-lite. Feel free to come back with some ideas on how to improve this. I could only get it to refresh using vega-embed. The hands are either shown or hidden based on simple transform filters. I can’t seem to use SVG Paths for the hands. Any ideas?

enter image description here

<!doctype html> 
<html> 
<head> 
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>      
<body>
<div id="vis"></div>

<script type="text/javascript">
</script>

<script id="jsCode" type="text/javascript"> 

function generateSpec() {
const spec = {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "Vega-lite clock",
"width": 500,
"height": 500, 
"data": {
"values": [
{"hour": "12", "min": "1", "minute": "01", "value": 1},
{"hour": "12", "min": "2", "minute": "02", "value": 1},
{"hour": "12", "min": "3", "minute": "03", "value": 1},
{"hour": "12", "min": "4", "minute": "04", "value": 1},
{"hour": "1", "min": "5", "minute": "05", "value": 1},
{"hour": "1", "min": "6", "minute": "06", "value": 1},
{"hour": "1", "min": "7", "minute": "07", "value": 1},
{"hour": "1", "min": "8", "minute": "08", "value": 1},
{"hour": "1", "min": "9", "minute": "09", "value": 1},
{"hour": "2", "min": "10", "minute": "10", "value": 1},
{"hour": "2", "min": "11", "minute": "11", "value": 1},
{"hour": "2", "min": "12", "minute": "12", "value": 1},
{"hour": "2", "min": "13", "minute": "13", "value": 1},
{"hour": "2", "min": "14", "minute": "14", "value": 1},
{"hour": "3", "min": "15", "minute": "15", "value": 1},
{"hour": "3", "min": "16", "minute": "16", "value": 1},
{"hour": "3", "min": "17", "minute": "17", "value": 1},
{"hour": "3", "min": "18", "minute": "18", "value": 1},
{"hour": "3", "min": "19", "minute": "19", "value": 1},
{"hour": "4", "min": "20", "minute": "20", "value": 1},
{"hour": "4", "min": "21", "minute": "21", "value": 1},
{"hour": "4", "min": "22", "minute": "22", "value": 1},
{"hour": "4", "min": "23", "minute": "23", "value": 1},
{"hour": "4", "min": "24", "minute": "24", "value": 1},
{"hour": "5", "min": "25", "minute": "25", "value": 1},
{"hour": "5", "min": "26", "minute": "26", "value": 1},
{"hour": "5", "min": "27", "minute": "27", "value": 1},
{"hour": "5", "min": "28", "minute": "28", "value": 1},
{"hour": "5", "min": "29", "minute": "29", "value": 1},
{"hour": "6", "min": "30", "minute": "30", "value": 1},
{"hour": "6", "min": "31", "minute": "31", "value": 1},
{"hour": "6", "min": "32", "minute": "32", "value": 1},
{"hour": "6", "min": "33", "minute": "33", "value": 1},
{"hour": "6", "min": "34", "minute": "34", "value": 1},
{"hour": "7", "min": "35", "minute": "35", "value": 1},
{"hour": "7", "min": "36", "minute": "36", "value": 1},
{"hour": "7", "min": "37", "minute": "37", "value": 1},
{"hour": "7", "min": "38", "minute": "38", "value": 1},
{"hour": "7", "min": "39", "minute": "39", "value": 1},
{"hour": "8", "min": "40", "minute": "40", "value": 1},
{"hour": "8", "min": "41", "minute": "41", "value": 1},
{"hour": "8", "min": "42", "minute": "42", "value": 1},
{"hour": "8", "min": "43", "minute": "43", "value": 1},
{"hour": "8", "min": "44", "minute": "44", "value": 1},
{"hour": "9", "min": "45", "minute": "45", "value": 1},
{"hour": "9", "min": "46", "minute": "46", "value": 1},
{"hour": "9", "min": "47", "minute": "47", "value": 1},
{"hour": "9", "min": "48", "minute": "48", "value": 1},
{"hour": "9", "min": "49", "minute": "49", "value": 1},
{"hour": "10", "min": "50", "minute": "50", "value": 1},
{"hour": "10", "min": "51", "minute": "51", "value": 1},
{"hour": "10", "min": "52", "minute": "52", "value": 1},
{"hour": "10", "min": "53", "minute": "53", "value": 1},
{"hour": "10", "min": "54", "minute": "54", "value": 1},
{"hour": "11", "min": "55", "minute": "55", "value": 1},
{"hour": "11", "min": "56", "minute": "56", "value": 1},
{"hour": "11", "min": "57", "minute": "57", "value": 1},
{"hour": "11", "min": "58", "minute": "58", "value": 1},
{"hour": "11", "min": "59", "minute": "59", "value": 1},
{"hour": "12", "min": "0", "minute": "60", "value": 1}
]
},
"transform": [
{"joinaggregate": [{"op": "sum", "field": "value", "as": "total"}]},
{
"sort": [{"field": "minute"}],
"window": [{"op": "sum", "field": "value", "as": "Cumulat"}],
"frame": [null, 0]
},
{
"calculate": "(360* ((datum.Cumulat) / datum.total))-90",
"as": "percentage"
}
],
"layer": [
{
"mark": {"type": "arc", "outerRadius": 365, "innerRadius": 275},
"encoding": {"color": {"value": "aliceblue"}}
},
{
"mark": {"type": "arc", "outerRadius": 278, "innerRadius": 275},
"encoding": {"color": {"value": "silver"}}
},
{
"mark": {
"type": "text",
"radius": 287,
"fontSize": 13,
"angle": {"expr": "datum.percentage"}
},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"text": {"value": "—"},
"color": {"value": "silver"}
}
},
{
"transform": [
{
"filter": {
"field": "minute",
"oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60]
}
}
],
"mark": {
"type": "text",
"radius": 270,
"fontSize": 25,
"angle": {"expr": "datum.percentage"}
},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"text": {"value": "⏴"},
"color": {"value": "black"}
}
},
{
"mark": {"type": "text", "radius": 305},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"text": {"field": "min", "type": "nominal"},
"color": {"value": "black"}
}
},
{
"transform": [
{
"filter": {
"field": "minute",
"oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60]
}
}
],
"mark": {
"type": "text",
"radius": 330,
"fontSize": 15,
"fontWeight": 800
},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"text": {"field": "hour"},
"color": {"value": "black"}
}
},
{
"transform": [
{
"filter": {
"field": "minute",
"oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60]
}
}
],
"mark": {
"type": "text",
"radius": 352,
"fontSize": 12,
"fontWeight": 600,
"text": {"expr": "parseFloat(datum.hour) + 12"}
},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"color": {"value": "silver"}
}
},
{
"transform": [{"filter": "datum.minute === minutes(now())"}],
"mark": {
"type": "text",
"radius": 120,
"fontWeight": 800,
"fontSize": 30,
"angle": {"expr": "datum.percentage"}
},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"text": {"value": "————————"},
"color": {"value": "red"}
}
},
{
"transform": [
{
"filter": "datum.hour == hours(now()) || datum.hour == hours(now())-12"
},
{
"filter": "((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 1  || hours(now()) == 13) && datum.minute == '5') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 2  || hours(now()) == 14) && datum.minute == '10') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 3  || hours(now()) == 15) && datum.minute == '15') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 4  || hours(now()) == 16) && datum.minute == '20') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 5  || hours(now()) == 17) && datum.minute == '25') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 6  || hours(now()) == 18) && datum.minute == '30') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 7  || hours(now()) == 19) && datum.minute == '35') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 8  || hours(now()) == 20) && datum.minute == '40') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 9  || hours(now()) == 21) && datum.minute == '45') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 10 || hours(now()) == 22) && datum.minute == '50') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 11 || hours(now()) == 23) && datum.minute == '55') ||((minutes(now()) >= 0 && minutes(now()) <= 12) && (hours(now()) == 12 || hours(now()) == 0 ) && datum.minute == '60') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 1  || hours(now()) == 13) && datum.minute == '6') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 2  || hours(now()) == 14) && datum.minute == '11') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 3  || hours(now()) == 15) && datum.minute == '16') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 4  || hours(now()) == 16) && datum.minute == '21') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 5  || hours(now()) == 17) && datum.minute == '26') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 6  || hours(now()) == 18) && datum.minute == '31') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 7  || hours(now()) == 19) && datum.minute == '36') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 8  || hours(now()) == 20) && datum.minute == '41') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 9  || hours(now()) == 21) && datum.minute == '46') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 10 || hours(now()) == 22) && datum.minute == '51') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 11 || hours(now()) == 23) && datum.minute == '56') ||((minutes(now()) >= 13 && minutes(now()) <= 24) && (hours(now()) == 12 || hours(now()) == 0 ) && datum.minute == '1') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 1  || hours(now()) == 13) && datum.minute == '7') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 2  || hours(now()) == 14) && datum.minute == '12') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 3  || hours(now()) == 15) && datum.minute == '17') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 4  || hours(now()) == 16) && datum.minute == '22') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 5  || hours(now()) == 17) && datum.minute == '27') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 6  || hours(now()) == 18) && datum.minute == '32') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 7  || hours(now()) == 19) && datum.minute == '37') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 8  || hours(now()) == 20) && datum.minute == '42') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 9  || hours(now()) == 21) && datum.minute == '47') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 10 || hours(now()) == 22) && datum.minute == '52') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 11 || hours(now()) == 23) && datum.minute == '57') ||((minutes(now()) >= 25 && minutes(now()) <= 36) && (hours(now()) == 12 || hours(now()) == 0 ) && datum.minute == '2') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 1  || hours(now()) == 13) && datum.minute == '8') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 2  || hours(now()) == 14) && datum.minute == '13') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 3  || hours(now()) == 15) && datum.minute == '18') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 4  || hours(now()) == 16) && datum.minute == '23') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 5  || hours(now()) == 17) && datum.minute == '28') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 6  || hours(now()) == 18) && datum.minute == '33') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 7  || hours(now()) == 19) && datum.minute == '38') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 8  || hours(now()) == 20) && datum.minute == '43') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 9  || hours(now()) == 21) && datum.minute == '48') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 10 || hours(now()) == 22) && datum.minute == '53') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 11 || hours(now()) == 23) && datum.minute == '58') ||((minutes(now()) >= 37 && minutes(now()) <= 48) && (hours(now()) == 12 || hours(now()) == 0 ) && datum.minute == '3') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 1  || hours(now()) == 13) && datum.minute == '9') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 2  || hours(now()) == 14) && datum.minute == '14') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 3  || hours(now()) == 15) && datum.minute == '19') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 4  || hours(now()) == 16) && datum.minute == '24') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 5  || hours(now()) == 17) && datum.minute == '29') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 6  || hours(now()) == 18) && datum.minute == '34') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 7  || hours(now()) == 19) && datum.minute == '39') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 8  || hours(now()) == 20) && datum.minute == '44') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 9  || hours(now()) == 21) && datum.minute == '49') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 10 || hours(now()) == 22) && datum.minute == '54') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 11 || hours(now()) == 23) && datum.minute == '59') ||((minutes(now()) >= 49 && minutes(now()) <= 60) && (hours(now()) == 12 || hours(now()) == 0 ) && datum.minute == '4')"
}
],
"mark": {
"type": "text",
"radius": 80,
"fontWeight": 800,
"fontSize": 60,
"angle": {"expr": "datum.percentage"}
},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"text": {"value": "———"},
"color": {"value": "navy"}
}
},
{
"transform": [
{
"filter": "(seconds(now()) == 0 && datum.minute == '60') ||(seconds(now()) == 1 && datum.minute == '1') ||(seconds(now()) == 2 && datum.minute == '2') ||(seconds(now()) == 3 && datum.minute == '3') ||(seconds(now()) == 4 && datum.minute == '4') ||(seconds(now()) == 5 && datum.minute == '5') ||(seconds(now()) == 6 && datum.minute == '6') ||(seconds(now()) == 7 && datum.minute == '7') ||(seconds(now()) == 8 && datum.minute == '8') ||(seconds(now()) == 9 && datum.minute == '9') ||(seconds(now()) == 10 && datum.minute == '10') ||(seconds(now()) == 11 && datum.minute == '11') ||(seconds(now()) == 12 && datum.minute == '12') ||(seconds(now()) == 13 && datum.minute == '13') ||(seconds(now()) == 14 && datum.minute == '14') ||(seconds(now()) == 15 && datum.minute == '15') ||(seconds(now()) == 16 && datum.minute == '16') ||(seconds(now()) == 17 && datum.minute == '17') ||(seconds(now()) == 18 && datum.minute == '18') ||(seconds(now()) == 19 && datum.minute == '19') ||(seconds(now()) == 20 && datum.minute == '20') ||(seconds(now()) == 21 && datum.minute == '21') ||(seconds(now()) == 22 && datum.minute == '22') ||(seconds(now()) == 23 && datum.minute == '23') ||(seconds(now()) == 24 && datum.minute == '24') ||(seconds(now()) == 25 && datum.minute == '25') ||(seconds(now()) == 26 && datum.minute == '26') ||(seconds(now()) == 27 && datum.minute == '27') ||(seconds(now()) == 28 && datum.minute == '28') ||(seconds(now()) == 29 && datum.minute == '29') ||(seconds(now()) == 30 && datum.minute == '30') ||(seconds(now()) == 31 && datum.minute == '31') ||(seconds(now()) == 32 && datum.minute == '32') ||(seconds(now()) == 33 && datum.minute == '33') ||(seconds(now()) == 34 && datum.minute == '34') ||(seconds(now()) == 35 && datum.minute == '35') ||(seconds(now()) == 36 && datum.minute == '36') ||(seconds(now()) == 37 && datum.minute == '37') ||(seconds(now()) == 38 && datum.minute == '38') ||(seconds(now()) == 39 && datum.minute == '39') ||(seconds(now()) == 40 && datum.minute == '40') ||(seconds(now()) == 41 && datum.minute == '41') ||(seconds(now()) == 42 && datum.minute == '42') ||(seconds(now()) == 43 && datum.minute == '43') ||(seconds(now()) == 44 && datum.minute == '44') ||(seconds(now()) == 45 && datum.minute == '45') ||(seconds(now()) == 46 && datum.minute == '46') ||(seconds(now()) == 47 && datum.minute == '47') ||(seconds(now()) == 48 && datum.minute == '48') ||(seconds(now()) == 49 && datum.minute == '49') ||(seconds(now()) == 50 && datum.minute == '50') ||(seconds(now()) == 51 && datum.minute == '51') ||(seconds(now()) == 52 && datum.minute == '52') ||(seconds(now()) == 53 && datum.minute == '53') ||(seconds(now()) == 54 && datum.minute == '54') ||(seconds(now()) == 55 && datum.minute == '55') ||(seconds(now()) == 56 && datum.minute == '56') ||(seconds(now()) == 57 && datum.minute == '57') ||(seconds(now()) == 58 && datum.minute == '58') ||(seconds(now()) == 59 && datum.minute == '59') ||(seconds(now()) == 60 && datum.minute == '60')"
}
],
"mark": {
"type": "text",
"radius": 135,
"fontWeight": 800,
"angle": {"expr": "datum.percentage"}
},
"encoding": {
"theta": {"field": "Cumulat", "type": "quantitative"},
"text": {"value": "———————————————————————"},
"color": {"value": "black"}
}
},
{
"transform": [
{
"filter": {
"field": "minute",
"oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 0]
}
}
],
"mark": {"type": "circle", "size": 500},
"encoding": {"color": {"value": "silver"}}
},
]
};

return spec;
}

let view;
vegaEmbed('#vis', generateSpec())
.then((result) => {
view = result.view;
})
.catch(console.error);

setInterval(() => {
const newSpec = generateSpec();
vegaEmbed('#vis', newSpec, {actions: false}).then((result) => {
view = result.view;
}).catch(console.error);
}, 1000);
</script>   
</body>
</html>

2

Answers


  1. Chosen as BEST ANSWER

    Here is a much more simple approach and also uses svg hands.

    Apple iOS style with day date.

    enter image description here

    <!doctype html> 
    <html> 
    <head> 
    <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
    <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
    </head>      
    <body>
    <div id="vis"></div>
    
    <script type="text/javascript">
    </script>
    
    <script id="jsCode" type="text/javascript"> 
    
    function generateSpec() {
    const spec = {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "description": "Vega-lite clock",
      "usermeta": {"embedOptions": {"renderer": "svg"}},
      "data": {
        "values": [
          {"hour": "12", "min": "1", "minute": "01", "value": 1},
          {"hour": "12", "min": "2", "minute": "02", "value": 1},
          {"hour": "12", "min": "3", "minute": "03", "value": 1},
          {"hour": "12", "min": "4", "minute": "04", "value": 1},
          {"hour": "1", "min": "5", "minute": "05", "value": 1},
          {"hour": "1", "min": "6", "minute": "06", "value": 1},
          {"hour": "1", "min": "7", "minute": "07", "value": 1},
          {"hour": "1", "min": "8", "minute": "08", "value": 1},
          {"hour": "1", "min": "9", "minute": "09", "value": 1},
          {"hour": "2", "min": "10", "minute": "10", "value": 1},
          {"hour": "2", "min": "11", "minute": "11", "value": 1},
          {"hour": "2", "min": "12", "minute": "12", "value": 1},
          {"hour": "2", "min": "13", "minute": "13", "value": 1},
          {"hour": "2", "min": "14", "minute": "14", "value": 1},
          {"hour": "3", "min": "15", "minute": "15", "value": 1},
          {"hour": "3", "min": "16", "minute": "16", "value": 1},
          {"hour": "3", "min": "17", "minute": "17", "value": 1},
          {"hour": "3", "min": "18", "minute": "18", "value": 1},
          {"hour": "3", "min": "19", "minute": "19", "value": 1},
          {"hour": "4", "min": "20", "minute": "20", "value": 1},
          {"hour": "4", "min": "21", "minute": "21", "value": 1},
          {"hour": "4", "min": "22", "minute": "22", "value": 1},
          {"hour": "4", "min": "23", "minute": "23", "value": 1},
          {"hour": "4", "min": "24", "minute": "24", "value": 1},
          {"hour": "5", "min": "25", "minute": "25", "value": 1},
          {"hour": "5", "min": "26", "minute": "26", "value": 1},
          {"hour": "5", "min": "27", "minute": "27", "value": 1},
          {"hour": "5", "min": "28", "minute": "28", "value": 1},
          {"hour": "5", "min": "29", "minute": "29", "value": 1},
          {"hour": "6", "min": "30", "minute": "30", "value": 1},
          {"hour": "6", "min": "31", "minute": "31", "value": 1},
          {"hour": "6", "min": "32", "minute": "32", "value": 1},
          {"hour": "6", "min": "33", "minute": "33", "value": 1},
          {"hour": "6", "min": "34", "minute": "34", "value": 1},
          {"hour": "7", "min": "35", "minute": "35", "value": 1},
          {"hour": "7", "min": "36", "minute": "36", "value": 1},
          {"hour": "7", "min": "37", "minute": "37", "value": 1},
          {"hour": "7", "min": "38", "minute": "38", "value": 1},
          {"hour": "7", "min": "39", "minute": "39", "value": 1},
          {"hour": "8", "min": "40", "minute": "40", "value": 1},
          {"hour": "8", "min": "41", "minute": "41", "value": 1},
          {"hour": "8", "min": "42", "minute": "42", "value": 1},
          {"hour": "8", "min": "43", "minute": "43", "value": 1},
          {"hour": "8", "min": "44", "minute": "44", "value": 1},
          {"hour": "9", "min": "45", "minute": "45", "value": 1},
          {"hour": "9", "min": "46", "minute": "46", "value": 1},
          {"hour": "9", "min": "47", "minute": "47", "value": 1},
          {"hour": "9", "min": "48", "minute": "48", "value": 1},
          {"hour": "9", "min": "49", "minute": "49", "value": 1},
          {"hour": "10", "min": "50", "minute": "50", "value": 1},
          {"hour": "10", "min": "51", "minute": "51", "value": 1},
          {"hour": "10", "min": "52", "minute": "52", "value": 1},
          {"hour": "10", "min": "53", "minute": "53", "value": 1},
          {"hour": "10", "min": "54", "minute": "54", "value": 1},
          {"hour": "11", "min": "55", "minute": "55", "value": 1},
          {"hour": "11", "min": "56", "minute": "56", "value": 1},
          {"hour": "11", "min": "57", "minute": "57", "value": 1},
          {"hour": "11", "min": "58", "minute": "58", "value": 1},
          {"hour": "11", "min": "59", "minute": "59", "value": 1},
          {"hour": "12", "min": "0", "minute": "60", "value": 1}
        ]
      },
      "params": [
        {
          "name": "_svg_min",
          "value": "M -2 0 a 3 3 0 1 0 6 0 v -130 a 3 3 0 1 0 -6 0 z"
        },
        {
          "name": "_svg_hr",
          "value": "M -2 0 a 3 3 0 1 0 6 0 v -90 a 3 3 0 1 0 -6 0 z"
        },
        {"name": "_svg_second", "value": "M 0 -160 l 0 190"}
      ],
      "transform": [
        {"joinaggregate": [{"op": "sum", "field": "value", "as": "total"}]},
        {
          "sort": [{"field": "minute"}],
          "window": [{"op": "sum", "field": "value", "as": "Cumulat"}],
          "frame": [null, 0]
        },
        {
          "calculate": "(360* ((datum.Cumulat) / datum.total))-90",
          "as": "percentage"
        },
        {"calculate": "minutes(now()) / 60 * 360", "as": "min_degrees"},
        {
          "calculate": "((hours(now()) % 12) * 30) + (minutes(now()) / 60 * 30)",
          "as": "hr_degrees"
        },
        {"calculate": "seconds(now()) * 6", "as": "sec_degrees"},
        {"calculate": "timeFormat(now(), '%a %e')", "as": "curr_date1"},
        {"calculate": "timeFormat(now(), '%B')", "as": "curr_date2"}
      ],
      "layer": [
        {
          "mark": {"type": "arc", "outerRadius": 200, "innerRadius": 199},
          "encoding": {"color": {"value": "#dddddd"}}
        },
        {
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 50,
            "fontWeight": 400,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "silver"}
          }
        },
        {
          "transform": [
            {
              "filter": {
                "field": "minute",
                "oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 0]
              }
            }
          ],
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 50,
            "fontWeight": 400,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "#1C1C1E"}
          }
        },
        {
          "transform": [{"filter": "datum.minute == minutes(now())"}],
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 50,
            "fontWeight": 400,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "#F79100"}
          }
        },
        {
          "transform": [{"filter": "datum.minute == seconds(now()) || seconds(now()) == 0 && datum.minute == '60'"}],
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 70,
            "fontWeight": 800,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "#FFFFFF"}
          }
        },
        {
          "transform": [{"filter": "datum.minute == seconds(now()) || seconds(now()) == 0 && datum.minute == '60'"}],
          "mark": {
            "type": "text",
            "radius": 190,
            "fontSize": 30,
            "fontWeight": 800,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "🢐"},
            "color": {"value": "#F79100"}
          }
        },
        {
          "transform": [
            {
              "filter": {
                "field": "minute",
                "oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60]
              }
            }
          ],
          "mark": {
            "type": "text",
            "radius": 145,
            "fontSize": 40,
            "fontWeight": 600,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"field": "hour"},
            "color": {
              "condition": {
                "test": "datum.hour == hours(now()) || datum.hour == hours(now())-12",
                "value": "#F79100"
              },
              "value": "black"
            }
          }
        },
        {
          "transform": [
            {
              "filter": {
                "field": "minute",
                "oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 0]
              }
            }
          ],
          "mark": {"type": "circle", "size": 500},
          "encoding": {"color": {"value": "#1C1C1E"}}
        },
        {
          "mark": {
            "type": "point",
            "size": 5.1,
            "shape": {"expr": "_svg_min"},
            "angle": {"expr": "datum.min_degrees"}
          },
          "encoding": {"fill": {"value": "#1C1C1E"}, "stroke": {"value": "#1C1C1E"}}
        },
        {
          "mark": {
            "type": "point",
            "size": 5.1,
            "shape": {"expr": "_svg_hr"},
            "angle": {"expr": "datum.hr_degrees"}
          },
          "encoding": {"fill": {"value": "#1C1C1E"}, "stroke": {"value": "#1C1C1E"}}
        },
        {
          "mark": {
            "type": "point",
            "size": 5.1,
            "shape": {"expr": "_svg_second"},
            "angle": {"expr": "datum.sec_degrees"}
          },
          "encoding": {"fill": {"value": "#F79100"}, "stroke": {"value": "#F79100"}}
        },
        {
          "mark": {"type": "circle", "size": 200},
          "encoding": {"color": {"value": "#F79100"}}
        },
        {
          "mark": {"type": "circle", "size": 50},
          "encoding": {"color": {"value": "#FFF"}}
        },
        {
          "transform": [{"filter": {"field": "minute", "oneOf": [14]}}],
          "mark": {
            "type": "text",
            "radius": 80,
            "fontSize": 18,
            "fontWeight": 600,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"field": "curr_date1"},
            "color": {"value": "silver"}
          }
        },
        {
          "transform": [{"filter": {"field": "minute", "oneOf": [16]}}],
          "mark": {
            "type": "text",
            "radius": 80,
            "fontSize": 16,
            "fontWeight": 400,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"field": "curr_date2"},
            "color": {"value": "silver"}
          }
        },
        {
          "transform": [{"filter": {"field": "minute", "oneOf": [60]}}],
          "mark": {
            "type": "text",
            "radius": 80,
            "fontSize": 28,
            "fontWeight": 400,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "VL"},
            "color": {"value": "#F79100"}
          }
        }
      ]
    };
    
    return spec;
    }
    
    let view;
    vegaEmbed('#vis', generateSpec())
    .then((result) => {
    view = result.view;
    })
    .catch(console.error);
    
    setInterval(() => {
    const newSpec = generateSpec();
    vegaEmbed('#vis', newSpec, {renderer: "svg", actions: false}).then((result) => {
    view = result.view;
    }).catch(console.error);
    }, 1000);
    </script>   
    </body>
    </html>
    

    Adam


  2. Here is a way to get a timer in Vega-Lite so you don’t need vega-embed. You will get an underline in the editor but you can ignore it.

    {
      "name": "time", 
         "on": [{"events": {"type": "timer", "throttle": 1000}, "update": "now()"}] 
    },
    

    This updates every second:

    Full code of working clock in VL.

    {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "description": "Vega-lite clock",
      "usermeta": {"embedOptions": {"renderer": "svg"}},
      "data": {
        "values": [
          {"hour": "12", "min": "1", "minute": "01", "value": 1},
          {"hour": "12", "min": "2", "minute": "02", "value": 1},
          {"hour": "12", "min": "3", "minute": "03", "value": 1},
          {"hour": "12", "min": "4", "minute": "04", "value": 1},
          {"hour": "1", "min": "5", "minute": "05", "value": 1},
          {"hour": "1", "min": "6", "minute": "06", "value": 1},
          {"hour": "1", "min": "7", "minute": "07", "value": 1},
          {"hour": "1", "min": "8", "minute": "08", "value": 1},
          {"hour": "1", "min": "9", "minute": "09", "value": 1},
          {"hour": "2", "min": "10", "minute": "10", "value": 1},
          {"hour": "2", "min": "11", "minute": "11", "value": 1},
          {"hour": "2", "min": "12", "minute": "12", "value": 1},
          {"hour": "2", "min": "13", "minute": "13", "value": 1},
          {"hour": "2", "min": "14", "minute": "14", "value": 1},
          {"hour": "3", "min": "15", "minute": "15", "value": 1},
          {"hour": "3", "min": "16", "minute": "16", "value": 1},
          {"hour": "3", "min": "17", "minute": "17", "value": 1},
          {"hour": "3", "min": "18", "minute": "18", "value": 1},
          {"hour": "3", "min": "19", "minute": "19", "value": 1},
          {"hour": "4", "min": "20", "minute": "20", "value": 1},
          {"hour": "4", "min": "21", "minute": "21", "value": 1},
          {"hour": "4", "min": "22", "minute": "22", "value": 1},
          {"hour": "4", "min": "23", "minute": "23", "value": 1},
          {"hour": "4", "min": "24", "minute": "24", "value": 1},
          {"hour": "5", "min": "25", "minute": "25", "value": 1},
          {"hour": "5", "min": "26", "minute": "26", "value": 1},
          {"hour": "5", "min": "27", "minute": "27", "value": 1},
          {"hour": "5", "min": "28", "minute": "28", "value": 1},
          {"hour": "5", "min": "29", "minute": "29", "value": 1},
          {"hour": "6", "min": "30", "minute": "30", "value": 1},
          {"hour": "6", "min": "31", "minute": "31", "value": 1},
          {"hour": "6", "min": "32", "minute": "32", "value": 1},
          {"hour": "6", "min": "33", "minute": "33", "value": 1},
          {"hour": "6", "min": "34", "minute": "34", "value": 1},
          {"hour": "7", "min": "35", "minute": "35", "value": 1},
          {"hour": "7", "min": "36", "minute": "36", "value": 1},
          {"hour": "7", "min": "37", "minute": "37", "value": 1},
          {"hour": "7", "min": "38", "minute": "38", "value": 1},
          {"hour": "7", "min": "39", "minute": "39", "value": 1},
          {"hour": "8", "min": "40", "minute": "40", "value": 1},
          {"hour": "8", "min": "41", "minute": "41", "value": 1},
          {"hour": "8", "min": "42", "minute": "42", "value": 1},
          {"hour": "8", "min": "43", "minute": "43", "value": 1},
          {"hour": "8", "min": "44", "minute": "44", "value": 1},
          {"hour": "9", "min": "45", "minute": "45", "value": 1},
          {"hour": "9", "min": "46", "minute": "46", "value": 1},
          {"hour": "9", "min": "47", "minute": "47", "value": 1},
          {"hour": "9", "min": "48", "minute": "48", "value": 1},
          {"hour": "9", "min": "49", "minute": "49", "value": 1},
          {"hour": "10", "min": "50", "minute": "50", "value": 1},
          {"hour": "10", "min": "51", "minute": "51", "value": 1},
          {"hour": "10", "min": "52", "minute": "52", "value": 1},
          {"hour": "10", "min": "53", "minute": "53", "value": 1},
          {"hour": "10", "min": "54", "minute": "54", "value": 1},
          {"hour": "11", "min": "55", "minute": "55", "value": 1},
          {"hour": "11", "min": "56", "minute": "56", "value": 1},
          {"hour": "11", "min": "57", "minute": "57", "value": 1},
          {"hour": "11", "min": "58", "minute": "58", "value": 1},
          {"hour": "11", "min": "59", "minute": "59", "value": 1},
          {"hour": "12", "min": "0", "minute": "60", "value": 1}
        ]
      },
      "params": [
        {
          "name": "_svg_min",
          "value": "M -2 0 a 3 3 0 1 0 6 0 v -130 a 3 3 0 1 0 -6 0 z"
        },
        {
          "name": "_svg_hr",
          "value": "M -2 0 a 3 3 0 1 0 6 0 v -90 a 3 3 0 1 0 -6 0 z"
        },
        {"name": "_svg_second", "value": "M 0 -160 l 0 190"},
        {
          "name": "time",
          "on": [{"events": {"type": "timer", "throttle": 1000}, "update": "now()"}]
        }
      ],
      "transform": [
        {"joinaggregate": [{"op": "sum", "field": "value", "as": "total"}]},
        {
          "sort": [{"field": "minute"}],
          "window": [{"op": "sum", "field": "value", "as": "Cumulat"}],
          "frame": [null, 0]
        },
        {
          "calculate": "(360* ((datum.Cumulat) / datum.total))-90",
          "as": "percentage"
        },
        {"calculate": "minutes(time) / 60 * 360", "as": "min_degrees"},
        {
          "calculate": "((hours(time) % 12) * 30) + (minutes(time) / 60 * 30)",
          "as": "hr_degrees"
        },
        {"calculate": "seconds(time) * 6", "as": "sec_degrees"},
        {"calculate": "timeFormat(time, '%a %e')", "as": "curr_date1"},
        {"calculate": "timeFormat(time, '%B')", "as": "curr_date2"}
      ],
      "layer": [
        {
          "mark": {"type": "arc", "outerRadius": 200, "innerRadius": 199},
          "encoding": {"color": {"value": "#dddddd"}}
        },
        {
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 50,
            "fontWeight": 400,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "silver"}
          }
        },
        {
          "transform": [
            {
              "filter": {
                "field": "minute",
                "oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 0]
              }
            }
          ],
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 50,
            "fontWeight": 400,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "#1C1C1E"}
          }
        },
        {
          "transform": [{"filter": "datum.minute == minutes(time)"}],
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 50,
            "fontWeight": 400,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "#F79100"}
          }
        },
        {
          "transform": [
            {
              "filter": "datum.minute == seconds(time) || seconds(time) == 0 && datum.minute == '60'"
            }
          ],
          "mark": {
            "type": "text",
            "radius": 180,
            "fontSize": 70,
            "fontWeight": 800,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "-"},
            "color": {"value": "#FFFFFF"}
          }
        },
        {
          "transform": [
            {
              "filter": "datum.minute == seconds(time) || seconds(time) == 0 && datum.minute == '60'"
            }
          ],
          "mark": {
            "type": "text",
            "radius": 190,
            "fontSize": 30,
            "fontWeight": 800,
            "angle": {"expr": "datum.percentage"}
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "🢐"},
            "color": {"value": "#F79100"}
          }
        },
        {
          "transform": [
            {
              "filter": {
                "field": "minute",
                "oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60]
              }
            }
          ],
          "mark": {
            "type": "text",
            "radius": 145,
            "fontSize": 40,
            "fontWeight": 600,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"field": "hour"},
            "color": {
              "condition": {
                "test": "datum.hour == hours(time) || datum.hour == hours(time)-12",
                "value": "#F79100"
              },
              "value": "black"
            }
          }
        },
        {
          "transform": [
            {
              "filter": {
                "field": "minute",
                "oneOf": [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 0]
              }
            }
          ],
          "mark": {"type": "circle", "size": 500},
          "encoding": {"color": {"value": "#1C1C1E"}}
        },
        {
          "mark": {
            "type": "point",
            "size": 5.1,
            "shape": {"expr": "_svg_min"},
            "angle": {"expr": "datum.min_degrees"}
          },
          "encoding": {"fill": {"value": "#1C1C1E"}, "stroke": {"value": "#1C1C1E"}}
        },
        {
          "mark": {
            "type": "point",
            "size": 5.1,
            "shape": {"expr": "_svg_hr"},
            "angle": {"expr": "datum.hr_degrees"}
          },
          "encoding": {"fill": {"value": "#1C1C1E"}, "stroke": {"value": "#1C1C1E"}}
        },
        {
          "mark": {
            "type": "point",
            "size": 5.1,
            "shape": {"expr": "_svg_second"},
            "angle": {"expr": "datum.sec_degrees"}
          },
          "encoding": {"fill": {"value": "#F79100"}, "stroke": {"value": "#F79100"}}
        },
        {
          "mark": {"type": "circle", "size": 200},
          "encoding": {"color": {"value": "#F79100"}}
        },
        {
          "mark": {"type": "circle", "size": 50},
          "encoding": {"color": {"value": "#FFF"}}
        },
        {
          "transform": [{"filter": {"field": "minute", "oneOf": [14]}}],
          "mark": {
            "type": "text",
            "radius": 80,
            "fontSize": 18,
            "fontWeight": 600,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"field": "curr_date1"},
            "color": {"value": "silver"}
          }
        },
        {
          "transform": [{"filter": {"field": "minute", "oneOf": [16]}}],
          "mark": {
            "type": "text",
            "radius": 80,
            "fontSize": 16,
            "fontWeight": 400,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"field": "curr_date2"},
            "color": {"value": "silver"}
          }
        },
        {
          "transform": [{"filter": {"field": "minute", "oneOf": [60]}}],
          "mark": {
            "type": "text",
            "radius": 80,
            "fontSize": 28,
            "fontWeight": 400,
            "font": "Arial"
          },
          "encoding": {
            "theta": {"field": "Cumulat", "type": "quantitative"},
            "text": {"value": "VL"},
            "color": {"value": "#F79100"}
          }
        }
      ]
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search