Skip to content

Animated breath chart for those stressed out #9567

@apb-reports

Description

@apb-reports

Enhancement Description

Just wanted to share this spec for a nice relaxing breath chart: 5 seconds in, 5 seconds out:)

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "width": 400,
  "height": 400,
  "params": [
    {
      "name": "time",
      "on": [{"events": {"type": "timer", "throttle": 20}, "update": "now()"}]
    }
  ],
  "transform": [
    {"joinaggregate": [{"op": "max", "field": "width", "as": "maxWidth"}]},
    {"calculate": "100", "as": "delay"},
    {"calculate": "seconds(time)", "as": "seconds"}
  ],
  "data": {
    "values": [
      {"id": 1, "width": 200},
      {"id": 2, "width": 200},
      {"id": 3, "width": 200},
      {"id": 4, "width": 200},
      {"id": 5, "width": 200},
      {"id": 6, "width": 200},
      {"id": 7, "width": 200},
      {"id": 8, "width": 200},
      {"id": 9, "width": 200},
      {"id": 10, "width": 200},
      {"id": 11, "width": 200}
    ]
  },
  "layer": [
    {
      "transform": [{"calculate": "seconds(time)", "as": "seconds"}],
      "mark": {
        "type": "circle",
        "size": 31000,
        "color": {
          "gradient": "radial",
          "stops": [
            {"offset": 0, "color": "#fff"},
            {"offset": 1, "color": "#a3bcdd"}
          ]
        },
        "opacity": 0.1
      },
      "encoding": {
        "x": {"field": "maxWidth", "axis": null},
        "y": {"field": "maxWidth", "axis": null}
      }
    },
    {
      "transform": [
        {"calculate": "seconds(time)", "as": "seconds"},
        {
          "filter": "indexof([1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,31,32,33,34,35,41,42,43,44,45,51,52,53,54,55], datum.seconds) !== -1"
        },
        {"calculate": "((milliseconds(time)/1000)*1000)", "as": "sec"},
        {
          "calculate": "datum.seconds== 0 ?  0: datum.seconds<=5 ?  5000-((datum.seconds*1000)-0): datum.seconds<=10 ? 5000-((datum.seconds*1000)-5000): datum.seconds<=15 ? 5000-((datum.seconds*1000)-10000): datum.seconds<=20 ? 5000-((datum.seconds*1000)-15000): datum.seconds<=25 ? 5000-((datum.seconds*1000)-20000): datum.seconds<=30 ? 5000-((datum.seconds*1000)-25000): datum.seconds<=35 ? 5000-((datum.seconds*1000)-30000): datum.seconds<=40 ? 5000-((datum.seconds*1000)-35000): datum.seconds<=45 ? 5000-((datum.seconds*1000)-40000): datum.seconds<=50 ? 5000-((datum.seconds*1000)-45000): datum.seconds<=55 ? 5000-((datum.seconds*1000)-50000): datum.seconds<=60 ? 5000-((datum.seconds*1000)-55000): 60000",
          "as": "sizeReset"
        },
        {
          "calculate": "0.4-(((5000-(datum.sizeReset-datum.sec))/5000)/2)",
          "as": "opacityFactor"
        }
      ],
      "data": {"values": [{"id": 1, "descr": "Exhale"}]},
      "mark": {
        "type": "text",
        "dy": -120,
        "fontSize": 20,
        "color": "#286abf",
        "opacity": {"expr": "datum.opacityFactor"}
      },
      "encoding": {"text": {"field": "descr"}}
    },
    {
      "transform": [
        {"calculate": "seconds(time)", "as": "seconds"},
        {
          "filter": "indexof([1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,31,32,33,34,35,41,42,43,44,45,51,52,53,54,55], datum.seconds) === -1"
        },
        {"calculate": "((milliseconds(time)/1000)*1000)", "as": "sec"},
        {
          "calculate": "datum.seconds== 0 ?  0: datum.seconds<=5 ?  5000-((datum.seconds*1000)-0): datum.seconds<=10 ? 5000-((datum.seconds*1000)-5000): datum.seconds<=15 ? 5000-((datum.seconds*1000)-10000): datum.seconds<=20 ? 5000-((datum.seconds*1000)-15000): datum.seconds<=25 ? 5000-((datum.seconds*1000)-20000): datum.seconds<=30 ? 5000-((datum.seconds*1000)-25000): datum.seconds<=35 ? 5000-((datum.seconds*1000)-30000): datum.seconds<=40 ? 5000-((datum.seconds*1000)-35000): datum.seconds<=45 ? 5000-((datum.seconds*1000)-40000): datum.seconds<=50 ? 5000-((datum.seconds*1000)-45000): datum.seconds<=55 ? 5000-((datum.seconds*1000)-50000): datum.seconds<=60 ? 5000-((datum.seconds*1000)-55000): 60000",
          "as": "sizeReset"
        },
        {
          "calculate": "0.4-(((5000-(datum.sizeReset-datum.sec))/5000)/2)",
          "as": "opacityFactor"
        }
      ],
      "data": {"values": [{"id": 1, "descr": "Inhale"}]},
      "mark": {
        "type": "text",
        "dy": -120,
        "fontSize": 20,
        "color": "#286abf",
        "opacity": {"expr": "datum.opacityFactor"}
      },
      "encoding": {"text": {"field": "descr"}}
    },
    {
      "transform": [
        {"calculate": "seconds(time)", "as": "seconds"},
        {
          "filter": "indexof([1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,31,32,33,34,35,41,42,43,44,45,51,52,53,54,55], datum.seconds) !== -1"
        },
        {"calculate": "((milliseconds(time)/1000)*1000)", "as": "sec"},
        {
          "calculate": "datum.seconds== 0 ?  0: datum.seconds<=5 ?  5000-((datum.seconds*1000)-0): datum.seconds<=10 ? 5000-((datum.seconds*1000)-5000): datum.seconds<=15 ? 5000-((datum.seconds*1000)-10000): datum.seconds<=20 ? 5000-((datum.seconds*1000)-15000): datum.seconds<=25 ? 5000-((datum.seconds*1000)-20000): datum.seconds<=30 ? 5000-((datum.seconds*1000)-25000): datum.seconds<=35 ? 5000-((datum.seconds*1000)-30000): datum.seconds<=40 ? 5000-((datum.seconds*1000)-35000): datum.seconds<=45 ? 5000-((datum.seconds*1000)-40000): datum.seconds<=50 ? 5000-((datum.seconds*1000)-45000): datum.seconds<=55 ? 5000-((datum.seconds*1000)-50000): datum.seconds<=60 ? 5000-((datum.seconds*1000)-55000): 60000",
          "as": "sizeReset"
        },
        {
          "calculate": "5000-(4000-(datum.sizeReset-datum.sec))",
          "as": "sizeFactor"
        },
        {"calculate": "0.3", "as": "Opacity"},
        {"calculate": "(datum.sizeFactor)/100", "as": "movement"},
        {"calculate": "datum.id*(20)", "as": "angle"},
        {
          "calculate": "(200 - cos(datum.angle + (PI/2)) * (datum.movement))",
          "as": "x_pos"
        },
        {
          "calculate": "(200 - cos(datum.angle) * (datum.movement))",
          "as": "y_pos"
        }
      ],
      "mark": {
        "type": "circle",
        "size": {"expr": "datum.sizeFactor"},
        "opacity": {"expr": "datum.Opacity"}
      },
      "encoding": {
        "x": {
          "field": "x_pos",
          "type": "quantitative",
          "scale": {"domain": [400, 0]},
          "axis": null
        },
        "y": {
          "field": "y_pos",
          "type": "quantitative",
          "scale": {"domain": [400, 0]},
          "axis": null
        }
      }
    },
    {
      "transform": [
        {"calculate": "seconds(time)", "as": "seconds"},
        {
          "filter": "indexof([1,2,3,4,5,11,12,13,14,15,21,22,23,24,25,31,32,33,34,35,41,42,43,44,45,51,52,53,54,55], datum.seconds) === -1"
        },
        {"calculate": "((milliseconds(time)/1000)*1000)", "as": "sec"},
        {
          "calculate": "datum.seconds== 0 ?  0: datum.seconds<=5 ?  5000-((datum.seconds*1000)-0): datum.seconds<=10 ? 5000-((datum.seconds*1000)-5000): datum.seconds<=15 ? 5000-((datum.seconds*1000)-10000): datum.seconds<=20 ? 5000-((datum.seconds*1000)-15000): datum.seconds<=25 ? 5000-((datum.seconds*1000)-20000): datum.seconds<=30 ? 5000-((datum.seconds*1000)-25000): datum.seconds<=35 ? 5000-((datum.seconds*1000)-30000): datum.seconds<=40 ? 5000-((datum.seconds*1000)-35000): datum.seconds<=45 ? 5000-((datum.seconds*1000)-40000): datum.seconds<=50 ? 5000-((datum.seconds*1000)-45000): datum.seconds<=55 ? 5000-((datum.seconds*1000)-50000): datum.seconds<=60 ? 5000-((datum.seconds*1000)-55000): 60000",
          "as": "sizeReset"
        },
        {"calculate": "(4000-(datum.sizeReset-datum.sec))", "as": "sizeFactor"},
        {"calculate": "0.3", "as": "Opacity"},
        {"calculate": "(datum.sizeFactor)/100", "as": "movement"},
        {"calculate": "datum.id*(20)", "as": "angle"},
        {
          "calculate": "(200 - cos(datum.angle + (PI/2)) * (datum.movement))",
          "as": "x_pos"
        },
        {
          "calculate": "(200 - cos(datum.angle) * (datum.movement))",
          "as": "y_pos"
        }
      ],
      "mark": {
        "type": "circle",
        "size": {"expr": "datum.sizeFactor"},
        "opacity": {"expr": "datum.Opacity"}
      },
      "encoding": {
        "x": {
          "field": "x_pos",
          "type": "quantitative",
          "scale": {"domain": [400, 0]},
          "axis": null
        },
        "y": {
          "field": "y_pos",
          "type": "quantitative",
          "scale": {"domain": [400, 0]},
          "axis": null
        }
      }
    }
  ],
  "config": {"view": {"stroke": "transparent"}, "background": "transparent"}
}

Link:
https://vega.github.io/editor/#/url/vega-lite/N4IgJAzgxgFgpgWwIYgFwhgF0wBwqgegIDc4BzJAOjIEtMYBXAI0poHsDp5kTykBaADZ04JAKyUAVhDYA7EABoQAdxoATemgAsABh1L4NMlm16lOJACckCCGgDaoWTbhoQmGgldK5D0HFJZTDtUUEwATxxXdA8vS0V3GEs2bEFogCYdAF8lBhw1JExokFk2ZQAKAEoQLIBdOqVMa1kIADM2SwQ-EEk2GmcyMks+Iu62HDdkAA8E1po4QTU3VQ0YBKQQkGmAdXVNOpzQKCRBKAZBQuKARj11zbUFpHCahSOTs4vR9Ag4KDk1CDlWJwapKDZuH5-WQAmq1JQFTAoUIgYgnBhwEKOEDqNBXJQrTSoTLZV7YpZE-F7NZEvSHMloADMlNWaGJdJxqC0zMJbNJHLE3OpvNAHIAbILWbS+eSAOwSmkkkXkgAc8uF9NQAE41VKlbj9CoqZLFRqrnjDSyFQclBdwnB4qgsU0kC12p1usdTudLhDfv9AcDQSBwd8-dC7A0tlYANZoMKRYpQGiWKBpBIQGgAL2iDJuZhAf0EHTjICGSDU8yCbmsFZO6cw40xoDYrVaP0waANheL6AAxK3Wi9m6327ilN2HSBe0gGUwoGoltaQOMkEmIp3KFcciA4LI-hXZGQSzNkXMFuSo1NdiywVMaCFZOdBNvnqf5otJkgr0bb-e0I-BGfLI6WdV0Oi6R03i9T5ikhf0gU8EE7l9KEYTpOZBCKSd+geKYW3Kew8XSBQmS5AUzQUK5iKuJkri5K4BXSIjiPSJl0i5dIBVzEjiIZJkGS5BkBS0PEtGIrRSK5LQBTEPExGIsQmTEMixDhAACBEGAQSg4PDSo1IAQgAXiMtT+CuIcC3eb0vhAcpygQGhAPvMMAQQrxKgIPMdEqAAqbygxDEBIUsz0Ph9dBNO03SARMtSdDUgB+NT4tQDTCi0nTXIgAAeIyxCSlKxD0HR+HsqKstQiB-JKyp+B8tKKpi3KjJuQrir0Mryia7Kar0OqOoa9LMEy5q8oY9qSq6nqqr6nzzJKoaZv9PLMkmzryoy6LeoC8zBsqRqtsqlajM49bSs2kbttm3biX6w6ruO8M8oZBLksG6ajuaua6s42qHtG7KXoK96psuwGbtq-hXv+4aIZO3Rzs+x7vt2oTYeW56jOkpHweu-0fv4XQMa+oH8retSPrxp6AUJ6SSZRsmxBBymwe60nIf6-hBqWjmTtFCmqfZxnOfm5nYYFkrkO+LM4AAJQxOAOzpMKbOKHRKC0LryipprZYV9t+BmypPP2gh0kCzYVzXcIADFVwbeJIwRJFQFRQR0SbU14QxKBJwAUSmGATlcJdkEsWNkQiKI3CKKYO3hV9zMyJR2iCABlWXJXHNgi0nXt0mVUUkCYQcfAsG2SzgKYcEnCrrboO2HeLYClF3fd+iPKPq47N9zzcB5oCd1uwmaNpwI9ayYJQ+DA2l4LsssjCsLcHDq-wwiFF4hQyMovEqMo2j6MY5it7YjiuLxBleP4wThNE8TJJ3mS5IUpSVPUzGAX0kzTPM0Kp4RTsg5JywhmruRBF5WqP154hRVoA2yX8IBxQpqlOG+MsYs1ZhtYW8NwyE15iLE6bVQY4KQYTfaAMMEAnGizIW5DdreUIXgmhp1BZswYVDBiDMWEtTOqQi6uDqHVVuotA66CaYtVerjIRkjCZ-XuhIsaRkhIyM4VzGGiikF5URgI5GvDCboy0XzLGOM9HU1RlDYmxiiGYPYWQkxtNdr0xsbwvKzM1GOJEVDHm4jtFGQFp42xTifGULUpLW4YJNgZmzAbJWADoJAI1lreyOswZ61iYrTARsOYmwIGbC288G4RHtlAR2sJ4SFFdiiNEGJugcnNIPP2bgACSshg5pjDjGEs0dihxwTiANQScqIGjTpgTO2Zs4Flzj2Kchdi6lwSMU18-ga51yOss0p5TW47j3GwA8Xcwg9xLGeD8kVfbDxAmPN0EEsSq2nqGKqEDLYz3DEvJyK90Brzwq0AiRESI7wUORfe1Ej6URPlvFi58t6Xx4iRW+JF7470fjvKSL8gVvyBR-BQSjsr6WMn-Cy8DEm2Xso5Zy4DAxQP6jAqJKEEnhUQV4lBhU0H+KwfQrxBC-FePGvYwR6ixY8OEbQoJBjGFiKoZI1a-L9HCMJtw1xIrTp0I4Vy0RwrpUqNlRYnaUMFHMOVao8xsjLEaMlbiqqOidWmr1RosJ-izHYIFeqqxFr2U2sFXVFxhqtUeJNV67m7reUBM9a6rm4tFERJ0LA-WWSGVqzcFTaxZUMnyyyTkkWJtY3Zi2S3Uk9ykmUAZPPAA8hXRuCaHl2TTXmywnk8zzwQGwUgXgqzEsZcUCq6hfLlEyC89ALoyCdILQg4ofa9BmTUn8QEFUh1pDUgAajUuUAACi082Js1K+RXRVZtrbdyYGzXS9AUwAD6OA2ARlHSS8dxIp0ztkfOkE27d1HX3YgQ9x7gybHCBeq9FSowRx6QmNwSYUxpiUDE6Iqza4Dy+rLOtLxlwVvXMiaucHIpHXLauStOz277M7seE574LznsvXYRooH0AAEcGAug8IiDwpB0yehg4MtgyB+gOGJgoHQ9RfwPifC+Ej-d0B-oowkXpbg6MMboIUGgLGoNsZLGoTjSBuOOl4-x7cX4-yoAAkBK5Lpx7ukglZW9ry3JzxPQvKq7zML2lXtCdevzN7b13hRA+NFKLHy3qfViW8L4kSvjfEid8d4Px3k-aSQLX5AvfkC1SOKkE-ziv-Dtib0BktAS5J5VKAq0p-fSzL1akEsuSmykNHK1XBO8VzX1yiSHOrlXI3aDqQ0TQDeG+aTCeV1ZlWK+VjCOsDZVUNtr+rg1jekd1ur8jRtuJUaqhx820bTaW7olrurRZ1SMY1smTrOVrbdZq5RxUJtmvmj6-rS3-XbdtbtoNZ2yaBLm+K0JEtFo5vTaOUrQDygptNXGw2xsB3BUQ83J2N7O1uA1iW2zOHK7-dJbWqHDbInFfQB+ttysYdZcGUdHtE7wfPqrQD+9-Bp1XqfYeBdy610botvpHdsicdfvB+RgDKO72Tqp4+uddOX2s73S2z9QRv1BQk9zuEQHI7xhjugcDqZvAQ8mehtZ8GUaQ7KfmlDuG0OwfWY9JHeHtwEYOcRvuZyQBc8o+4ajIBZNBHk8x1X0AQ6qfU5p+w2mBPBjvEJwCInrcXml-b6TtH6Mu6Y4p93KnkRqa4-ILTZgdOCf-MJ4CsuoRzEOSieYygSwQCaGwaMfSx4WGGO2pQTBVzRiGGwBg0JY6V6sIemoWQgA

Enjoy.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions