diff --git a/assets/base.css b/assets/base.css index 8342607a524..525c03a6f7d 100644 --- a/assets/base.css +++ b/assets/base.css @@ -584,6 +584,7 @@ details > * { :root { --duration-short: 100ms; --duration-default: 200ms; + --duration-announcement-bar: 250ms; --duration-medium: 300ms; --duration-long: 500ms; --duration-extra-long: 600ms; @@ -2171,6 +2172,11 @@ product-info .loading-overlay:not(.hidden) ~ *, .announcement-bar .slider--everywhere { margin-bottom: 0; + scroll-behavior: auto; +} + +.utility-bar__grid .announcement-bar-slider { + width: 100%; } .utility-bar__grid .announcement-bar-slider { @@ -2253,6 +2259,51 @@ product-info .loading-overlay:not(.hidden) ~ *, letter-spacing: 0.1rem; } +.announcement-bar-slider--fade-in-next .announcement-bar__message, +.announcement-bar-slider--fade-in-previous .announcement-bar__message, +.announcement-bar-slider--fade-out-next .announcement-bar__message, +.announcement-bar-slider--fade-out-previous .announcement-bar__message { + animation-duration: var(--duration-announcement-bar); + animation-timing-function: ease-in-out; + animation-fill-mode: forwards; +} + +.announcement-bar-slider--fade-in-next .announcement-bar__message { + --announcement-translate-from: -1.5rem; + /* Prevent flicker */ + opacity: 0; + animation-name: translateAnnouncementSlideIn; + animation-delay: var(--duration-announcement-bar); +} + +.announcement-bar-slider--fade-in-previous .announcement-bar__message { + --announcement-translate-from: 1.5rem; + /* Prevent flicker */ + opacity: 0; + animation-name: translateAnnouncementSlideIn; + animation-delay: var(--duration-announcement-bar); +} + +.announcement-bar-slider--fade-out-next .announcement-bar__message { + --announcement-translate-to: 1.5rem; + animation-name: translateAnnouncementSlideOut; +} + +.announcement-bar-slider--fade-out-previous .announcement-bar__message { + --announcement-translate-to: -1.5rem; + animation-name: translateAnnouncementSlideOut; +} + +@keyframes translateAnnouncementSlideIn { + 0% {opacity: 0; transform: translateX(var(--announcement-translate-from))} + 100% {opacity: 1; transform: translateX(0)} +} + +@keyframes translateAnnouncementSlideOut { + 0% {opacity: 1; transform: translateX(0)} + 100% {opacity: 0; transform: translateX(var(--announcement-translate-to))} +} + /* section-header */ .section-header.shopify-section-group-header-group { z-index: 3; diff --git a/assets/global.js b/assets/global.js index 7e5b4b743eb..2023d91e01d 100644 --- a/assets/global.js +++ b/assets/global.js @@ -700,8 +700,12 @@ class SliderComponent extends HTMLElement { event.currentTarget.name === 'next' ? this.slider.scrollLeft + step * this.sliderItemOffset : this.slider.scrollLeft - step * this.sliderItemOffset; + this.setSlidePosition(this.slideScrollPosition); + } + + setSlidePosition(position) { this.slider.scrollTo({ - left: this.slideScrollPosition, + left: position, }); } } @@ -719,12 +723,16 @@ class SlideshowComponent extends SliderComponent { this.sliderFirstItemNode = this.slider.querySelector('.slideshow__slide'); if (this.sliderItemsToShow.length > 0) this.currentPage = 1; + this.announcementBarSlider = this.querySelector('.announcement-bar-slider'); + // Value below should match --duration-announcement-bar CSS value + this.announcerBarAnimationDelay = this.announcementBarSlider ? 250 : 0; + this.sliderControlLinksArray = Array.from(this.sliderControlWrapper.querySelectorAll('.slider-counter__link')); this.sliderControlLinksArray.forEach((link) => link.addEventListener('click', this.linkToSlide.bind(this))); this.slider.addEventListener('scroll', this.setSlideVisibility.bind(this)); this.setSlideVisibility(); - if (this.querySelector('.announcement-bar-slider')) { + if (this.announcementBarSlider) { this.announcementBarArrowButtonWasClicked = false; this.desktopLayout = window.matchMedia('(min-width: 750px)'); @@ -771,10 +779,15 @@ class SlideshowComponent extends SliderComponent { onButtonClick(event) { super.onButtonClick(event); + this.wasClicked = true; + const isFirstSlide = this.currentPage === 1; const isLastSlide = this.currentPage === this.sliderItemsToShow.length; - if (!isFirstSlide && !isLastSlide) return; + if (!isFirstSlide && !isLastSlide) { + this.applyAnimationToAnnouncementBar(event.currentTarget.name); + return; + } if (isFirstSlide && event.currentTarget.name === 'previous') { this.slideScrollPosition = @@ -782,9 +795,19 @@ class SlideshowComponent extends SliderComponent { } else if (isLastSlide && event.currentTarget.name === 'next') { this.slideScrollPosition = 0; } - this.slider.scrollTo({ - left: this.slideScrollPosition, - }); + + this.setSlidePosition(this.slideScrollPosition); + + this.applyAnimationToAnnouncementBar(event.currentTarget.name); + } + + setSlidePosition(position) { + if (this.setPositionTimeout) clearTimeout(this.setPositionTimeout); + this.setPositionTimeout = setTimeout (() => { + this.slider.scrollTo({ + left: position, + }); + }, this.announcerBarAnimationDelay); } update() { @@ -832,7 +855,7 @@ class SlideshowComponent extends SliderComponent { } else if (this.autoplayButtonIsSetToPlay) { this.pause(); } - } else if (this.querySelector('.announcement-bar-slider').contains(event.target)) { + } else if (this.announcementBarSlider.contains(event.target)) { this.pause(); } } @@ -862,13 +885,13 @@ class SlideshowComponent extends SliderComponent { const slideScrollPosition = this.currentPage === this.sliderItems.length ? 0 - : this.slider.scrollLeft + this.slider.querySelector('.slideshow__slide').clientWidth; - this.slider.scrollTo({ - left: slideScrollPosition, - }); + : this.slider.scrollLeft + this.sliderItemOffset; + + this.setSlidePosition(slideScrollPosition); + this.applyAnimationToAnnouncementBar(); } - setSlideVisibility() { + setSlideVisibility(event) { this.sliderItemsToShow.forEach((item, index) => { const linkElements = item.querySelectorAll('a'); if (index === this.currentPage - 1) { @@ -887,6 +910,38 @@ class SlideshowComponent extends SliderComponent { item.setAttribute('tabindex', '-1'); } }); + this.wasClicked = false; + } + + applyAnimationToAnnouncementBar(button = 'next') { + if (!this.announcementBarSlider) return; + + const itemsCount = this.sliderItems.length; + const increment = button === 'next' ? 1 : -1; + + const currentIndex = this.currentPage - 1; + let nextIndex = (currentIndex + increment) % itemsCount; + nextIndex = nextIndex === -1 ? itemsCount - 1 : nextIndex; + + const nextSlide = this.sliderItems[nextIndex]; + const currentSlide = this.sliderItems[currentIndex]; + + const animationClassIn = 'announcement-bar-slider--fade-in'; + const animationClassOut = 'announcement-bar-slider--fade-out'; + + const isFirstSlide = currentIndex === 0; + const isLastSlide = currentIndex === itemsCount - 1; + + const shouldMoveNext = (button === 'next' && !isLastSlide) || (button === 'previous' && isFirstSlide); + const direction = shouldMoveNext ? 'next' : 'previous'; + + currentSlide.classList.add(`${animationClassOut}-${direction}`); + nextSlide.classList.add(`${animationClassIn}-${direction}`); + + setTimeout(() => { + currentSlide.classList.remove(`${animationClassOut}-${direction}`); + nextSlide.classList.remove(`${animationClassIn}-${direction}`); + }, this.announcerBarAnimationDelay * 2); } linkToSlide(event) {