/ progress_indeterminate.html
progress_indeterminate.html
  1  <!DOCTYPE html>
  2  <html lang="en">
  3  <head>
  4      <meta charset="utf-8">
  5      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  6  
  7      <title>Indeterminate Progress — Space.js</title>
  8  
  9      <link rel="preconnect" href="https://fonts.gstatic.com">
 10      <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono">
 11      <link rel="stylesheet" href="assets/css/style.css">
 12  
 13      <script type="module">
 14          import { Interface, clearTween, delayedCall, ticker, tween } from './src/index.js';
 15  
 16          class ProgressIndeterminate extends Interface {
 17              constructor() {
 18                  super(null, 'svg');
 19  
 20                  const size = 90;
 21  
 22                  this.width = size;
 23                  this.height = size;
 24                  this.x = size / 2;
 25                  this.y = size / 2;
 26                  this.radius = size * 0.4;
 27                  this.startOffset = -0.25;
 28                  this.animatedIn = false;
 29                  this.needsUpdate = false;
 30  
 31                  this.initSVG();
 32              }
 33  
 34              initSVG() {
 35                  this.attr({
 36                      width: this.width,
 37                      height: this.height
 38                  });
 39  
 40                  this.circle = new Interface(null, 'svg', 'circle');
 41                  this.circle.attr({
 42                      cx: this.x,
 43                      cy: this.y,
 44                      r: this.radius
 45                  });
 46                  this.circle.css({
 47                      fill: 'none',
 48                      stroke: 'var(--ui-color)',
 49                      strokeWidth: 1.5
 50                  });
 51                  this.circle.start = 0;
 52                  this.circle.offset = this.startOffset;
 53                  this.circle.progress = 0;
 54                  this.add(this.circle);
 55              }
 56  
 57              addListeners() {
 58                  ticker.add(this.onUpdate);
 59              }
 60  
 61              removeListeners() {
 62                  ticker.remove(this.onUpdate);
 63              }
 64  
 65              // Event handlers
 66  
 67              onUpdate = () => {
 68                  if (this.needsUpdate) {
 69                      this.update();
 70                  }
 71              };
 72  
 73              // Public methods
 74  
 75              update = () => {
 76                  this.circle.drawLine();
 77              };
 78  
 79              animateIn = () => {
 80                  this.animatedIn = true;
 81                  this.needsUpdate = true;
 82  
 83                  this.addListeners();
 84  
 85                  const start = () => {
 86                      tween(this.circle, { progress: 1 }, 1000, 'easeOutCubic', () => {
 87                          tween(this.circle, { start: 1 }, 1000, 'easeInOutCubic', () => {
 88                              this.circle.start = 0;
 89  
 90                              delayedCall(500, () => {
 91                                  if (this.animatedIn) {
 92                                      start();
 93                                  } else {
 94                                      this.removeListeners();
 95                                      this.needsUpdate = false;
 96                                  }
 97                              });
 98                          }, () => {
 99                              this.circle.progress = 1 - this.circle.start;
100                          });
101                      });
102                  };
103  
104                  start();
105              };
106  
107              animateOut = () => {
108                  this.animatedIn = false;
109              };
110  
111              destroy = () => {
112                  this.removeListeners();
113  
114                  clearTween(this.circle);
115  
116                  return super.destroy();
117              };
118          }
119  
120          class App {
121              static async init() {
122                  this.initView();
123  
124                  this.addListeners();
125              }
126  
127              static initView() {
128                  this.view = new ProgressIndeterminate();
129                  this.view.css({
130                      position: 'absolute',
131                      left: '50%',
132                      top: '50%',
133                      marginLeft: -this.view.width / 2,
134                      marginTop: -this.view.height / 2,
135                      cursor: 'pointer'
136                  });
137                  document.body.appendChild(this.view.element);
138  
139                  this.view.animateIn();
140              }
141  
142              static addListeners() {
143                  this.view.element.addEventListener('click', this.onClick);
144                  ticker.start();
145              }
146  
147              // Event handlers
148  
149              static onClick = () => {
150                  if (this.view.needsUpdate) {
151                      this.view.animateOut();
152                  } else {
153                      this.view.animateIn();
154                  }
155              };
156          }
157  
158          App.init();
159      </script>
160  </head>
161  <body>
162  </body>
163  </html>