From a777a83a9443b8c95afb4c696ee6b1b638c39ec1 Mon Sep 17 00:00:00 2001 From: Tom Granger Date: Wed, 16 Aug 2017 21:04:19 +0200 Subject: [PATCH 1/4] Update battery demo --- demos/battery/css/style.css | 33 +++- demos/battery/index.html | 12 +- demos/battery/js/index.js | 322 +++++++++++++++++++++--------------- 3 files changed, 220 insertions(+), 147 deletions(-) diff --git a/demos/battery/css/style.css b/demos/battery/css/style.css index e5ea80d..dbcbb36 100644 --- a/demos/battery/css/style.css +++ b/demos/battery/css/style.css @@ -1,14 +1,25 @@ -#debug { - margin-top: 24px; +body { + background: #1d1f20; +} + +a { + color: #2196f3; +} + +table { + margin: 24px 24px 0 0; padding: 12px; line-height: 24px; - width: auto; + min-width: 290px; color: white; background-color: rgba(0,0,0,.5); + border-radius: 2px; } td:nth-child(2) { padding-left: 12px; + min-width: 110px; + text-align: right; } .ui { @@ -17,12 +28,12 @@ td:nth-child(2) { left: 24px; } -#start { +#btn { height: 54px; padding: 0 24px; border: none; - background-color: #424242; - color: #FFC107; + background-color: white; + color: #ff8507; text-transform: uppercase; border-radius: 2px; box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2); @@ -31,4 +42,12 @@ td:nth-child(2) { .paper-gui-0 #fab.paper-gui { top: 24px; right: 24px; -} \ No newline at end of file +} + +.copyme { + position: absolute; + top: 0; + left: 0; + transform: scale(0); + opacity: 0; +} diff --git a/demos/battery/index.html b/demos/battery/index.html index ca8332a..ad76795 100644 --- a/demos/battery/index.html +++ b/demos/battery/index.html @@ -11,11 +11,17 @@ - +
- -
+ + + +
+ Hello there, welcome to this battery performance test. Battery API is required for this test and might not be around for very long. +

Press Start to run the test, or use the orange buttton to fiddle with the parameters. To reduce noise, make sure to test over a large-enough battery drain threshold (default is 5%).

+
+ diff --git a/demos/battery/js/index.js b/demos/battery/js/index.js index a3ec82c..a28fb23 100644 --- a/demos/battery/js/index.js +++ b/demos/battery/js/index.js @@ -1,200 +1,248 @@ -// List of test images. +// List of preset test images. CORS must be enabled for these resources. var images = { - 'Starry Night': 'https://lh6.ggpht.com/HlgucZ0ylJAfZgusynnUwxNIgIp5htNhShF559x3dRXiuy_UdP3UQVLYW6c', - 'T-Rex': 'https://lh4.ggpht.com/qCW18EYlcmqi-NIHuJ5VtwR1Cf8UlNzP-D0aqkPKG2nLzug3j8WnUtD2bwJ7', - 'The Kiss': 'https://lh4.ggpht.com/UuYCUnqvo2EIZhyFHYFVLbkmma_cubVk7SwxOF3lklT6aor5647BXVhEaFB7jg', - 'Nemo': 'https://lh3.googleusercontent.com/hDdgaQfhLXH7R8yaaHqcpvja5halx6LFzSc8NU50AqzjWyDZvNuZyOu7_HQBxpgJCw' + 'WebP 600 x 800 px': 'https://www.gstatic.com/webp/measurement/example1.webp', + 'WebP 800 x 533 px': 'https://www.gstatic.com/webp/measurement/example2.webp', + 'WebP 1200 x 800 px': 'https://www.gstatic.com/webp/measurement/example3.webp', + 'JPEG 600 x 800 px': 'https://www.gstatic.com/webp/measurement/example1.jpg', + 'JPEG 800 x 533 px': 'https://www.gstatic.com/webp/measurement/example2.jpg', + 'JPEG 1200 x 800 px': 'https://www.gstatic.com/webp/measurement/example3.jpg', }; -// Experiment configuration params. -var params = {}; -params.debug = true; -params.webp = true; -params.size = 400; -params.target = images['Starry Night']; -params.batterydrop = 5; +// Experiment configuration parameters. +var params = { + showInfo: true, + presetUrl: images['WebP 600 x 800 px'], + customUrl: '', + batteryDrop: 5 +}; -// Public vars. +// Public variables. var counter = 0; -var stats, timer, initialBatt; -var hud = {}; -var lock = false; var running = false; -var once = false; -var debugEl = document.getElementById('debug'); -var containerEl = document.getElementById('container'); - -var avg = function(arr) { - return arr.reduce(function(a, b) { - return a + b; - }) / arr.length; +var timings, fetchPromise, once; +var stats = {}; +var infoEl = document.getElementById('info'); +var imageEl = document.getElementById('image'); + +// Clears the stats object and fetch counter. +function resetStats() { + stats = {}; + counter = 0; + imageEl.src = ''; + updateInfo(); }; -var updateHud = function() { - if (!params.debug) { - debugEl.style.display = 'none'; +// Renders the stats object as an HTML table. +function updateInfo() { + if (!params.showInfo) { + infoEl.style.display = 'none'; return; } - hud['Image format'] = params.webp ? 'WebP' : 'JPEG'; - hud['Fetch count'] = counter; - if (stats.batt.length) { - hud['Images/batt.% (avg)'] = stats.batt[stats.batt.length - 1] + ' (' + Math.round(avg(stats.batt)) + ')'; + var tableData = {}; + + if (stats.currentBatt && stats.initialBatt) { + tableData['Battery level (at start)'] = Math.floor(stats.currentBatt * 100) + + '% (' + Math.floor(stats.initialBatt * 100) + '%)'; } - if (stats.fetch.length) { - hud['Fetch time (avg)'] = stats.fetch[stats.fetch.length - 1] + ' ms (' + Math.round(avg(stats.fetch)) + ' ms)'; + if (stats.fileSize && stats.mime) { + tableData['File info'] = stats.mime + ', ' + + Math.round(stats.fileSize / 1000) + ' Kb'; } - if (stats.read.length) { - hud['Decode time (avg)'] = stats.read[stats.read.length - 1] + ' ms (' + Math.round(avg(stats.read)) + ' ms)'; + tableData['Fetch count'] = counter; + if (stats.batt && stats.batt.length) { + tableData['Images/batt.% (avg)'] = stats.batt[stats.batt.length - 1] + ' (' + + Math.round(average(stats.batt)) + ')'; } - if (stats.dom.length) { - hud['DOM time (avg)'] = stats.dom[stats.dom.length - 1] + ' ms (' + Math.round(avg(stats.dom)) + ' ms)'; + if (stats.fetch && stats.fetch.length) { + tableData['Fetch time (avg)'] = stats.fetch[stats.fetch.length - 1] + + ' ms (' + Math.round(average(stats.fetch)) + ' ms)'; } - - debugEl.style.display = ''; - var data = ''; - Object.keys(hud).forEach(function(key, index) { - data += '' + key + '' + hud[key] + ''; - }); - debugEl.innerHTML = data; -} - -var updateBattery = function(level) { - if (running && !!initialBatt) { - if (stats.batt.length > 0) { - stats.batt.push( - counter - stats.batt.reduce(function(a, b) { - return a + b; - })); - } else { - stats.batt.push(counter); - } - if ((initialBatt - level) >= params.batterydrop / 100) { - stop(); - } + if (stats.load && stats.load.length) { + tableData['Load time (avg)'] = stats.load[stats.load.length - 1] + ' ms (' + + Math.round(average(stats.load)) + ' ms)'; } - hud['Battery level'] = Math.floor(level * 100); - updateHud(); -} -params.reset = function() { - stats = { - fetch: [], - read: [], - dom: [], - batt: [] - }; - counter = 0; - hud = { - 'Battery level': hud['Battery level'] - }; - updateHud(); -}; - - -var storeTimings = function(timings) { - stats.fetch.push(timings.ready - timings.start); - stats.read.push(timings.readEnd - timings.ready); - stats.dom.push(timings.domEnd - timings.readEnd); + var html = ''; + Object.keys(tableData).forEach(function(key) { + html += '' + key + '' + tableData[key] + ''; + }); + window.requestAnimationFrame(function() { + infoEl.innerHTML = html; + infoEl.style.display = ''; + }); } -var fetch = function(url) { - if (lock) { +// Downloads an image by URL via fetch API, storing some stats in the process, +// and renders it to the DOM via createObjectURL. +var doFetch = function(url) { + if (fetchPromise) { return; } - lock = true; - timer = {}; - - var reader = new FileReader(); - reader.onloadend = function() { - timer.readEnd = Date.now(); - containerEl.src = reader.result; + timings = { + fetchStart: Date.now() }; + fetchPromise = fetch(url) + .then(function(response) { + timings.fetchEnd = Date.now(); + return response.blob(); + }, function(e) { + alert('Unable to fetch the image. Either the URL is incorrect' + + ' or the file has no Access-Control-Allow-Origin header.'); + stop(); + fetchPromise = null; + }) + .then(function(imageBlob) { + fetchPromise = null; + if (!imageBlob || imageBlob.type.indexOf('image/') < 0) { + alert('Woops, fetched file is not an image!'); + stop(); + return; + } + stats.mime = imageBlob.type.replace('image/', ''); + stats.fileSize = imageBlob.size; + URL.revokeObjectURL(imageEl.src); + var objectURL = URL.createObjectURL(imageBlob); + timings.loadStart = Date.now(); + imageEl.src = objectURL; + counter++; + }); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url); - xhr.responseType = 'blob'; - xhr.onload = function(e) { - counter++; - if (xhr.readyState == 4) { - timer.ready = Date.now(); - var blob = xhr.response; - reader.readAsDataURL(blob); - } - }; - timer.start = Date.now(); - xhr.send(); }; +// Fetches the next image or shows test results if the loop was interrupted. var fetchNext = function(opt_manual) { once = !!opt_manual; if (!running && !opt_manual) { - alert('Test complete. Images loaded: ' + counter); + alert('Test complete!\n\nImages loaded: ' + counter + + (stats.batt && stats.batt.length ? '\nImages/batt.%: ' + + Math.round(average(stats.batt)) : '\nNo battery stats.')); return; } - var options = '=s' + params.size + (params.webp ? '-rw' : '-rj'); - fetch(params.target + options + '?count=' + counter + '&batt=' + hud['Battery level'] + '&ts=' + Date.now()); + var url = params.customUrl || params.presetUrl; + doFetch(url + '?ts=' + Date.now()); +}; + +// Image element load callback. Tracks timings and triggers the next fetch. +imageEl.onload = function() { + var loadEnd = Date.now(); + if (!stats.fetch) { + stats.fetch = []; + stats.load = []; + } + stats.fetch.push(timings.fetchEnd - timings.fetchStart); + stats.load.push(loadEnd - timings.loadStart); + updateInfo(); + !once && fetchNext(); + once = false; }; -var stop = function() { +// Interrupts the fetch loop. +function stop() { running = false; - document.getElementById('start').innerText = 'Start'; + document.getElementById('btn').innerText = 'Start'; } -var startStop = function() { +// Callback for the start/stop button. Runs or interrupts the fetch loop. +function startStop() { if (running) { stop(); return; } var start = function() { - document.getElementById('start').innerText = 'Stop'; + document.getElementById('btn').innerText = 'Stop'; running = true; - params.reset(); fetchNext(); } - initialBatt = null; if (navigator.getBattery) { navigator.getBattery().then(function(battery) { - initialBatt = battery.level; + stats.initialBatt = stats.currentBatt = battery.level; if (battery.charging) { - alert('Device is currently charging, the test will likely run until you stop it manually.'); - start(); + alert('Device is currently charging, the test will likely run until' + + ' you stop it manually.'); } start(); }); } else { - alert('Device doesn\'t support battery API, you\'ll have to manually stop the test.'); + alert('Device doesn\'t support battery API, you\'ll have to manually stop' + + ' the test.'); start(); } }; -// Init stuff. -params.reset(); -containerEl.onload = function() { - timer.domEnd = Date.now(); - storeTimings(timer); - lock = false; - updateHud(); - !once && fetchNext(); - once = false; -}; +// Battery level change handler. Logs the fetched images count per dropped %. +function batteryChangeHandler(level) { + if (running && !!stats.initialBatt) { + if (!stats.batt) { + stats.batt = []; + } + if (stats.batt.length > 0) { + stats.batt.push( + counter - stats.batt.reduce(function(a, b) { + return a + b; + })); + } else if (stats.initialBatt - level < 0.02) { + // First battery level log. We skip the first percent drop because + // it was probably not entirely consumed by the test and would skew + // the stats. + stats.firstPercentDrop = counter; + } else { + // First entry in the batt stats (count of images for the first full + // percent drop). + stats.batt.push(counter - stats.firstPercentDrop); + } + if ((stats.initialBatt - level) >= params.batteryDrop / 100) { + stop(); + } + } + stats.currentBatt = level; + updateInfo(); +} -// Monitor battery level changes. +// Monitors battery level changes. if (navigator.getBattery) { navigator.getBattery().then(function(battery) { battery.onlevelchange = function() { - updateBattery(battery.level); + batteryChangeHandler(battery.level); }; - updateBattery(battery.level); }); }; +// Utility function. +function average(arr) { + return arr.reduce((a, b) => a + b) / arr.length; +}; + // Build the GUI. var gui = new PaperGUI(); -gui.add(params, 'debug').name('Show debug').onChange(updateHud); -gui.add(params, 'webp').name('Use WebP').onChange(updateHud); -gui.add(params, 'size').min(100).max(1200).step(100).name('Image size').onChange(updateHud); -gui.add(params, 'batterydrop').min(1).name('Stop test after battery drop (%)'); -gui.add(params, 'target', images).name('Target image'); -gui.add({test: function() { - fetchNext(true); }}, 'test').name('Test image load'); -gui.add(params, 'reset').name('Reset stats'); \ No newline at end of file +gui.add(params, 'showInfo').name('Show stats (uses more battery)') + .onChange(updateInfo); +gui.add(params, 'batteryDrop').min(2).name('Stop test after battery drop (%)'); +gui.add(params, 'presetUrl', images).name('Target image').onChange(stop); +gui.add(params, 'customUrl').name('Custom image URL').onChange(stop); +params.test = function() { + !running && fetchNext(true); +}; +gui.add(params, 'test').name('Test image load'); +params.reset = function() { + resetStats(); +}; +gui.add(params, 'reset').name('Reset stats'); + +// Bonus feature: export stats object to clipboard. +if (document.queryCommandSupported('copy')) { + params.copyStats = function() { + window.getSelection().removeAllRanges(); + var textContainerEl = document.querySelector('.copyme'); + textContainerEl.value = JSON.stringify(stats); + var range = document.createRange(); + range.selectNode(textContainerEl); + window.getSelection().addRange(range); + try { + var successful = document.execCommand('copy'); + alert(successful ? 'Stats have been copied to your clipboard as a JSON string.' : 'Copy command failed!'); + } catch(e) { + alert('Woops. Clouldn\'t copy the data to the clipboard!' + e); + } + }; + gui.add(params, 'copyStats').name('Export stats'); +} From 846846bafe540e0b2444d2875174126003b5631a Mon Sep 17 00:00:00 2001 From: Tom Granger Date: Wed, 16 Aug 2017 21:17:41 +0200 Subject: [PATCH 2/4] Fix typo --- demos/battery/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/battery/index.html b/demos/battery/index.html index ad76795..e77718a 100644 --- a/demos/battery/index.html +++ b/demos/battery/index.html @@ -17,7 +17,7 @@
Hello there, welcome to this battery performance test. Battery API is required for this test and might not be around for very long. -

Press Start to run the test, or use the orange buttton to fiddle with the parameters. To reduce noise, make sure to test over a large-enough battery drain threshold (default is 5%).

+

Press Start to run the test, or use the orange button to fiddle with the parameters. To reduce noise, make sure to test over a large-enough battery drain threshold (default is 5%).

From 5d06459a99e915f34333511c220d064729e805e2 Mon Sep 17 00:00:00 2001 From: Tom Granger Date: Thu, 17 Aug 2017 15:03:12 +0200 Subject: [PATCH 3/4] Revert back to JS polyfill to restore Firefox compatibility, also rename the options in the image selector dropdown --- demos/battery/index.html | 1 - demos/battery/js/index.js | 92 ++++++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/demos/battery/index.html b/demos/battery/index.html index e77718a..0fc350f 100644 --- a/demos/battery/index.html +++ b/demos/battery/index.html @@ -4,7 +4,6 @@ WebP Battery test experiment - diff --git a/demos/battery/js/index.js b/demos/battery/js/index.js index a28fb23..8b91253 100644 --- a/demos/battery/js/index.js +++ b/demos/battery/js/index.js @@ -1,17 +1,17 @@ // List of preset test images. CORS must be enabled for these resources. -var images = { - 'WebP 600 x 800 px': 'https://www.gstatic.com/webp/measurement/example1.webp', - 'WebP 800 x 533 px': 'https://www.gstatic.com/webp/measurement/example2.webp', - 'WebP 1200 x 800 px': 'https://www.gstatic.com/webp/measurement/example3.webp', - 'JPEG 600 x 800 px': 'https://www.gstatic.com/webp/measurement/example1.jpg', - 'JPEG 800 x 533 px': 'https://www.gstatic.com/webp/measurement/example2.jpg', - 'JPEG 1200 x 800 px': 'https://www.gstatic.com/webp/measurement/example3.jpg', +const images = { + 'Example 1 - WebP (600×800px)': 'https://www.gstatic.com/webp/measurement/example1.webp', + 'Example 1 - JPEG (600×800px)': 'https://www.gstatic.com/webp/measurement/example1.jpg', + 'Example 2 - WebP (800x533px)': 'https://www.gstatic.com/webp/measurement/example2.webp', + 'Example 2 - JPEG (800×533px)': 'https://www.gstatic.com/webp/measurement/example2.jpg', + 'Example 3 - WebP (1200×800px)': 'https://www.gstatic.com/webp/measurement/example3.webp', + 'Example 3 - JPEG (1200×800px)': 'https://www.gstatic.com/webp/measurement/example3.jpg', }; // Experiment configuration parameters. -var params = { +const params = { showInfo: true, - presetUrl: images['WebP 600 x 800 px'], + presetUrl: images['Example 1 - WebP (600×800px)'], customUrl: '', batteryDrop: 5 }; @@ -213,36 +213,46 @@ function average(arr) { }; // Build the GUI. -var gui = new PaperGUI(); -gui.add(params, 'showInfo').name('Show stats (uses more battery)') - .onChange(updateInfo); -gui.add(params, 'batteryDrop').min(2).name('Stop test after battery drop (%)'); -gui.add(params, 'presetUrl', images).name('Target image').onChange(stop); -gui.add(params, 'customUrl').name('Custom image URL').onChange(stop); -params.test = function() { - !running && fetchNext(true); -}; -gui.add(params, 'test').name('Test image load'); -params.reset = function() { - resetStats(); -}; -gui.add(params, 'reset').name('Reset stats'); - -// Bonus feature: export stats object to clipboard. -if (document.queryCommandSupported('copy')) { - params.copyStats = function() { - window.getSelection().removeAllRanges(); - var textContainerEl = document.querySelector('.copyme'); - textContainerEl.value = JSON.stringify(stats); - var range = document.createRange(); - range.selectNode(textContainerEl); - window.getSelection().addRange(range); - try { - var successful = document.execCommand('copy'); - alert(successful ? 'Stats have been copied to your clipboard as a JSON string.' : 'Copy command failed!'); - } catch(e) { - alert('Woops. Clouldn\'t copy the data to the clipboard!' + e); - } +document.addEventListener('PaperGUIReady', function() { + var gui = new PaperGUI(); + gui.add(params, 'showInfo').name('Show stats (uses more battery)') + .onChange(updateInfo); + gui.add(params, 'batteryDrop').min(2) + .name('Stop test after battery drop (%)'); + gui.add(params, 'presetUrl', images).name('Target image').onChange(stop); + gui.add(params, 'customUrl').name('Custom image URL').onChange(stop); + params.test = function() { + !running && fetchNext(true); }; - gui.add(params, 'copyStats').name('Export stats'); -} + gui.add(params, 'test').name('Test image load'); + params.reset = function() { + resetStats(); + }; + gui.add(params, 'reset').name('Reset stats'); + + // Bonus feature: export stats object to clipboard. + if (document.queryCommandSupported('copy')) { + params.copyStats = function() { + window.getSelection().removeAllRanges(); + var textContainerEl = document.querySelector('.copyme'); + textContainerEl.value = JSON.stringify(stats); + var range = document.createRange(); + range.selectNode(textContainerEl); + window.getSelection().addRange(range); + try { + var successful = document.execCommand('copy'); + alert(successful ? + 'Stats have been copied to your clipboard as a JSON string.' : + 'Copy command failed!'); + } catch(e) { + alert('Woops. Clouldn\'t copy the data to the clipboard!' + e); + } + }; + gui.add(params, 'copyStats').name('Export stats'); + } +}); + +var guiScript = document.createElement('script'); +guiScript.src = 'https://google.github.io/paper-gui/dist/paperGUI.js'; +document.body.appendChild(guiScript); + From 268b1ad6b5cf40ff6ba2f2968bb505be235eadb9 Mon Sep 17 00:00:00 2001 From: Tom Granger Date: Thu, 17 Aug 2017 15:42:08 +0200 Subject: [PATCH 4/4] Remove battery demo from master branch (since it's in gh-pages too) and update the readme --- README.md | 5 +- demos/battery/README.md | 7 - demos/battery/css/style.css | 53 -------- demos/battery/index.html | 27 ---- demos/battery/js/index.js | 258 ------------------------------------ 5 files changed, 3 insertions(+), 347 deletions(-) delete mode 100644 demos/battery/README.md delete mode 100644 demos/battery/css/style.css delete mode 100644 demos/battery/index.html delete mode 100644 demos/battery/js/index.js diff --git a/README.md b/README.md index 1d4e16f..66448c8 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,12 @@ image format. Sources: https://github.com/webmproject/libwebp-demo/tree/gh-pages/webp_js - ## Decoding timing test [android_timing_test/](https://github.com/webmproject/libwebp-demo/tree/master/android_webp_test) contains a script to measure the decoding speed on Android devices. ## Battery usage test -[demos/battery/](https://github.com/webmproject/libwebp-demo/tree/master/demos/battery) contains a script to measure the battery consumption when repeatingly decoding an image (WebP, JPEG, ...). +[battery](https://webmproject.github.io/libwebp-demo/battery/index.html) contains a script to measure the battery consumption when repeatingly decoding an image (WebP, JPEG, ...). + +Sources: https://github.com/webmproject/libwebp-demo/tree/gh-pages/battery diff --git a/demos/battery/README.md b/demos/battery/README.md deleted file mode 100644 index 55946c3..0000000 --- a/demos/battery/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# WebP Battery test - -A battery test experiment. This demo uses the [Battery Status API](https://w3c.github.io/battery/) and decodes as many JPEG or WebP images as possible within a given (preset) battery drop. Hence, this test focuses on battery performance rather than speed. - -Use the edit icon on the top right to modify the settings, and start button to run the experiment. - -The UI is built with [PaperGUI](http://google.github.io/paper-gui). diff --git a/demos/battery/css/style.css b/demos/battery/css/style.css deleted file mode 100644 index dbcbb36..0000000 --- a/demos/battery/css/style.css +++ /dev/null @@ -1,53 +0,0 @@ -body { - background: #1d1f20; -} - -a { - color: #2196f3; -} - -table { - margin: 24px 24px 0 0; - padding: 12px; - line-height: 24px; - min-width: 290px; - color: white; - background-color: rgba(0,0,0,.5); - border-radius: 2px; -} - -td:nth-child(2) { - padding-left: 12px; - min-width: 110px; - text-align: right; -} - -.ui { - position: fixed; - top: 24px; - left: 24px; -} - -#btn { - height: 54px; - padding: 0 24px; - border: none; - background-color: white; - color: #ff8507; - text-transform: uppercase; - border-radius: 2px; - box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2); -} - -.paper-gui-0 #fab.paper-gui { - top: 24px; - right: 24px; -} - -.copyme { - position: absolute; - top: 0; - left: 0; - transform: scale(0); - opacity: 0; -} diff --git a/demos/battery/index.html b/demos/battery/index.html deleted file mode 100644 index 0fc350f..0000000 --- a/demos/battery/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - WebP Battery test experiment - - - - - - - -
- - - -
- Hello there, welcome to this battery performance test. Battery API is required for this test and might not be around for very long. -

Press Start to run the test, or use the orange button to fiddle with the parameters. To reduce noise, make sure to test over a large-enough battery drain threshold (default is 5%).

-
-
- - - - - diff --git a/demos/battery/js/index.js b/demos/battery/js/index.js deleted file mode 100644 index 8b91253..0000000 --- a/demos/battery/js/index.js +++ /dev/null @@ -1,258 +0,0 @@ -// List of preset test images. CORS must be enabled for these resources. -const images = { - 'Example 1 - WebP (600×800px)': 'https://www.gstatic.com/webp/measurement/example1.webp', - 'Example 1 - JPEG (600×800px)': 'https://www.gstatic.com/webp/measurement/example1.jpg', - 'Example 2 - WebP (800x533px)': 'https://www.gstatic.com/webp/measurement/example2.webp', - 'Example 2 - JPEG (800×533px)': 'https://www.gstatic.com/webp/measurement/example2.jpg', - 'Example 3 - WebP (1200×800px)': 'https://www.gstatic.com/webp/measurement/example3.webp', - 'Example 3 - JPEG (1200×800px)': 'https://www.gstatic.com/webp/measurement/example3.jpg', -}; - -// Experiment configuration parameters. -const params = { - showInfo: true, - presetUrl: images['Example 1 - WebP (600×800px)'], - customUrl: '', - batteryDrop: 5 -}; - -// Public variables. -var counter = 0; -var running = false; -var timings, fetchPromise, once; -var stats = {}; -var infoEl = document.getElementById('info'); -var imageEl = document.getElementById('image'); - -// Clears the stats object and fetch counter. -function resetStats() { - stats = {}; - counter = 0; - imageEl.src = ''; - updateInfo(); -}; - -// Renders the stats object as an HTML table. -function updateInfo() { - if (!params.showInfo) { - infoEl.style.display = 'none'; - return; - } - var tableData = {}; - - if (stats.currentBatt && stats.initialBatt) { - tableData['Battery level (at start)'] = Math.floor(stats.currentBatt * 100) - + '% (' + Math.floor(stats.initialBatt * 100) + '%)'; - } - if (stats.fileSize && stats.mime) { - tableData['File info'] = stats.mime + ', ' - + Math.round(stats.fileSize / 1000) + ' Kb'; - } - tableData['Fetch count'] = counter; - if (stats.batt && stats.batt.length) { - tableData['Images/batt.% (avg)'] = stats.batt[stats.batt.length - 1] + ' (' - + Math.round(average(stats.batt)) + ')'; - } - if (stats.fetch && stats.fetch.length) { - tableData['Fetch time (avg)'] = stats.fetch[stats.fetch.length - 1] - + ' ms (' + Math.round(average(stats.fetch)) + ' ms)'; - } - if (stats.load && stats.load.length) { - tableData['Load time (avg)'] = stats.load[stats.load.length - 1] + ' ms (' - + Math.round(average(stats.load)) + ' ms)'; - } - - var html = ''; - Object.keys(tableData).forEach(function(key) { - html += '' + key + '' + tableData[key] + ''; - }); - window.requestAnimationFrame(function() { - infoEl.innerHTML = html; - infoEl.style.display = ''; - }); -} - -// Downloads an image by URL via fetch API, storing some stats in the process, -// and renders it to the DOM via createObjectURL. -var doFetch = function(url) { - if (fetchPromise) { - return; - } - timings = { - fetchStart: Date.now() - }; - fetchPromise = fetch(url) - .then(function(response) { - timings.fetchEnd = Date.now(); - return response.blob(); - }, function(e) { - alert('Unable to fetch the image. Either the URL is incorrect' - + ' or the file has no Access-Control-Allow-Origin header.'); - stop(); - fetchPromise = null; - }) - .then(function(imageBlob) { - fetchPromise = null; - if (!imageBlob || imageBlob.type.indexOf('image/') < 0) { - alert('Woops, fetched file is not an image!'); - stop(); - return; - } - stats.mime = imageBlob.type.replace('image/', ''); - stats.fileSize = imageBlob.size; - URL.revokeObjectURL(imageEl.src); - var objectURL = URL.createObjectURL(imageBlob); - timings.loadStart = Date.now(); - imageEl.src = objectURL; - counter++; - }); - -}; - -// Fetches the next image or shows test results if the loop was interrupted. -var fetchNext = function(opt_manual) { - once = !!opt_manual; - if (!running && !opt_manual) { - alert('Test complete!\n\nImages loaded: ' + counter - + (stats.batt && stats.batt.length ? '\nImages/batt.%: ' - + Math.round(average(stats.batt)) : '\nNo battery stats.')); - return; - } - var url = params.customUrl || params.presetUrl; - doFetch(url + '?ts=' + Date.now()); -}; - -// Image element load callback. Tracks timings and triggers the next fetch. -imageEl.onload = function() { - var loadEnd = Date.now(); - if (!stats.fetch) { - stats.fetch = []; - stats.load = []; - } - stats.fetch.push(timings.fetchEnd - timings.fetchStart); - stats.load.push(loadEnd - timings.loadStart); - updateInfo(); - !once && fetchNext(); - once = false; -}; - -// Interrupts the fetch loop. -function stop() { - running = false; - document.getElementById('btn').innerText = 'Start'; -} - -// Callback for the start/stop button. Runs or interrupts the fetch loop. -function startStop() { - if (running) { - stop(); - return; - } - var start = function() { - document.getElementById('btn').innerText = 'Stop'; - running = true; - fetchNext(); - } - if (navigator.getBattery) { - navigator.getBattery().then(function(battery) { - stats.initialBatt = stats.currentBatt = battery.level; - if (battery.charging) { - alert('Device is currently charging, the test will likely run until' - + ' you stop it manually.'); - } - start(); - }); - } else { - alert('Device doesn\'t support battery API, you\'ll have to manually stop' - + ' the test.'); - start(); - } -}; - -// Battery level change handler. Logs the fetched images count per dropped %. -function batteryChangeHandler(level) { - if (running && !!stats.initialBatt) { - if (!stats.batt) { - stats.batt = []; - } - if (stats.batt.length > 0) { - stats.batt.push( - counter - stats.batt.reduce(function(a, b) { - return a + b; - })); - } else if (stats.initialBatt - level < 0.02) { - // First battery level log. We skip the first percent drop because - // it was probably not entirely consumed by the test and would skew - // the stats. - stats.firstPercentDrop = counter; - } else { - // First entry in the batt stats (count of images for the first full - // percent drop). - stats.batt.push(counter - stats.firstPercentDrop); - } - if ((stats.initialBatt - level) >= params.batteryDrop / 100) { - stop(); - } - } - stats.currentBatt = level; - updateInfo(); -} - -// Monitors battery level changes. -if (navigator.getBattery) { - navigator.getBattery().then(function(battery) { - battery.onlevelchange = function() { - batteryChangeHandler(battery.level); - }; - }); -}; - -// Utility function. -function average(arr) { - return arr.reduce((a, b) => a + b) / arr.length; -}; - -// Build the GUI. -document.addEventListener('PaperGUIReady', function() { - var gui = new PaperGUI(); - gui.add(params, 'showInfo').name('Show stats (uses more battery)') - .onChange(updateInfo); - gui.add(params, 'batteryDrop').min(2) - .name('Stop test after battery drop (%)'); - gui.add(params, 'presetUrl', images).name('Target image').onChange(stop); - gui.add(params, 'customUrl').name('Custom image URL').onChange(stop); - params.test = function() { - !running && fetchNext(true); - }; - gui.add(params, 'test').name('Test image load'); - params.reset = function() { - resetStats(); - }; - gui.add(params, 'reset').name('Reset stats'); - - // Bonus feature: export stats object to clipboard. - if (document.queryCommandSupported('copy')) { - params.copyStats = function() { - window.getSelection().removeAllRanges(); - var textContainerEl = document.querySelector('.copyme'); - textContainerEl.value = JSON.stringify(stats); - var range = document.createRange(); - range.selectNode(textContainerEl); - window.getSelection().addRange(range); - try { - var successful = document.execCommand('copy'); - alert(successful ? - 'Stats have been copied to your clipboard as a JSON string.' : - 'Copy command failed!'); - } catch(e) { - alert('Woops. Clouldn\'t copy the data to the clipboard!' + e); - } - }; - gui.add(params, 'copyStats').name('Export stats'); - } -}); - -var guiScript = document.createElement('script'); -guiScript.src = 'https://google.github.io/paper-gui/dist/paperGUI.js'; -document.body.appendChild(guiScript); -