From bd9b91f480a698989d15df0c4db226c4ff2c845b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fe=CC=81lix=20Pe=CC=81ault?= Date: Sun, 5 Apr 2020 16:07:44 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Fix=20most=20animations/tr?= =?UTF-8?q?ansitions=20performance=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Basically add `will-change` to CSS elements to enable GPU animations, less choppy, more sassy - Use requestAnimationFrame over setTimeout - Avoid repaints AMAP --- src/animations/Carousel.js | 9 ++--- src/animations/Locations.js | 31 +++++++++-------- src/animations/Photo.js | 2 +- src/animations/TitleSite.js | 1 - src/animations/index.js | 50 ++++++++++++---------------- src/animations/page.js | 1 - src/animations/place.js | 2 +- src/animations/viewer.js | 35 ++++++++++--------- src/routes/sitemap.xml.js | 3 +- src/style/_animations.scss | 6 ++-- src/style/_base.scss | 14 ++++++-- src/style/atoms/_button-control.scss | 4 +-- src/style/molecules/_location.scss | 4 +++ src/style/molecules/_photo.scss | 3 ++ src/style/organisms/_carousel.scss | 24 ++++++++----- src/style/organisms/_fullscreen.scss | 7 ++-- src/style/pages/_homepage.scss | 4 +++ src/style/pages/_page.scss | 9 ++++- src/style/pages/_place.scss | 2 ++ src/style/pages/_viewer.scss | 5 +-- src/utils/Transition.svelte | 2 +- 21 files changed, 129 insertions(+), 89 deletions(-) diff --git a/src/animations/Carousel.js b/src/animations/Carousel.js index d060f52..d0e7146 100644 --- a/src/animations/Carousel.js +++ b/src/animations/Carousel.js @@ -9,14 +9,15 @@ import { animDuration, animDelay } from 'utils/store' export const animateIn = scope => { const tl = anime.timeline({ easing: 'easeOutQuart', - duration: animDuration + duration: animDuration, + delay: 0 }) // Photo: Active tl.add({ targets: scope.querySelector('.is-active picture'), - translateY: [32, 0] - }) + translateY: [8, 0] + }, 100) // Photo: Prev tl.add({ targets: scope.querySelector('.is-prev picture'), @@ -30,7 +31,7 @@ export const animateIn = scope => { translateY: [8, 0], translateX: [-48, 0], rotate: [2, 0] - }, 150) + }, 100) // Reveal on scroll const carouselIn = ScrollOut({ diff --git a/src/animations/Locations.js b/src/animations/Locations.js index 97f07ee..cf005c5 100644 --- a/src/animations/Locations.js +++ b/src/animations/Locations.js @@ -11,30 +11,33 @@ export const animateIn = scope => { const locations = ScrollOut({ targets: scope.querySelectorAll('.location'), onShown (el) { - const tl = anime.timeline({ - easing: 'easeOutQuart', - duration: 600 - }) - // Image - tl.add({ + anime({ targets: el.querySelector('img'), scale: [1.3, 1], opacity: [0, 1], - duration: 1800 - }, 100) + easing: 'easeOutQuart', + duration: 1800, + delay: 100 + }) // Name - tl.add({ + anime({ targets: el.querySelector('h3'), - translateY: ['100%', 0] - }, 300) + translateY: ['100%', 0], + easing: 'easeOutQuart', + duration: 600, + delay: 300 + }) // Country - tl.add({ + anime({ targets: el.querySelector('p'), - translateY: ['100%', 0] - }, 550) + translateY: ['100%', 0], + easing: 'easeOutQuart', + duration: 600, + delay: 550 + }) } }) } diff --git a/src/animations/Photo.js b/src/animations/Photo.js index 877e725..8b83b66 100644 --- a/src/animations/Photo.js +++ b/src/animations/Photo.js @@ -66,7 +66,7 @@ export const animateIn = scope => { autoplay: false }) window.addEventListener('scroll', throttle(() => parallaxAnime(el, translate), 50)) - setTimeout(() => parallaxAnime(el, translate), 50) + requestAnimationFrame(() => parallaxAnime(el, translate)) }, onHidden () { if (parallaxAnime) window.removeEventListener('scroll', parallaxAnime) diff --git a/src/animations/TitleSite.js b/src/animations/TitleSite.js index 7c23e78..7465916 100644 --- a/src/animations/TitleSite.js +++ b/src/animations/TitleSite.js @@ -11,7 +11,6 @@ export const animateIn = (scope, init) => { easing: 'easeOutQuart', duration: 1000 }) - // Stagger each letters and words tl.add({ targets: scope.querySelectorAll('span, em span'), diff --git a/src/animations/index.js b/src/animations/index.js index e8ddc34..95e3e66 100644 --- a/src/animations/index.js +++ b/src/animations/index.js @@ -17,16 +17,27 @@ export const animateIn = () => { easing: 'easeOutQuart' }) + // Carousel + const carousel = anime({ + targets: document.querySelector('.intro .carousel'), + translateY: [32, 0], + opacity: [0, 1], + easing: 'easeOutQuart', + duration: animDuration, + delay: animDelay, + complete: event => event.animatables[0].target.removeAttribute('style') + }) + // Parallax on scroll const translate = anime({ targets: '#title-houses', translateX: ['7%', '-15%'], easing: 'linear', - autoplay: false, - duration: animDuration + duration: animDuration, + autoplay: false }) window.addEventListener('scroll', throttle(() => parallaxAnime(document.getElementById('title-houses'), translate), 10)) - setTimeout(() => parallaxAnime(document.getElementById('title-houses'), translate), 50) + requestAnimationFrame(() => parallaxAnime(document.getElementById('title-houses'), translate), 50) // Intro: Description const introDescription = anime({ @@ -38,23 +49,6 @@ export const animateIn = () => { delay: anime.stagger(200, { start: animDelay + 200 }) }) - // Intro: Carousel revealing (scroll) - const introCarousel = ScrollOut({ - once: true, - targets: '#intro-carousel', - onShown (el) { - anime({ - targets: el, - opacity: [0, 1], - translateY: [24, 0], - easing: 'easeOutQuart', - duration: animDuration, - delay: animDelay + 400, - complete: event => event.animatables[0].target.removeAttribute('style') - }) - } - }) - // Title: Of (reveal on scroll) const titleOf = ScrollOut({ once: true, @@ -71,6 +65,13 @@ export const animateIn = () => { }) // Title: World (reveal on scroll) + const titleWorldTranslate = anime({ + targets: document.getElementById('title-world'), + translateX: ['5%', '-3%'], + easing: 'linear', + autoplay: false, + duration: animDuration + }) const titleWorld = ScrollOut({ once: true, targets: document.getElementById('title-world'), @@ -82,15 +83,8 @@ export const animateIn = () => { delay: anime.stagger(70), duration: animDuration }) - const titleWorldTranslate = anime({ - targets: el, - translateX: ['5%', '-3%'], - easing: 'linear', - autoplay: false, - duration: animDuration - }) window.addEventListener('scroll', throttle(() => parallaxAnime(el, titleWorldTranslate), 10)) - setTimeout(() => parallaxAnime(el, titleWorldTranslate), 50) + requestAnimationFrame(() => parallaxAnime(el, titleWorldTranslate), 50) }, onHidden: () => window.removeEventListener('scroll', parallaxAnime) }) diff --git a/src/animations/page.js b/src/animations/page.js index 13edb2c..33b622c 100644 --- a/src/animations/page.js +++ b/src/animations/page.js @@ -10,7 +10,6 @@ export const animateIn = () => { easing: 'easeOutQuart', duration: animDuration }) - // Simple slide and fade on each part of the page tl.add({ targets: document.querySelectorAll('.page__part'), diff --git a/src/animations/place.js b/src/animations/place.js index cf0182b..75b01ad 100644 --- a/src/animations/place.js +++ b/src/animations/place.js @@ -38,7 +38,7 @@ export const animateIn = () => { // Illustration tl.add({ targets: '.place__illustration', - scale: [1.075, 1], + scale: [1.1, 1], opacity: [0, 1] }, 0) diff --git a/src/animations/viewer.js b/src/animations/viewer.js index 4ebf025..5a207a4 100644 --- a/src/animations/viewer.js +++ b/src/animations/viewer.js @@ -1,35 +1,38 @@ import anime from 'animejs' -import { animDuration } from 'utils/store' +import { animDuration, animDelay } from 'utils/store' /* ** Transition In */ export const animateIn = () => { + const viewer = document.querySelector('.viewer') const tl = anime.timeline({ easing: 'easeOutQuart', - duration: animDuration - }) - - // Buttons - tl.add({ - targets: '.viewer__top p, .viewer__top .buttons a', - translateY: [-32, 0], - delay: anime.stagger(150, { start: 600 }), + duration: animDuration, + delay: animDelay }) // Carousel tl.add({ - targets: '.viewer .carousel', + targets: viewer.querySelector('.carousel'), opacity: [0, 1], - translateY: window.innerWidth >= 768 ? [32, 0] : ['-33%', '-37%'] - }, 300) + translateY: window.innerWidth >= 768 ? [32, 0] : ['-33%', '-37%'], + complete: event => event.animatables[0].target.removeAttribute('style') + }) // Carousel: Number tl.add({ - targets: '.viewer .carousel__number_column', + targets: viewer.querySelector('.counter'), opacity: [0, 1], - marginTop: [24, 0], - delay: anime.stagger(100) - }, 450) + translateY: [window.innerWidth >= 768 ? -24 : 24, 0] + }, 0) + + // Buttons + tl.add({ + targets: viewer.querySelectorAll('.tip, .viewer__buttons a'), + translateY: [-32, 0], + opacity: [0, 1], + delay: anime.stagger(120), + }, 400) } diff --git a/src/routes/sitemap.xml.js b/src/routes/sitemap.xml.js index 35a30b1..f1de5b4 100644 --- a/src/routes/sitemap.xml.js +++ b/src/routes/sitemap.xml.js @@ -8,9 +8,10 @@ let baseURL const pages = [''] // Get routes and push it to array +const routesExclude = ['sitemap', 'index', 'location', 'viewer'] fs.readdirSync('./src/routes').forEach(file => { const filename = file.split('.')[0] - if (!file.startsWith('.') && !filename.startsWith('_') && ['sitemap', 'index', 'location', 'viewer'].indexOf(filename) === -1) { + if (!file.startsWith('.') && !filename.startsWith('_') && routesExclude.indexOf(filename) === -1) { pages.push(filename) } }) diff --git a/src/style/_animations.scss b/src/style/_animations.scss index c52d314..429a289 100644 --- a/src/style/_animations.scss +++ b/src/style/_animations.scss @@ -12,7 +12,10 @@ display: flex; align-items: center; justify-content: center; - will-change: transform; + + &, * { + will-change: transform, opacity; + } // Content &__loader { @@ -29,7 +32,6 @@ height: 100%; background-color: $color-primary; transform-origin: 50% 0; - will-change: transform; } // Hidden diff --git a/src/style/_base.scss b/src/style/_base.scss index 43840c4..92d85a9 100644 --- a/src/style/_base.scss +++ b/src/style/_base.scss @@ -59,7 +59,6 @@ button { letter-spacing: -2vw; pointer-events: none; user-select: none; - will-change: transform; @include breakpoint (lg) { font-size: pxVW(700); @@ -67,6 +66,10 @@ button { @include breakpoint (1920px) { font-size: rem(900px); } + + &, span { + will-change: transform, opacity; + } } @@ -109,6 +112,9 @@ button { margin-right: -8px; } } + span, em { + will-change: transform; + } // Bigger version &--big { @@ -189,7 +195,7 @@ button { .style-caps { font-family: $font-sans; - font-size: rem(14px); + font-size: rem(12px); color: $color-tertiary; text-transform: uppercase; letter-spacing: 1px; @@ -198,6 +204,10 @@ button { &--transparent { color: rgba($color-tertiary, 0.5); } + + @include breakpoint (sm) { + font-size: rem(14px); + } } .style-description { diff --git a/src/style/atoms/_button-control.scss b/src/style/atoms/_button-control.scss index 2a9e86f..72e90ae 100644 --- a/src/style/atoms/_button-control.scss +++ b/src/style/atoms/_button-control.scss @@ -14,7 +14,7 @@ cursor: pointer; outline: none; transition: background-color 350ms $ease-quart; - will-change: background-color; + will-change: transform, opacity; @include breakpoint (sm) { width: 56px; @@ -160,7 +160,7 @@ overflow: visible; background-color: rgba($color-lightpurple, 0.5); transition: background-color 150ms $ease-inout; - will-change: background-color; + will-change: transform, opacity; // Icon svg[fill] { diff --git a/src/style/molecules/_location.scss b/src/style/molecules/_location.scss index 97fda66..558c808 100644 --- a/src/style/molecules/_location.scss +++ b/src/style/molecules/_location.scss @@ -12,6 +12,10 @@ margin-right: 72px; } + &, a * { + will-change: transform, opacity; + } + a { position: relative; z-index: 2; diff --git a/src/style/molecules/_photo.scss b/src/style/molecules/_photo.scss index 2d072a2..d779953 100644 --- a/src/style/molecules/_photo.scss +++ b/src/style/molecules/_photo.scss @@ -23,6 +23,7 @@ span { display: block; + will-change: transform; } } p { @@ -80,6 +81,7 @@ display: block; width: 100%; height: auto; + will-change: transform, opacity; } // Overlay @@ -142,6 +144,7 @@ span { display: block; + will-change: transform; } @include breakpoint (sm) { diff --git a/src/style/organisms/_carousel.scss b/src/style/organisms/_carousel.scss index b112d1d..a009501 100644 --- a/src/style/organisms/_carousel.scss +++ b/src/style/organisms/_carousel.scss @@ -1,7 +1,7 @@ // Carousel .carousel { background-color: $color-primary; - overflow: hidden; + will-change: transform, opacity; @include breakpoint (sm) { overflow: visible; @@ -65,7 +65,7 @@ height: 100%; transform: scale($scale); transition: transform $duration $ease-quart, opacity ($duration / 2) $ease-quart; - will-change: transform, opacity, top, left; + @extend %willchange; // Active photo &.is-active { @@ -134,6 +134,7 @@ width: 100%; height: 100%; box-shadow: 0 pxVW(15) pxVW(60) rgba(#000, 0.3); + @extend %willchange; @include breakpoint (sm) { border-radius: $radius; @@ -162,6 +163,11 @@ height: auto; } } + + // Performance + %willchange { + will-change: transform, opacity; + } } @@ -231,11 +237,11 @@ left: 0; width: 100%; opacity: 0; + margin-top: 32px; + padding-bottom: 4px; text-align: center; transition: transform 0.9s $ease-quart, opacity 0.9s $ease-quart; will-change: transform, opacity; - margin-top: 32px; - padding-bottom: 4px; @include breakpoint (sm) { margin-top: pxVW(80); @@ -244,6 +250,11 @@ margin-top: 72px; } + // State + .state { + margin-top: 6px; + } + // States &.is-prev { transform: translateY(-$distance); @@ -255,11 +266,6 @@ &.is-next { transform: translateY($distance); } - - // State - .state { - margin-top: 6px; - } } diff --git a/src/style/organisms/_fullscreen.scss b/src/style/organisms/_fullscreen.scss index 93c68aa..8d105b6 100644 --- a/src/style/organisms/_fullscreen.scss +++ b/src/style/organisms/_fullscreen.scss @@ -8,6 +8,10 @@ overflow: hidden; pointer-events: none; + &, * { + will-change: transform, opacity; + } + // Photo &__image { width: 100%; @@ -17,7 +21,6 @@ transform: scale(1.1); background: $color-primary; transition: transform 0.8s $ease-quart, opacity 0.8s $ease-quart; - will-change: transform, opacity; img { position: relative; @@ -48,7 +51,6 @@ opacity: 0; transform: scale(1.1) translateY(24px); transition: transform 0.8s $ease-quart, opacity 0.8s $ease-quart; - will-change: transform, opacity; // Visible state &.is-visible { @@ -74,7 +76,6 @@ background-color: $color-primary; border-radius: 100%; transition: transform 0.8s $ease-quart, opacity 0.8s $ease-quart; - will-change: transform, opacity; // Hidden state &.is-hidden { diff --git a/src/style/pages/_homepage.scss b/src/style/pages/_homepage.scss index aa9202a..42984ee 100644 --- a/src/style/pages/_homepage.scss +++ b/src/style/pages/_homepage.scss @@ -92,6 +92,10 @@ margin-bottom: 200px; font-size: rem(248px); } + + span { + will-change: transform; + } } // Massive title diff --git a/src/style/pages/_page.scss b/src/style/pages/_page.scss index 03f6a4d..e55bfdc 100644 --- a/src/style/pages/_page.scss +++ b/src/style/pages/_page.scss @@ -1,6 +1,11 @@ .page { overflow-x: hidden; + // Page part + &__part { + will-change: transform, opacity; + } + // Top part &__top { margin-top: 96px; @@ -64,7 +69,9 @@ margin-bottom: 40px; } dt { - margin-bottom: 8px; + @include breakpoint (sm) { + margin-bottom: 8px; + } } } diff --git a/src/style/pages/_place.scss b/src/style/pages/_place.scss index 7c0604f..54c0d5b 100644 --- a/src/style/pages/_place.scss +++ b/src/style/pages/_place.scss @@ -78,6 +78,7 @@ padding-top: pxVW(200); padding-bottom: 72px; background-color: $color-tertiary; + will-change: transform, opacity; @include breakpoint (m) { padding-bottom: 96px; @@ -170,6 +171,7 @@ background-position: 0 0; background-repeat: no-repeat; background-size: 100% auto; + will-change: transform, opacity; @include breakpoint (sm) { background-image: var(--url-desktop); diff --git a/src/style/pages/_viewer.scss b/src/style/pages/_viewer.scss index 8294313..04abbf4 100644 --- a/src/style/pages/_viewer.scss +++ b/src/style/pages/_viewer.scss @@ -3,6 +3,7 @@ position: relative; height: 100vh; min-height: 560px; + padding-top: 16px; @include breakpoint (sm) { width: 100%; @@ -10,6 +11,7 @@ display: flex; flex-direction: column; justify-content: space-between; + padding-top: 24px; } @@ -18,7 +20,6 @@ */ &__top { z-index: 10; - margin-top: 16px; padding-left: 24px; padding-right: 16px; display: flex; @@ -29,7 +30,7 @@ @include breakpoint (sm) { // position: static; justify-content: center; - margin: 24px 0; + margin-bottom: 24px; } // Tip message diff --git a/src/utils/Transition.svelte b/src/utils/Transition.svelte index 4e44a45..e5a2fe4 100644 --- a/src/utils/Transition.svelte +++ b/src/utils/Transition.svelte @@ -29,7 +29,7 @@ // Scroll to page top window.scrollTo(0,0) // Run the page animation / after a tiny delay - setTimeout(() => animateIn(scope), 1) + requestAnimationFrame(() => animateIn(scope)) }, animDelayPanel) })