/ 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>