Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PWA Workshop: From 0 to MS Paint with Project ...

PWA Workshop: From 0 to MS Paint with Project Fugu (Hands-on)

Progressive Web Apps are a great cross-platform application model, even if somewhat restricted in use—until now, that is. Project Fugu is an initiative led by Google, Microsoft, and Intel. Their goal: a more capable web. Home screen shortcuts, sharing files with other apps, clipboard, and file system access are only a few examples of Fugu’s powerful APIs. Are you ready to superpower your Progressive Web App?

In this workshop, you’ll learn how Progressive Web Apps and Project Fugu enable you to develop full productivity apps that feel virtually like native apps, using web technologies. We will implement a simple remake of a productivity app dinosaur: MS Paint. We will first implement the basic functionality, then make the app installable and offline-capable using the PWA APIs, and finally enrich the user experience even further with new Fugu APIs.

Christian Liebel

April 29, 2022
Tweet

More Decks by Christian Liebel

Other Decks in Programming

Transcript

  1. PWA Workshop From 0 to MS Paint with Project Fugu

    (Hands-on) Christian Liebel @christianliebel Consultant
  2. Hello, it’s me. PWA Workshop From 0 to MS Paint

    with Project Fugu (Hands-on) Christian Liebel Twitter: @christianliebel Email: christian.liebel @thinktecture.com Angular & PWA Slides: thinktecture.com /christian-liebel
  3. 09:00–10:30 Block 1 10:30–11:00 Break 11:00–12:30 Block 2 12:30–13:30 Lunch

    Break 13:30–15:00 Block 3 15:00–15:30 Break 15:30–17:00 Block 4 PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Timetable
  4. (Proposal) 09:00–10:30 Block 1 10:30–10:45 Break 10:45–12:30 Block 2 12:30–13:15

    Lunch Break 13:15–15:00 Block 3 15:00–15:15 Break 15:15–16:15 Block 4 PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Timetable
  5. Things NOT to expect - Blueprint for PWA development -

    No AuthN/AuthZ Things To Expect - Extensive/up-to-date insights into PWA and Project Fugu - A productivity app use case that works on your desktop & smartphone - Lots of fun - Hands-on exercises From 0 to MS Paint with Project Fugu (Hands-on) PWA Workshop
  6. https://paint.js.org – Productivity app – Draw images – Lots of

    actions & tools – Installable – Read/save files – Copy/paste images from/to clipboard – Share files to other apps – Register for file extensions PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Demo Use Case
  7. (Workshop Edition) – Productivity app – Draw images – Lots

    of actions & tools – Installable – Read/save files – Copy/paste images from/to clipboard – Share files to other apps – Register for file extensions PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Demo Use Case
  8. Setup complete? (Node.js, Google Chrome, Editor, Git) PWA Workshop From

    0 to MS Paint with Project Fugu (Hands-on) Setup (1/3) LAB #0
  9. git clone https://github.com/thinktecture/ijs-ldn- 2022-pwa.git cd ijs-ldn-2022-pwa npm i npm start

    PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Setup (3/3) LAB #0
  10. Paint PWA Capabilities Progressive Enhancement PWA Workshop From 0 to

    MS Paint with Project Fugu (Hands-on) Agenda
  11. Paint PWA Capabilities Progressive Enhancement PWA Workshop From 0 to

    MS Paint with Project Fugu (Hands-on) Agenda
  12. Canvas – Plain bitmap for the web – Cross-platform, hardware-

    accelerated graphics operations – Supports different contexts (2D, 3D: WebGL, WebGL 2.0) – Supported on all evergreen browsers and on IE 9+ PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint
  13. Canvas Add a canvas element to index.html (line 11): <canvas

    id="canvas" width="600" height="480"></canvas> Query the canvas element in app.js (line 1): const canvas = document.querySelector('#canvas'); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint LAB #1
  14. Canvas 2D Context fillRect(x, y, width, height) strokeRect(x, y, width,

    height) beginPath() moveTo(x, y) lineTo(x, y) fill() stroke() PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint
  15. Canvas 2D Context Get the 2D context and prepare the

    canvas in app.js: const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = 'black'; PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint LAB #2
  16. Low-latency Rendering PWA Workshop From 0 to MS Paint with

    Project Fugu (Hands-on) Paint Web pages need to synchronize with the DOM on graphics updates causing latencies that can make drawing difficult Low-latency rendering skips syncing with the DOM with can lead to a more natural experience Supported on Chrome (Chrome OS and Windows only)
  17. Low-latency Rendering Opt-in to low-latency rendering (app.js): const ctx =

    canvas.getContext('2d', { desynchronized: true }); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint LAB #3
  18. Pointer Events PWA Workshop From 0 to MS Paint with

    Project Fugu (Hands-on) Paint Users nowadays use different input methods, cross-platform apps should cover all of them Pointer Events offer an abstract interface to catch all input methods (pointerdown, pointermove, pointerup) event.offsetX/event.offsetY contain the pointer’s position relative to the listener
  19. Pointer Events Add event listeners (bottom of app.js): canvas.addEventListener('pointerdown', event

    => {}); canvas.addEventListener('pointermove', event => { // ~~ is the shorter Math.floor() ctx.fillRect(~~event.offsetX, ~~event.offsetY, 2, 2); }); canvas.addEventListener('pointerup', () => {}); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint LAB #4
  20. Bresenham Line Algorithm The user may move faster than the

    input can be processed For this case, we need to remember the previous point and interpolate the points in between The Bresenham line algorithm calculates the missing pixels between two given points PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint
  21. Bresenham Line Algorithm 1. Add the following import to the

    top of app.js (“.js” is required, otherwise the request will 404): import {bresenhamLine} from "./helpers.js"; 2. Then, adjust the event listeners: a) In the pointerdown event handler, store ~~event.offsetX/~~event.offsetY in a variable called previousPoint (~~ is required, otherwise Bresenham will go into an endless loop) b) In pointermove i. only draw if the pointer is down ii. retrieve the current point (event.offsetX|Y, ~~ required!) iii. In a for…of loop, call bresenhamLine() with previousPoint.x|y & currentPoint.x|y and draw a 2x2 rectangle for each returned point. iv. store current point in previousPoint c) In pointerup, set previousPoint to null again. PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint LAB #5
  22. Bresenham Line Algorithm let previousPoint = null; canvas.addEventListener('pointerdown', event =>

    previousPoint = {x: ~~event.offsetX, y: ~~event.offsetY}); canvas.addEventListener('pointermove', event => { if (previousPoint) { const currentPoint = {x: ~~event.offsetX, y: ~~event.offsetY}; for(let {x, y} of bresenhamLine(previousPoint.x, previousPoint.y, currentPoint.x, currentPoint.y)) { ctx.fillRect(x, y, 2, 2); } previousPoint = currentPoint; } }); canvas.addEventListener('pointerup', () => previousPoint = null); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint
  23. Color Selection For this demo, we will only implement the

    brush tool. However, we want to bring in some color. A simple method to implement a color selection is to use the HTML color-type input element that shows the native color picker: <input type="color"> PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint
  24. Color Selection 1. In index.html, add the following input to

    the <nav> node: <input type="color" id="color"> 2. In app.js, add the following code at the bottom: const txtColor = document.querySelector('#color'); txtColor.addEventListener('change', () => { ctx.fillStyle = txtColor.value; }); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Paint LAB #6
  25. Paint PWA Capabilities Progressive Enhancement PWA Workshop From 0 to

    MS Paint with Project Fugu (Hands-on) Agenda
  26. From 0 to MS Paint with Project Fugu (Hands-on) PWA

    Workshop Responsive Linkable Discoverable Installable App-like Connectivity Independent Fresh Safe Re-engageable Progressive
  27. PWA Workshop From 0 to MS Paint with Project Fugu

    (Hands-on) Web App Manifest Service Worker
  28. Web App Manifest Distinguishes Web Apps from Websites JSON-based file

    containing metadata for apps only Apps can be identified by search engines, app store providers, etc. PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA
  29. manifest.webmanifest { "short_name": "Paint", "name": "Paint Workshop", "theme_color": "white", "icons":

    [{ "src": "icon.png", "sizes": "512x512" }], "start_url": "/index.html", "display": "standalone", "shortcuts": [/* … */] } PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA Names Icons Display Mode Shortcuts Start URL Theme color (status bar/window bar)
  30. Manifest Display Modes PWA Workshop From 0 to MS Paint

    with Project Fugu (Hands-on) PWA browser minimal-ui standalone fullscreen
  31. Manifest Icon Purposes (any) any context (e.g. app icon) monochrome

    different color requirements (at risk) maskable user agent masks icon as required PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA Safe Zone Windows iOS Android
  32. Web App Manifest 1. In index.html, add the following tag

    to the document’s <head> node (e.g., after the stylesheet): <link rel="manifest" href="manifest.webmanifest"> 2. Create the new file manifest.webmanifest and set: a) name and short_name to values of your choice b) display to standalone c) theme_color to #000080 and background_color to #808080 d) start_url to /index.html and scope to / e) icons to [{ src: "icon.png", sizes: "512x512" }] 3. Test manifest in DevTools: F12 > Application > Manifest PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA LAB #7
  33. Manifest Shortcuts PWA Workshop From 0 to MS Paint with

    Project Fugu (Hands-on) PWA Secondary entrypoints for your application (e.g., home screen quick actions, jump lists, …) Static definition in Web App Manifest Dynamic API may follow in the future Supported by Google Chrome for Android
  34. Web App Manifest 1. In manifest.webmanifest, add (at least) the

    following shortcut: "shortcuts": [{ "name": "About Paint", "short_name": "About", "description": "Show information about Paint", "url": "/about.html", "icons": [{ "src": "icon.png", "sizes": "512x512" }] }], 2. Test in DevTools: F12 > Application > Manifest PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA LAB #8
  35. Web App Manifest – Chrome Installability Signals Web App is

    not already installed Meets a user engagement heuristic (previously: user has interacted with domain for at least 30 seconds) Includes a Web App Manifest that has short_name or name, at least a 192px and 512px icon, a start_url and a display mode of fullscreen, standalone or minimal-ui Served over HTTPS Has a registered service worker with a fetch event handler PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA
  36. Service Worker JavaScript snippet executed in an own thread, registered

    by the website Acts as a controller, proxy, or interceptor Has a cache to store responses (for offline availability and performance) Can wake up even when the website is closed and perform background tasks (e.g., push notifications or sync) PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA
  37. Service Worker PWA Workshop From 0 to MS Paint with

    Project Fugu (Hands-on) PWA Service Worker Internet Website HTML/JS Cache fetch
  38. Workbox Toolchain by Google Includes a CLI Generates a service

    worker implementation from directory contents (e.g., build output, or our development directory) Allows you to modify the service worker behavior (maximum flexibility) PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA
  39. Service Worker 1. In your console, run: a) npm run

    workbox wizard (use “src” as project root and defaults for the rest) b) npm run workbox generateSW 2. In index.html, add the following script to the bottom of the <body> node: <script> if (navigator.serviceWorker) { navigator.serviceWorker.register('sw.js'); } </script> 3. Test in DevTools: F12 > Application > Service Worker 4. Install the app: Address Bar > ⊕ Install PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA LAB #9
  40. Service Worker Debugging More information on installed service workers can

    be found on • about://serviceworker- internals (Chromium-based browsers) • about:serviceworkers (Mozilla Firefox) PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA
  41. Lighthouse PWA Workshop From 0 to MS Paint with Project

    Fugu (Hands-on) PWA Auditing tool by Google, integrated in Chrome DevTools Automatically checks performance metrics, PWA support, accessibility, SEO and other best practices and gives tips on how to improve Can simulate mobile devices
  42. Lighthouse 1. Make sure only one tab/window of your PWA

    is open 2. Open DevTools: F12 > Lighthouse 3. Make sure to select at least the “Progressive Web App” category 4. Click “Generate report” PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) PWA LAB #10
  43. Paint PWA Capabilities Progressive Enhancement PWA Workshop From 0 to

    MS Paint with Project Fugu (Hands-on) Agenda
  44. Project Fugu PWA Workshop From 0 to MS Paint with

    Project Fugu (Hands-on) Capabilities »Let’s bring the web back – API by API« Thomas Steiner, Google
  45. PWA Workshop From 0 to MS Paint with Project Fugu

    (Hands-on) Capabilities Contacts Picker Screen Wake Lock API File System Access API Shape Detection API
  46. PWA Workshop From 0 to MS Paint with Project Fugu

    (Hands-on) Capabilities https://goo.gle/fugu-api-tracker
  47. Fugu Process – Write an Explainer (https://github.com/WICG/native-file- system/blob/master/EXPLAINER.md) – Discuss

    capability with developers, browser vendors, standard orgs – Iterate – Compile a design document – W3C TAG Review (https://github.com/w3ctag/design- reviews/issues/390) – Formal spec within Web Incubator Community Group (WICG, https://wicg.github.io/native-file-system/) PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities
  48. Fugu Process – Implementation in Chromium – Launches behind a

    flag (chrome://flags/#enable-experimental-web- platform-features) – Origin Trial (https://developers.chrome.com/origintrials/) – Transfer to W3C Working Group + Recommendation? PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities
  49. Browser PWA Workshop From 0 to MS Paint with Project

    Fugu (Hands-on) Capabilities navigator.share({ url: 'http://example.com' }); ShareIntent DataTransferManager … NSSharingServicePicker
  50. File System Access API Some applications heavily rely on working

    with files (e.g. Visual Studio Code, Adobe Photoshop, …) File System Access API allows you to open, save and override files and directories Supported by Chrome, Edge PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities
  51. File System Access API 1. Add the following buttons to

    the <nav> bar in index.html (before the color input): <button id="open">Open</button> <button id="save">Save</button> <button id="copy">Copy</button> <button id="paste">Paste</button> <button id="share">Share</button> PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #11
  52. File System Access API 2. At the top of app.js,

    change the import as follows: import {bresenhamLine, getImage, toBlob} from "./helpers.js"; 3. Add the following lines at the bottom of app.js: const fileOptions = { types: [{ description: 'PNG files', accept: {'image/png': ['.png']} }] }; PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #11
  53. File System Access API 4. Add the following lines at

    the bottom of app.js: const btnSave = document.querySelector('#save'); btnSave.addEventListener('click', async () => { const blob = await toBlob(canvas); const handle = await window.showSaveFilePicker(fileOptions); const writable = await handle.createWritable(); await writable.write(blob); await writable.close(); }); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #11
  54. File System Access API Add the following code at the

    end of app.js: const btnOpen = document.querySelector('#open'); btnOpen.addEventListener('click', async () => { const [handle] = await window.showOpenFilePicker(fileOptions); const file = await handle.getFile(); const image = await getImage(file); ctx.drawImage(image, 0, 0); }); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #12
  55. Async Clipboard API Allows reading from/writing to the clipboard in

    an asynchronous manner (UI won’t freeze during long-running operations) Reading from the clipboard requires user consent first (privacy!) Supported by Chrome, Edge and Safari PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities
  56. Async Clipboard API Add the following code at the end

    of app.js: const btnCopy = document.querySelector('#copy'); btnCopy.addEventListener('click', async () => { const blob = await toBlob(canvas); await navigator.clipboard.write([ new ClipboardItem({[blob.type]: blob}) ]); }); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #13
  57. Async Clipboard API const btnPaste = document.querySelector('#paste'); btnPaste.addEventListener('click', async ()

    => { const clipboardItems = await navigator.clipboard.read(); for (const clipboardItem of clipboardItems) { for (const type of clipboardItem.types) { if (type === 'image/png') { const blob = await clipboardItem.getType(type); const image = await getImage(blob); ctx.drawImage(image, 0, 0); } } } }); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #14
  58. Web Share API Allows sharing a title, URL, text, or

    files API can only be invoked as a result of a user action (i.e. a click or keypress) Supported by Chrome, Edge, Safari PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities
  59. Web Share API const btnShare = document.querySelector('#share'); btnShare.disabled = !('canShare'

    in navigator); btnShare.addEventListener('click', async () => { const blob = await toBlob(canvas); const file = new File([blob], 'untitled.png', {type: 'image/png'}); const item = {files: [file], title: 'untitled.png'}; if (await navigator.canShare(item)) { await navigator.share(item); } }); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #15
  60. File Handling API Register your PWA as a handler for

    file extensions Requires installing the application first Declare supported extensions in Web App Manifest and add imperative code to your application logic PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities
  61. File Handling API 1. Enable the following flag in a

    Chromium-based browser and restart the browser: about://flags/#file-handling-api 2. Add the following property to your manifest (manifest.webmanifest): "file_handlers": [{ "action": "/index.html", "accept": { "image/png": [".png"] } }] PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #16
  62. File Handling API 3. Add the following code to the

    bottom of app.js: if ('launchQueue' in window) { launchQueue.setConsumer(async params => { const [handle] = params.files; if (handle) { const file = await handle.getFile(); const image = await getImage(file); ctx.drawImage(image, 0, 0); } }); } 4. Re-generate Service Worker: npm run workbox generateSW PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities LAB #16
  63. Summary - Project Fugu helps the web to become more

    and more capable - Project Fugu APIs could bring many more app experiences to the web (IDEs, productivity apps, etc.) - But: Web is threatened by alternative approaches and platforms, support for modern Web API varies from platform to platform - You can make a difference! - File bugs in browser engine bugtrackers - File Fugu requests: https://goo.gle/new-fugu-request PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Capabilities
  64. Paint PWA Capabilities Progressive Enhancement PWA Workshop From 0 to

    MS Paint with Project Fugu (Hands-on) Agenda
  65. Overview Use available interfaces and functions of a system (opposite

    of Graceful Degradation) Users with modern, feature-rich browsers get a better experience Apps are available on older browsers, but with limited functionality Concept: Browser feature support should grow over time—thereby more users can enjoy an increasing number of features PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Progressive Enhancement
  66. Overview if ('serviceWorker' in navigator) { navigator.serviceWorker.register(…) .then(() => /*

    … */); } In JavaScript: check whether an API/feature is available. If yes—use it! Otherwise: 1. Disable the functionality 2. Fall back to an alternative API (if available) PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Progressive Enhancement
  67. Disabling unsupported features In app.js, disable the buttons in case

    the respective API does not exist, e.g.: btnOpen.disabled = !('showOpenFilePicker' in window); 1. Open: window.showOpenFilePicker() 2. Save: window.showSaveFilePicker() 3. Copy: navigator.clipboard + navigator.clipboard.write() 4. Paste: navigator.clipboard + navigator.clipboard.read() 5. Share: navigator.canShare() PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Progressive Enhancement LAB #17
  68. Providing an alternative implementation 1. In app.js, test for the

    existence of the showSaveFilePicker() method on the window object. 2. If the method does not exist, fall back to the following code for save: const anchor = document.createElement('a'); const url = URL.createObjectURL(blob); anchor.href = url; anchor.download = ''; anchor.click(); URL.revokeObjectURL(url); PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Progressive Enhancement LAB #18
  69. Deployment PWA Workshop From 0 to MS Paint with Project

    Fugu (Hands-on) Electron/Capacitor JS HTML CSS .ipa .exe .app ELF .apk .appx Single-Page Application Capacitor Electron
  70. (Simplified) PWA Workshop From 0 to MS Paint with Project

    Fugu (Hands-on) Store Deployments https://example.com .appx .apk Server Microsoft Store Play Store Source Files
  71. Run app on your device npm run workbox generateSW https://ngrok.com/download

    ngrok http 3000 PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) LAB #19
  72. Crunch Time PWA Workshop From 0 to MS Paint with

    Project Fugu (Hands-on) LAB #20
  73. New powerful APIs regularly ship with new releases of Chromium-based

    browsers Some only add minor finishing touches, others enable whole application categories as productivity apps to finally make the shift to the web Some APIs already made their way into other browsers (e.g., Web Share) Fugu process makes sure that capabilities are implemented in a secure, privacy-preserving manner Let’s make the web a more capable platform! PWA Workshop From 0 to MS Paint with Project Fugu (Hands-on) Summary