From 3c1ef577407ac6216ead8ffd443699c1d0fe506c Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Fri, 6 Oct 2023 02:57:46 +0200 Subject: [PATCH] prevent Cumulative Layout Shift by fading in elements after initial translation is loaded --- public/scripts/network.js | 4 +- public/scripts/ui.js | 21 +++++++++- public/styles.css | 40 ++++++++++++++----- .../scripts/network.js | 4 +- public_included_ws_fallback/scripts/ui.js | 19 ++++++++- public_included_ws_fallback/styles.css | 40 ++++++++++++++----- 6 files changed, 103 insertions(+), 25 deletions(-) diff --git a/public/scripts/network.js b/public/scripts/network.js index 7a197b6..9d2a69e 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -220,7 +220,9 @@ class ServerConnection { _onDisconnect() { console.log('WS: server disconnected'); - Events.fire('notify-user', Localization.getTranslation("notifications.connecting")); + setTimeout(() => { + Events.fire('notify-user', Localization.getTranslation("notifications.connecting")); + }, 100); //delay for 100ms to prevent flickering on page reload clearTimeout(this._reconnectTimer); this._reconnectTimer = setTimeout(_ => this._connect(), 1000); Events.fire('ws-disconnected'); diff --git a/public/scripts/ui.js b/public/scripts/ui.js index 5b66f42..a930dd3 100644 --- a/public/scripts/ui.js +++ b/public/scripts/ui.js @@ -36,9 +36,24 @@ class PeersUI { Events.on('drop', e => this._onDrop(e)); Events.on('keydown', e => this._onKeyDown(e)); + this.$header = document.querySelector('body > header') this.$xPeers = $$('x-peers'); this.$xNoPeers = $$('x-no-peers'); this.$xInstructions = $$('x-instructions'); + this.$center = $$('#center'); + this.$logo = $$('footer .icon.logo'); + this.$discoveryWrapper = $$('footer .discovery-wrapper'); + this.$knownAsWrapper = $$('footer .known-as-wrapper'); + + this.$header.style.opacity = "1"; + this.$xPeers.style.opacity = "1"; + this.$xNoPeers.style.opacity = "1"; + this.$xInstructions.style.opacity = "0.5"; + this.$center.style.opacity = "1"; + this.$logo.style.opacity = "1"; + this.$discoveryWrapper.style.opacity = "1"; + this.$knownAsWrapper.style.opacity = "1"; + Events.on('peer-added', _ => this.evaluateOverflowing()); Events.on('bg-resize', _ => this.evaluateOverflowing()); @@ -1009,7 +1024,7 @@ class ReceiveRequestDialog extends ReceiveDialog { : Localization.getTranslation('document-titles.file-transfer-requested'); this.$receiveTitle.innerText = transferRequestTitle; - + document.title = `${transferRequestTitle} - PairDrop`; document.changeFavicon("images/favicon-96x96-notification.png"); this.show(); @@ -2048,6 +2063,7 @@ class Notifications { this.$button.removeAttribute('hidden'); this.$button.addEventListener('click', _ => this._requestPermission()); } + Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId)); Events.on('files-received', e => this._downloadNotification(e.detail.files)); Events.on('files-transfer-request', e => this._requestNotification(e.detail.request, e.detail.peerId)); @@ -2733,6 +2749,7 @@ Events.on('load', () => { dw = Math.round(Math.max(w, h, 1000) / 13); drawCircles(cCtx, dw); + c.style.opacity = "1"; } Events.on('bg-resize', _ => init()); @@ -2754,7 +2771,7 @@ Events.on('load', () => { } } - init(); + setTimeout(_ => init(), 300); }); document.changeFavicon = function (src) { diff --git a/public/styles.css b/public/styles.css index 925a783..33f0811 100644 --- a/public/styles.css +++ b/public/styles.css @@ -415,8 +415,9 @@ x-no-peers { display: flex; flex-direction: column; padding: 8px; + height: 137px; text-align: center; - animation: fade-in 300ms; + animation: fade-in 600ms; animation-fill-mode: backwards; /* prevent flickering on load */ animation-iteration-count: 0; @@ -612,6 +613,7 @@ footer .logo { margin-bottom: 8px; color: var(--primary-color); margin-top: -10px; + animation: ease-in; } .discovery-wrapper { @@ -622,6 +624,7 @@ footer .logo { padding: 2px; background-color: rgb(var(--bg-color)); transition: background-color 0.5s ease; + min-height: 24px; } /*You can be discovered wrapper*/ @@ -1200,15 +1203,22 @@ button::-moz-focus-inner { z-index: 1; } +#about:not(:target) header.fade-in { + transition-delay: 400ms; +} + +#about:target header.fade-in { + transition-delay: 100ms; +} + #about .fade-in { - transition: opacity 300ms; + transition: opacity 300ms ease 300ms; will-change: opacity; - transition-delay: 300ms; pointer-events: all; } #about:not(:target) .fade-in { - opacity: 0; + opacity: 0 !important; pointer-events: none; transition-delay: 0s; } @@ -1258,10 +1268,6 @@ button::-moz-focus-inner { flex-grow: 1; } -#about header { - align-self: end; -} - canvas.circles { width: 100vw; position: absolute; @@ -1345,7 +1351,6 @@ x-toast:not([show]):not(:hover) { x-instructions { position: relative; opacity: 0.5; - transition: opacity 300ms; text-align: center; margin-left: 10px; margin-right: 10px; @@ -1372,7 +1377,7 @@ x-instructions p { } x-peers:empty~x-instructions { - opacity: 0; + opacity: 0 !important; } @media (hover: none) and (pointer: coarse) { @@ -1382,6 +1387,21 @@ x-peers:empty~x-instructions { } } +/* Prevent Cumulative Layout Shift */ + +body > header, +canvas, +#center, +x-no-peers, +x-peers, +x-instructions, +footer > .icon.logo, +.discovery-wrapper, +.known-as-wrapper { + transition: opacity 0.5s ease 0.1s; + opacity: 0; /* will be set to 1 after initial translation is loaded */ +} + /* Responsive Styles */ @media screen and (min-height: 800px) { diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index 87e4ea2..3329d37 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -231,7 +231,9 @@ class ServerConnection { _onDisconnect() { console.log('WS: server disconnected'); - Events.fire('notify-user', Localization.getTranslation("notifications.connecting")); + setTimeout(() => { + Events.fire('notify-user', Localization.getTranslation("notifications.connecting")); + }, 100); //delay for 100ms to prevent flickering on page reload clearTimeout(this._reconnectTimer); this._reconnectTimer = setTimeout(_ => this._connect(), 1000); Events.fire('ws-disconnected'); diff --git a/public_included_ws_fallback/scripts/ui.js b/public_included_ws_fallback/scripts/ui.js index 32761ae..e03c377 100644 --- a/public_included_ws_fallback/scripts/ui.js +++ b/public_included_ws_fallback/scripts/ui.js @@ -36,9 +36,24 @@ class PeersUI { Events.on('drop', e => this._onDrop(e)); Events.on('keydown', e => this._onKeyDown(e)); + this.$header = document.querySelector('body > header') this.$xPeers = $$('x-peers'); this.$xNoPeers = $$('x-no-peers'); this.$xInstructions = $$('x-instructions'); + this.$center = $$('#center'); + this.$logo = $$('footer .icon.logo'); + this.$discoveryWrapper = $$('footer .discovery-wrapper'); + this.$knownAsWrapper = $$('footer .known-as-wrapper'); + + this.$header.style.opacity = "1"; + this.$xPeers.style.opacity = "1"; + this.$xNoPeers.style.opacity = "1"; + this.$xInstructions.style.opacity = "0.5"; + this.$center.style.opacity = "1"; + this.$logo.style.opacity = "1"; + this.$discoveryWrapper.style.opacity = "1"; + this.$knownAsWrapper.style.opacity = "1"; + Events.on('peer-added', _ => this.evaluateOverflowing()); Events.on('bg-resize', _ => this.evaluateOverflowing()); @@ -2050,6 +2065,7 @@ class Notifications { this.$button.removeAttribute('hidden'); this.$button.addEventListener('click', _ => this._requestPermission()); } + Events.on('text-received', e => this._messageNotification(e.detail.text, e.detail.peerId)); Events.on('files-received', e => this._downloadNotification(e.detail.files)); Events.on('files-transfer-request', e => this._requestNotification(e.detail.request, e.detail.peerId)); @@ -2734,6 +2750,7 @@ Events.on('load', () => { dw = Math.round(Math.max(w, h, 1000) / 13); drawCircles(cCtx, dw); + c.style.opacity = "1"; } Events.on('bg-resize', _ => init()); @@ -2755,7 +2772,7 @@ Events.on('load', () => { } } - init(); + setTimeout(_ => init(), 300); }); document.changeFavicon = function (src) { diff --git a/public_included_ws_fallback/styles.css b/public_included_ws_fallback/styles.css index 4f94217..55be5c3 100644 --- a/public_included_ws_fallback/styles.css +++ b/public_included_ws_fallback/styles.css @@ -420,8 +420,9 @@ x-no-peers { display: flex; flex-direction: column; padding: 8px; + height: 137px; text-align: center; - animation: fade-in 300ms; + animation: fade-in 600ms; animation-fill-mode: backwards; /* prevent flickering on load */ animation-iteration-count: 0; @@ -642,6 +643,7 @@ footer .logo { margin-bottom: 8px; color: var(--primary-color); margin-top: -10px; + animation: ease-in; } .discovery-wrapper { @@ -652,6 +654,7 @@ footer .logo { padding: 2px; background-color: rgb(var(--bg-color)); transition: background-color 0.5s ease; + min-height: 24px; } /*You can be discovered wrapper*/ @@ -1230,15 +1233,22 @@ button::-moz-focus-inner { z-index: 1; } +#about:not(:target) header.fade-in { + transition-delay: 400ms; +} + +#about:target header.fade-in { + transition-delay: 100ms; +} + #about .fade-in { - transition: opacity 300ms; + transition: opacity 300ms ease 300ms; will-change: opacity; - transition-delay: 300ms; pointer-events: all; } #about:not(:target) .fade-in { - opacity: 0; + opacity: 0 !important; pointer-events: none; transition-delay: 0s; } @@ -1288,10 +1298,6 @@ button::-moz-focus-inner { flex-grow: 1; } -#about header { - align-self: end; -} - canvas.circles { width: 100vw; position: absolute; @@ -1375,7 +1381,6 @@ x-toast:not([show]):not(:hover) { x-instructions { position: relative; opacity: 0.5; - transition: opacity 300ms; text-align: center; margin-left: 10px; margin-right: 10px; @@ -1402,7 +1407,7 @@ x-instructions p { } x-peers:empty~x-instructions { - opacity: 0; + opacity: 0 !important; } @media (hover: none) and (pointer: coarse) { @@ -1412,6 +1417,21 @@ x-peers:empty~x-instructions { } } +/* Prevent Cumulative Layout Shift */ + +body > header, +canvas, +#center, +x-no-peers, +x-peers, +x-instructions, +footer > .icon.logo, +.discovery-wrapper, +.known-as-wrapper { + transition: opacity 0.5s ease 0.1s; + opacity: 0; /* will be set to 1 after initial translation is loaded */ +} + /* Responsive Styles */ @media screen and (min-height: 800px) {