From ec1fb625d34233e4358835965ffc5007977f8a68 Mon Sep 17 00:00:00 2001 From: Kjell Reigstad Date: Tue, 22 Nov 2022 09:28:13 -0500 Subject: [PATCH 01/19] Try scroll animations --- assets/animations.js | 15 ++++++++++++++ assets/base.css | 38 ++++++++++++++++++++++++++++++++++ config/settings_schema.json | 11 ++++++++++ layout/theme.liquid | 4 ++++ locales/en.default.schema.json | 8 +++++++ 5 files changed, 76 insertions(+) create mode 100644 assets/animations.js diff --git a/assets/animations.js b/assets/animations.js new file mode 100644 index 00000000000..52374999845 --- /dev/null +++ b/assets/animations.js @@ -0,0 +1,15 @@ +(function() { + window.addEventListener('scroll', animateIn); + animateIn(); + function animateIn() { + var elements = document.querySelectorAll('.section, .banner__box, .product__info-wrapper'); + for (var i = 0; i < elements.length; i++) { + elements[i].classList.add('animate'); + var windowHeight = window.innerHeight; + var scrollTop = elements[i].getBoundingClientRect().top; + if (scrollTop < windowHeight - 100) { + elements[i].classList.add('animate--active'); + } + } + }; +})(); diff --git a/assets/base.css b/assets/base.css index 0cc614cbd97..0dd227d6bd5 100644 --- a/assets/base.css +++ b/assets/base.css @@ -2957,3 +2957,41 @@ details-disclosure > details { outline: transparent solid 1px; } } + +/* Animations */ + +.animate { + transition: opacity 0.5s ease-in-out; + opacity: 0; +} + +.animate--active { + animation: 0.55s ease-in-out slideIn; + opacity: 1; +} + +@keyframes slideIn { + from { + transform: translateY(10vh); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +/* Omit the first section, since that'll be above by default */ +#MainContent > .animate:first-child { + opacity: 1; + animation: none; +} + +/* Respect the visitor's motion preferences. */ +@media (prefers-reduced-motion) { + .animate, + .animate--active { + opacity: 1; + animation: none; + } +} diff --git a/config/settings_schema.json b/config/settings_schema.json index afa55201696..2c5114ddf2d 100644 --- a/config/settings_schema.json +++ b/config/settings_schema.json @@ -1340,6 +1340,17 @@ } ] }, + { + "name": "t:settings_schema.motion.name", + "settings": [ + { + "type": "checkbox", + "id": "motion_toggle", + "label": "t:settings_schema.motion.settings.motion_toggle.label", + "default": false + } + ] + }, { "name": "t:settings_schema.social-media.name", "settings": [ diff --git a/layout/theme.liquid b/layout/theme.liquid index a3829a73e86..2e95f4c88cf 100644 --- a/layout/theme.liquid +++ b/layout/theme.liquid @@ -291,5 +291,9 @@ {%- if settings.predictive_search_enabled -%} {%- endif -%} + + {%- if settings.motion_toggle -%} + + {%- endif -%} diff --git a/locales/en.default.schema.json b/locales/en.default.schema.json index 83fc7a06ac6..056465aef71 100644 --- a/locales/en.default.schema.json +++ b/locales/en.default.schema.json @@ -221,6 +221,14 @@ } } }, + "motion": { + "name": "Motion", + "settings": { + "motion_toggle": { + "label": "Reveal sections on scroll" + } + } + }, "social-media": { "name": "Social media", "settings": { From d50d8143d6497cae981ab24cdce4e778a720dbeb Mon Sep 17 00:00:00 2001 From: Kjell Reigstad Date: Tue, 22 Nov 2022 10:42:44 -0500 Subject: [PATCH 02/19] Sync up transition and animation timing. --- assets/base.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/base.css b/assets/base.css index 0dd227d6bd5..e0a9991dee0 100644 --- a/assets/base.css +++ b/assets/base.css @@ -2961,7 +2961,7 @@ details-disclosure > details { /* Animations */ .animate { - transition: opacity 0.5s ease-in-out; + transition: opacity 0.55s ease-in-out; opacity: 0; } From 9fb3ac19461910a6d5ecc1814b1c10b4a39af371 Mon Sep 17 00:00:00 2001 From: Kjell Reigstad Date: Mon, 28 Nov 2022 10:37:28 -0500 Subject: [PATCH 03/19] Switch to using IntersectionObserver --- assets/animations.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/assets/animations.js b/assets/animations.js index 52374999845..aa66817bb16 100644 --- a/assets/animations.js +++ b/assets/animations.js @@ -1,15 +1,20 @@ (function() { - window.addEventListener('scroll', animateIn); - animateIn(); - function animateIn() { - var elements = document.querySelectorAll('.section, .banner__box, .product__info-wrapper'); - for (var i = 0; i < elements.length; i++) { - elements[i].classList.add('animate'); - var windowHeight = window.innerHeight; - var scrollTop = elements[i].getBoundingClientRect().top; - if (scrollTop < windowHeight - 100) { - elements[i].classList.add('animate--active'); + // Add animation classes. + function onIntersection(entries) { + for (const entry of entries) { + entry.target.classList.add('animate'); + if (entry.isIntersecting) { + entry.target.classList.add('animate--active'); } } - }; + } + + const intersectionObserver = new IntersectionObserver(onIntersection, { + thresholds: [1], + }); + const selector = '.section, .banner__box, .product__info-wrapper'; + const elementsToObserve = Array.from(document.querySelectorAll(selector)); + + // Observe intersections between the viewport and the elements. + elementsToObserve.forEach((element) => intersectionObserver.observe(element)); })(); From 4880e8fd66fa0f2979536089b9719e251ed61890 Mon Sep 17 00:00:00 2001 From: Kjell Reigstad Date: Mon, 13 Feb 2023 16:16:54 -0500 Subject: [PATCH 04/19] Stick to the end state of the animation. --- assets/base.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/base.css b/assets/base.css index 3d1a13fbcd7..59182ed4e2d 100644 --- a/assets/base.css +++ b/assets/base.css @@ -3002,7 +3002,7 @@ details-disclosure > details { } .animate--active { - animation: 0.55s ease-in-out slideIn; + animation: 0.55s ease-in-out slideIn forwards; opacity: 1; } From 3d1bbb20fdbeed872a4800539ed03c0bb511cabd Mon Sep 17 00:00:00 2001 From: Kjell Reigstad Date: Tue, 14 Feb 2023 10:22:07 -0500 Subject: [PATCH 05/19] Revise animation appproach. --- assets/animations.js | 43 ++++++++++++++-------- assets/base.css | 57 ++++++++++++++++++++++------- layout/theme.liquid | 7 ++-- sections/collage.liquid | 4 +- sections/contact-form.liquid | 2 +- sections/featured-collection.liquid | 2 +- sections/footer.liquid | 6 +-- sections/image-banner.liquid | 2 +- sections/image-with-text.liquid | 2 +- sections/main-product.liquid | 2 +- sections/multicolumn.liquid | 4 +- sections/rich-text.liquid | 2 +- sections/video.liquid | 2 +- snippets/article-card.liquid | 1 + snippets/card-collection.liquid | 1 + snippets/card-product.liquid | 1 + 16 files changed, 90 insertions(+), 48 deletions(-) diff --git a/assets/animations.js b/assets/animations.js index aa66817bb16..229b6fc2aa2 100644 --- a/assets/animations.js +++ b/assets/animations.js @@ -1,20 +1,31 @@ -(function() { - // Add animation classes. - function onIntersection(entries) { - for (const entry of entries) { - entry.target.classList.add('animate'); - if (entry.isIntersecting) { - entry.target.classList.add('animate--active'); - } +const SCROLL_TRIGGER_CLASSNAME = "scroll-trigger"; +const IN_VIEW_CLASSNAME = "scrolled-into-view"; + +const OPTIONS = { + threshold: 0.5, +}; + +function onIntersection(entries, observer) { + for (const entry of entries) { + if (entry.isIntersecting) { + entry.target.classList.add(IN_VIEW_CLASSNAME); + observer.unobserve(entry.target); } } +} + +function initializeScrollTrigger() { + const scrollTriggerElements = Array.from( + document.getElementsByClassName(SCROLL_TRIGGER_CLASSNAME) + ); + + if (scrollTriggerElements.length === 0) { + return; + } + + const observer = new IntersectionObserver(onIntersection, OPTIONS); - const intersectionObserver = new IntersectionObserver(onIntersection, { - thresholds: [1], - }); - const selector = '.section, .banner__box, .product__info-wrapper'; - const elementsToObserve = Array.from(document.querySelectorAll(selector)); + scrollTriggerElements.forEach((element) => observer.observe(element)); +} - // Observe intersections between the viewport and the elements. - elementsToObserve.forEach((element) => intersectionObserver.observe(element)); -})(); +window.addEventListener("load", initializeScrollTrigger); diff --git a/assets/base.css b/assets/base.css index 59182ed4e2d..5abb2751865 100644 --- a/assets/base.css +++ b/assets/base.css @@ -753,12 +753,12 @@ h3 .icon-arrow, } /* arrow animation */ -.animate-arrow .icon-arrow path { +.scroll-trigger-arrow .icon-arrow path { transform: translateX(-0.25rem); transition: transform var(--duration-short) ease; } -.animate-arrow:hover .icon-arrow path { +.scroll-trigger-arrow:hover .icon-arrow path { transform: translateX(-0.05rem); } @@ -1501,7 +1501,7 @@ button.shopify-payment-button__button--unbranded { } details[open] > .share-button__fallback { - animation: animateMenuOpen var(--duration-default) ease; + animation:.scroll-triggerMenuOpen var(--duration-default) ease; } .share-button__button:hover { @@ -2255,7 +2255,7 @@ product-info .loading-overlay:not(.hidden) ~ *, top: 0; } -.section-header.animate { +.section-header.scroll-trigger { transition: top 0.15s ease-out; } @@ -2508,7 +2508,7 @@ menu-drawer + .header__search { details[open] > .search-modal { opacity: 1; - animation: animateMenuOpen var(--duration-default) ease; + animation:.scroll-triggerMenuOpen var(--duration-default) ease; } details[open] .modal-overlay { @@ -2618,7 +2618,7 @@ details[open] > .header__icon--menu .icon-hamburger { } details[open] > .header__submenu { - animation: animateMenuOpen var(--duration-default) ease; + animation:.scroll-triggerMenuOpen var(--duration-default) ease; animation-fill-mode: forwards; z-index: 1; } @@ -2759,7 +2759,7 @@ details-disclosure > details { position: relative; } -@keyframes animateMenuOpen { +@keyframes.scroll-triggerMenuOpen { 0% { opacity: 0; transform: translateY(-1.5rem); @@ -2996,14 +2996,43 @@ details-disclosure > details { /* Animations */ -.animate { +.scroll-trigger { transition: opacity 0.55s ease-in-out; opacity: 0; } -.animate--active { +.scrolled-into-view { animation: 0.55s ease-in-out slideIn forwards; - opacity: 1; +} + +.grid__item:nth-child(1n).scrolled-into-view, +.grid__item:nth-child(1n) .scrolled-into-view, +.collage__item:nth-child(1n) .scrolled-into-view { + animation-delay: 0.1s; +} + +.grid__item:nth-child(2n).scrolled-into-view, +.grid__item:nth-child(2n) .scrolled-into-view, +.collage__item:nth-child(2n) .scrolled-into-view { + animation-delay: 0.2s; +} + +@media screen and (min-width: 990px) { + .grid__item:nth-child(3n).scrolled-into-view, + .grid__item:nth-child(3n) .scrolled-into-view, + .collage__item:nth-child(3n) .scrolled-into-view { + animation-delay: 0.3s; + } + + .grid--4-col-desktop .grid__item:nth-child(4n).scrolled-into-view, + .grid--4-col-desktop .grid__item:nth-child(4n) .scrolled-into-view { + animation-delay: 0.4s; + } + + .grid--5-col-desktop .grid__item:nth-child(5n).scrolled-into-view, + .grid--5-col-desktop .grid__item:nth-child(5n) .scrolled-into-view { + animation-delay: 0.5s; + } } @keyframes slideIn { @@ -3017,16 +3046,16 @@ details-disclosure > details { } } -/* Omit the first section, since that'll be above by default */ -#MainContent > .animate:first-child { +/* Async loading prevents animation for Related Product cards. */ +.related-products .grid__item .scroll-trigger { opacity: 1; animation: none; } /* Respect the visitor's motion preferences. */ @media (prefers-reduced-motion) { - .animate, - .animate--active { + .scroll-trigger, + .scrolled-into-view { opacity: 1; animation: none; } diff --git a/layout/theme.liquid b/layout/theme.liquid index d823046a7d9..6a1941c404f 100644 --- a/layout/theme.liquid +++ b/layout/theme.liquid @@ -32,6 +32,9 @@ + {%- if settings.motion_toggle -%} + + {%- endif -%} {{ content_for_header }} {%- liquid @@ -293,9 +296,5 @@ {%- if settings.predictive_search_enabled -%} {%- endif -%} - - {%- if settings.motion_toggle -%} - - {%- endif -%} diff --git a/sections/collage.liquid b/sections/collage.liquid index 3ba716e0f76..0df67ad81fb 100644 --- a/sections/collage.liquid +++ b/sections/collage.liquid @@ -21,11 +21,11 @@
-

{{ section.settings.heading | escape }}

+

{{ section.settings.heading | escape }}

{%- for block in section.blocks -%}
{%- case block.type -%} diff --git a/sections/contact-form.liquid b/sections/contact-form.liquid index 372fee6df29..b99db17d716 100644 --- a/sections/contact-form.liquid +++ b/sections/contact-form.liquid @@ -14,7 +14,7 @@ } {%- endstyle -%} -
+
{%- if section.settings.heading != blank -%}

diff --git a/sections/featured-collection.liquid b/sections/featured-collection.liquid index d6bb6b6635f..46e5527cfac 100644 --- a/sections/featured-collection.liquid +++ b/sections/featured-collection.liquid @@ -50,7 +50,7 @@
{%- if section.settings.title != blank -%} -

{{ section.settings.title | escape }}

+

{{ section.settings.title | escape }}

{%- endif -%} {%- if section.settings.description != blank or section.settings.show_description diff --git a/sections/footer.liquid b/sections/footer.liquid index 2554394580a..b3fe56eee07 100644 --- a/sections/footer.liquid +++ b/sections/footer.liquid @@ -37,7 +37,7 @@ {%- endstyle -%}