1/** 2 * CountdownSyncClock 3 * 4 * ã«ã¦ã³ããã¦ã³ç³»ã®è¡¨ç¤ºããç§å¢çã§åæãã¦è¦ãããããã«ããããã®åä¸ã¯ããã¯ã 5 * setInterval / requestAnimationFrame ãè¦ç´ ãã¨ã«å¼µãã¨ä½ç¸ããã©ã¤ããCPU ãç¡é§ã«ä½¿ãããã 6 * ãµã¤ãå ¨ä½ã§å ±æããã¹ã±ã¸ã¥ã¼ã© 3 種ãæä¾ããã 7 * 8 * Usage: 9 * var fn = window.CountdownSyncClock.subscribe(function (nowMs) { ... }); // 200ms å¨æï¼æ¢åï¼ 10 * window.CountdownSyncClock.subscribeSecond(function (nowMs) { ... }); // 1 ç§å¨æï¼ç§å¢ç +15msï¼ 11 * window.CountdownSyncClock.subscribeFrame(function (nowMs) { ... }); // æ¯ãã¬ã¼ã ï¼å ±æ rAF 1 æ¬ï¼ 12 * window.CountdownSyncClock.unsubscribe(fn); // ã©ã®ç¨®é¡ãããå¤ã 13 * 14 * å¥ç´ï¼spec: docs/features/mobile-perf-js-loops/spec.md §6.2ï¼: 15 * - subscribe / subscribeSecond ã¯è³¼èªæã«å³ 1 å fn(Date.now()) ãå¼ã³ãfn ãè¿ãã 16 * subscribeFrame ã¯å³æå¼ã³åºããããªãï¼æ¬¡ã®ãã¬ã¼ã ã§å¼ã¶ï¼ 17 * - åã fn ãåã種é¡ã«äºéè³¼èªããªãï¼æ¢ã«è³¼èªæ¸ã¿ãªãä½ãããªãï¼ 18 * - å tick ã¯è³¼èªè é åã®ã³ãã¼ãåããtick ä¸ã»å³æå¼ã³åºãä¸ã® unsubscribe / 種é¡ã®åæ¿ã¯å®å ¨ 19 * - è³¼èªè ã 0 ã®ç¨®é¡ã¯ã¿ã¤ãã¼ / rAF ãå¼µããªã 20 * - é表示ã¿ãï¼document.hiddenï¼ã§ã¯ 3 種ã¨ãæ¢ããè¡¨ç¤ºã«æ»ã£ããï¼pageshow ã® persisted 復帰ãå«ãï¼ 21 * å ¨è³¼èªè ã 1 å峿ã«å¼ãã§ããåéãããé表示ã®ã¾ã¾èªã¿è¾¼ã¾ãããã¼ã¸ã¯è¡¨ç¤ºãããã¾ã§å¼µããªã 22 * - éå§ / åéã¯åªçï¼æ¢ã«å¼µããã¦ããã°ä½ãããªãï¼ 23 * - è³¼èªè ã®ä¾å¤ã¯æ¡ãã¤ã¶ãï¼1 人ã®ä¾å¤ã§ä»ãæ¢ããªãï¼ 24 * 25 * 注æ: 26 * - subscriber ã¯æ° ms 以å ã§æ¸ã軽ãå¦çã«ä¿ã¤ï¼éãå¦çã¯ä»ã® subscriber ãé å»¶ãããï¼ 27 * - è³¼èªè ã¯çµ¶å¯¾æå»ããæ¯ååè¨ç®ããä½ãã«ãããã¨ï¼é表示ä¸ã¯å¼ã°ããªãï¼ 28 */ 29(function () { 30 if (window.CountdownSyncClock) return; 31 32 var TICK_MS = 200; // 5Hz 33 var SECOND_SLACK_MS = 15; // ç§å¢çã®ç´å¾ã«çºç«ãããããã®ä½è£ 34 35 var tickSubs = []; 36 var secondSubs = []; 37 var frameSubs = []; 38 39 var intervalId = null; 40 var secondTimerId = null; 41 var secondExpected = null; // 次ã«å¼ã¶ã¹ãç§ï¼Math.floor(ms / 1000)ï¼ 42 var rafId = null; 43 44 var raf = typeof window.requestAnimationFrame === 'function' 45 ? function (cb) { return window.requestAnimationFrame(cb); } 46 : function (cb) { return setTimeout(cb, 16); }; 47 var cancelRaf = typeof window.cancelAnimationFrame === 'function' 48 ? function (id) { window.cancelAnimationFrame(id); } 49 : function (id) { clearTimeout(id); }; 50 51 function isHidden() { 52 return document.hidden === true; 53 } 54 55 function isSubscribed(fn) { 56 return tickSubs.indexOf(fn) !== -1 57 || secondSubs.indexOf(fn) !== -1 58 || frameSubs.indexOf(fn) !== -1; 59 } 60 61 function safeCall(fn, now) { 62 try { fn(now); } catch (e) { /* swallow */ } 63 } 64 65 // è³¼èªè é åã®ã³ãã¼ãåããéä¸ã§å¤ããè³¼èªè ã¯å¼ã°ãªãï¼å¤ãã人ã®ããã§ä»ãé£ã°ããªãï¼ 66 function callList(list, now) { 67 var copy = list.slice(); 68 for (var i = 0; i < copy.length; i++) { 69 if (list.indexOf(copy[i]) === -1) continue; 70 safeCall(copy[i], now); 71 } 72 } 73 74 // ---- 200ms ---- 75 function tick() { 76 callList(tickSubs, Date.now()); 77 } 78 79 function ensureTick() { 80 if (intervalId !== null || tickSubs.length === 0 || isHidden()) return; 81 intervalId = setInterval(tick, TICK_MS); 82 } 83 84 function stopTick() { 85 if (intervalId === null) return; 86 clearInterval(intervalId); 87 intervalId = null; 88 } 89 90 // ---- 1 ç§ï¼ç§å¢ç +15msï¼ ---- 91 function armSecond(now) { 92 secondTimerId = setTimeout(onSecond, 1000 - (now % 1000) + SECOND_SLACK_MS); 93 } 94 95 function ensureSecond() { 96 if (secondTimerId !== null || secondSubs.length === 0 || isHidden()) return; 97 var now = Date.now(); 98 secondExpected = Math.floor(now / 1000) + 1; 99 armSecond(now); 100 } 101 102 function onSecond() { 103 secondTimerId = null; 104 var now = Date.now(); 105 var sec = Math.floor(now / 1000); 106 try { 107 if (secondExpected !== null && sec < secondExpected && secondExpected - sec <= 1) { 108 // å¢çããåã«æ©ãçºç«ãã: è³¼èªè ãå¼ã°ãã«æ¬¡ã®å¢çã¸å¼µãç´ãï¼ç§ã®éè¤ãé²ãï¼ 109 // å·®ã 1 ç§ãè¶ ãããªã端æ«ã®æè¨ãéå»ã¸æ»ã£ãï¼æå夿´ã»NTP è£æ£ï¼ã®ã§ã
110 // æ©çºç«ã¨ã¯ã¿ãªããè³¼èªè ãå¼ã³ãfinally ã§åºæºãåãç´ãï¼æ»ã£ãç§æ°ã¶ãæ¢ã¾ããªãï¼ 111 if (secondSubs.length > 0 && !isHidden()) armSecond(now); 112 return; 113 } 114 callList(secondSubs, now); 115 } finally { 116 // å¼µãç´ãã¯è³¼èªè ã®ä¾å¤ã«é¢ãããå¿ ãè¡ãï¼æ¬¡ã® tick ã失ããªãï¼ 117 if (secondTimerId === null && secondSubs.length > 0 && !isHidden()) { 118 var after = Date.now(); 119 secondExpected = Math.floor(after / 1000) + 1; 120 armSecond(after); 121 } 122 } 123 } 124 125 function stopSecond() { 126 if (secondTimerId === null) return; 127 clearTimeout(secondTimerId); 128 secondTimerId = null; 129 } 130 131 // ---- æ¯ãã¬ã¼ã ---- 132 function onFrame() { 133 rafId = null; 134 try { 135 callList(frameSubs, Date.now()); 136 } finally { 137 ensureFrame(); 138 } 139 } 140 141 function ensureFrame() { 142 if (rafId !== null || frameSubs.length === 0 || isHidden()) return; 143 rafId = raf(onFrame); 144 } 145 146 function stopFrame() { 147 if (rafId === null) return; 148 cancelRaf(rafId); 149 rafId = null; 150 } 151 152 function ensureAll() { 153 ensureTick(); 154 ensureSecond(); 155 ensureFrame(); 156 } 157 158 function stopAll() { 159 stopTick(); 160 stopSecond(); 161 stopFrame(); 162 } 163 164 // 表示復帰: å ¨è³¼èªè ã 1 å峿ã«å¼ãã§ããåéãã 165 function resume() { 166 if (isHidden()) return; 167 var all = []; 168 var lists = [tickSubs, secondSubs, frameSubs]; 169 for (var l = 0; l < lists.length; l++) { 170 for (var i = 0; i < lists[l].length; i++) { 171 if (all.indexOf(lists[l][i]) === -1) all.push(lists[l][i]); 172 } 173 } 174 var now = Date.now(); 175 for (var j = 0; j < all.length; j++) { 176 if (!isSubscribed(all[j])) continue; 177 safeCall(all[j], now); 178 } 179 ensureAll(); 180 } 181 182 function add(list, fn) { 183 if (list.indexOf(fn) !== -1) return false; 184 list.push(fn); 185 return true; 186 } 187 188 function remove(list, fn) { 189 var i = list.indexOf(fn); 190 if (i > -1) list.splice(i, 1); 191 } 192 193 window.CountdownSyncClock = { 194 subscribe: function (fn) { 195 if (typeof fn !== 'function') return null; 196 if (!add(tickSubs, fn)) return fn; 197 safeCall(fn, Date.now()); 198 ensureTick(); 199 return fn; 200 }, 201 subscribeSecond: function (fn) { 202 if (typeof fn !== 'function') return null; 203 if (!add(secondSubs, fn)) return fn; 204 safeCall(fn, Date.now()); 205 ensureSecond(); 206 return fn; 207 }, 208 subscribeFrame: function (fn) { 209 if (typeof fn !== 'function') return null; 210 if (!add(frameSubs, fn)) return fn; 211 ensureFrame(); 212 return fn; 213 }, 214 unsubscribe: function (fn) { 215 remove(tickSubs, fn); 216 remove(secondSubs, fn); 217 remove(frameSubs, fn); 218 if (tickSubs.length === 0) stopTick(); 219 if (secondSubs.length === 0) stopSecond(); 220 if (frameSubs.length === 0) stopFrame(); 221 } 222 }; 223 224 document.addEventListener('visibilitychange', function () { 225 if (isHidden()) { 226 stopAll(); 227 } else { 228 resume(); 229 } 230 }); 231 232 window.addEventListener('pageshow', function (e) { 233 if (e && e.persisted) resume(); 234 }); 235})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.