Last active
May 1, 2025 14:02
-
-
Save endymion1818/8119f7af21db1f62d9119581fc3a8d19 to your computer and use it in GitHub Desktop.
Revisions
-
endymion1818 revised this gist
May 1, 2025 . 2 changed files with 309 additions and 174 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,89 +1,207 @@ function getHiddenElementHeight(element) { // Save the original display style const originalDisplay = element.style.display; // Temporarily show the element element.style.display = 'block'; // Get the height const height = element.offsetHeight; // Revert to the original display style element.style.display = originalDisplay; return height; } function fadeIn(element) { let opacity = 0; element.style.opacity = opacity; element.style.display = 'block'; const interval = setInterval(() => { opacity += 0.05; if (opacity >= 1) { opacity = 1; clearInterval(interval); } element.style.opacity = opacity; }, 50); } function fadeOut(element) { let opacity = 1; element.style.opacity = opacity; const interval = setInterval(() => { opacity -= 0.05; if (opacity <= 0) { opacity = 0; element.style.display = 'none'; clearInterval(interval); } element.style.opacity = opacity; }, 50); } /** * observe changes in the height of carousel items * @param {HTMLElement} carouselElement * @returns {void} */ function observeCarouselHeightChanges(carouselElement) { if (!carouselElement) { return; } let proposedCarouselHeight = 0; // Create a ResizeObserver instance const resizeObserver = new ResizeObserver(entries => { let maxHeight = 0; for (let entry of entries) { const liElement = /** @type {HTMLElement} */ (entry.target); const children = Array.from(liElement.children); children.forEach(child => { const height = child.offsetHeight; if (height > maxHeight) { maxHeight = height; } }); } if (maxHeight > proposedCarouselHeight) { proposedCarouselHeight = maxHeight; requestAnimationFrame(() => { carouselElement.style.height = `${proposedCarouselHeight}px`; }); } }); // Select all <li> elements within the carousel const liElements = carouselElement?.querySelectorAll('li'); // Observe each <li> element liElements.forEach(li => { resizeObserver.observe(li); }); // Initial height setting carouselElement.style.height = `${proposedCarouselHeight}px`; } /** * * @param {HTMLElement} carouselElement * @returns a ✨ new carousel ✨ * @notes * * The carousel has some parameters which can be modified by adding classes to the carousel element * * 1. Autorotation speed. Add a class of `js-autoplayspeed-<number>` where `<number>` is the speed in milliseconds to adjust the default rotation speed * 3. Show indicators. Add a class of `js-show-indicators` to show indicators * 4. Show navigators. Add a class of `js-show-navigators` to show navigators * 5. Do not autorotate. Add a class of `js-no-autorotate` to prevent the carousel from rotating * */ function createCarousel( carouselElement, ) { // nope if (typeof window === 'undefined') { return; } // also nope if (!carouselElement) { return; } /** @type { HTMLCollection | null} */ const carouselItems = carouselElement.children; // Check if carousel has more than one item if (!carouselItems || carouselItems.length <= 1) { console.info("Carousel doesn't have more than one item, so it will not be activated."); return; // Exit if there's only one or no items } let autoRotationInterval = carouselElement.className.match(/js-no-autorotate/) ? null : 3_000; if (carouselElement.className.match(/js-autoplayspeed-\d+/g)) { const speedClasses = carouselElement.className.match(/js-autoplayspeed-\d+/g) if (!speedClasses) { return; } const speedClass = speedClasses[0].split("-")[2] autoRotationInterval = parseInt(speedClass); } /** @type { HTMLElement[] } */ const carouselItemsArray = Array.from(carouselItems).filter(item => item instanceof HTMLElement); // 1. set carousel to be full width of the containing area carouselElement.style.width = '100%'; // if not, use height of the tallest image carouselElement.style.display = 'block'; observeCarouselHeightChanges(carouselElement); // finally, set the carousel to be visible carouselElement.classList.add('tw-block', 'tw-relative', 'md:tw-overflow-hidden'); // define interval timer so we can clear it later let intervalInstance = null; /** * HELPER FUNCTIONS */ /** * Gets the currently active slide * @returns {HTMLElement | Element} item */ function getActiveItem() { const activeItem = carouselElement?.querySelector('[data-carousel-item-current="true"]'); if (!activeItem || !(activeItem instanceof HTMLElement)) { // @ts-ignore carouselItems is definitely defined by this point return carouselItemsArray[0]; } return activeItem; } /** * * gets the position of the item in the array * @param {HTMLElement | Element} item * @returns {number} itemIndex */ function getPositionOfItem(item) { const position = carouselItemsArray.findIndex((carouselItem) => { return carouselItem === item && carouselItem.getAttribute('data-carousel-item'); }); return position; } /** * * @param {HTMLElement| Element } carouselItem * @returns null */ function setItemAsActive(carouselItem) { carouselItem.setAttribute('data-carousel-item-current', 'true'); fadeIn(carouselItem); } /** * * @param {HTMLElement| Element } carouselItem * @returns null */ function setItemAsInactive(carouselItem) { carouselItem.setAttribute('data-carousel-item-current', 'false'); fadeOut(carouselItem); } /** @@ -94,47 +212,77 @@ function createCarousel( * @returns {void} */ function cycle() { if (!autoRotationInterval || autoRotationInterval <= 0) { return; } intervalInstance = window.setTimeout(() => { next(); }, autoRotationInterval); } function pause() { clearTimeout(intervalInstance); } /** * Clears the cycling interval * @returns {void} */ function pause() { clearInterval(intervalInstance); } /** * Slides to the next position * * @param {HTMLElement | Element} nextItem * @returns {void} */ function slideTo(nextItem) { const activeItem = getActiveItem(); if (!activeItem || !nextItem) { return; } setItemAsInactive(activeItem); setItemAsActive(nextItem); showActiveIndicator(nextItem); pause(); cycle(); } function showActiveIndicator(nextItem) { const nextItemIndex = getPositionOfItem(nextItem); const indicators = carouselElement.querySelectorAll('[data-carousel-indicator-for]'); indicators && Array.from(indicators).map((indicator, index) => { if (index === nextItemIndex) { indicator.setAttribute('aria-pressed', 'true'); } else { indicator.setAttribute('aria-pressed', 'false'); } }); } /** * Based on the currently active item it will go to the next position * @returns {void} */ function next() { const activeItem = getActiveItem(); const activeItemPosition = getPositionOfItem(activeItem) ?? 0; if (!carouselItemsArray) { return; } if (activeItemPosition === carouselItemsArray.length - 1) { // if it is the last item, set first item as next return slideTo(carouselItemsArray[0]); } const nextItem = carouselItemsArray[activeItemPosition + 1]; if (!nextItem.getAttribute('data-carousel-item')) { // if it's an indicator, set first item as next return slideTo(carouselItemsArray[0]); } slideTo(nextItem); } @@ -144,15 +292,22 @@ function createCarousel( * @returns {void} */ function prev() { if (!carouselItemsArray) return; let activeItem = getActiveItem(); if (!activeItem) { console.info('no active item'); activeItem = carouselItemsArray[0]; } const activeItemPosition = getPositionOfItem(activeItem); const prevItem = carouselItemsArray[activeItemPosition - 1]; const actualCarouselItems = carouselItemsArray.filter(item => item.getAttribute('data-carousel-item')); if (!prevItem && actualCarouselItems) { return slideTo(actualCarouselItems[actualCarouselItems.length - 1]); } slideTo(prevItem); } @@ -161,32 +316,64 @@ function createCarousel( * INIT FUNCTIONS */ /** * Create the indicators for the carousel * @returns {void} */ function createIndicators() { if (!carouselElement.classList.contains('js-show-indicators')) { return; } const indicatorContainer = ` <div class="indicator-container tw-absolute tw-bottom-4 tw-left-0 tw-right-0 tw-flex tw-justify-center tw-mb-4"> ${carouselItemsArray.map((item, index) => ` <button data-carousel-indicator-for="${index}" aria-pressed="${index === 0 ? "true" : "false"}" class="tw-w-4 tw-h-4 tw-mx-1 tw-rounded-full tw-border tw-border-primary-600 tw-transition-colors tw-duration-300 tw-ease-in-out tw-cursor-pointer hover:tw-ring-2 hover:tw-ring-primary-600 tw-bg-white aria-pressed:tw-bg-zinc-500" aria-label="Slide ${index + 1}"> </button> `).join('')} </div> `; carouselElement.insertAdjacentHTML('beforeend', indicatorContainer); const instantiatedIndicators = carouselElement.querySelectorAll('[data-carousel-indicator-for]'); const instantiatedIndicatorsArray = Array.from(instantiatedIndicators); instantiatedIndicatorsArray.map(indicator => { const clickedCarouselItem = indicator.getAttribute('data-carousel-indicator-for'); indicator?.addEventListener('click', () => { clearTimeout(intervalInstance); const carouselItem = carouselItemsArray.find((carouselItem) => carouselItem.getAttribute('data-carousel-item') === clickedCarouselItem); carouselItem && slideTo(carouselItem); instantiatedIndicators.forEach((indicator) => { indicator.setAttribute('aria-pressed', 'false'); }); indicator.setAttribute('aria-pressed', 'true'); }); }) } function createNavigators() { if (!carouselElement.classList.contains('js-show-navigators')) { return; } const navigatorPrev = ` <button class="carousel-navigate navigate-prev tw-absolute tw-left-0 tw-bottom-0 tw-top-0 tw-text-white tw-text-2xl tw-shadow-sm tw-transition-all hover:tw-scale-110" type="button"><span class="tw-block tw-rounded tw-transition-opacity tw-bg-white/20 tw-border-white/50 hover:tw-bg-zinc-400">←</span></button> `; const navigatorNext = ` <button class="carousel-navigate navigate-next tw-absolute tw-right-0 tw-bottom-0 tw-top-0 tw-text-white tw-text-2xl tw-shadow-sm tw-transition-all hover:tw-scale-110" type="button"><span class="tw-block tw-rounded tw-transition-opacity tw-bg-white/20 tw-border-white/50 hover:tw-bg-zinc-400">→</span></button> `; carouselElement.insertAdjacentHTML('beforeend', navigatorPrev); carouselElement.insertAdjacentHTML('beforeend', navigatorNext); carouselElement.querySelectorAll('.carousel-navigate')?.forEach((navigator) => { navigator.addEventListener('click', () => { navigator.classList.contains('navigate-prev') ? prev() : next(); }); }); } @@ -198,45 +385,40 @@ function createCarousel( function init() { const activeItem = getActiveItem(); if (!carouselItemsArray) { return; } // Set the active item first and make it visible if (activeItem && activeItem instanceof HTMLElement) { activeItem.classList.add('tw-absolute', 'tw-inset-0', 'tw-block'); activeItem.setAttribute('data-carousel-item-current', 'true'); activeItem.style.opacity = '1'; } else { carouselItemsArray[0].classList.add('tw-absolute', 'tw-inset-0', 'tw-block'); carouselItemsArray[0].setAttribute('data-carousel-item-current', 'true'); carouselItemsArray[0].style.opacity = '1'; } // Apply opacity changes to other items carouselItemsArray.map((item, index) => { if (item !== activeItem) { item.classList.add('tw-absolute', 'tw-inset-0', 'tw-hidden'); item.style.opacity = '0'; } item.setAttribute('data-carousel-item', `${index}`); }); } createIndicators(); createNavigators(); init(); cycle(); window.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { cycle(); } else { pause(); } }); } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,47 +0,0 @@ -
endymion1818 revised this gist
May 15, 2023 . 1 changed file with 14 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,13 @@ document.body.addEventListener('load', function () { * @param {HTMLElement} selector * @returns a ✨ new carousel ✨ */ function createCarousel( selector, autoRotationInterval = 3_000, onPrevCallback = null, onNextCallback = null, onIndicatorClickCallback = null ) { // nope if (typeof window === 'undefined') { return; @@ -88,6 +94,9 @@ function createCarousel(selector) { * @returns {void} */ function cycle() { if(autoRotationInterval <= 0) { return } intervalInstance = window.setInterval(() => { next(); }, 3_000); @@ -117,6 +126,7 @@ function createCarousel(selector) { * @returns {void} */ function next() { onNextCallback && onNextCallback(); let nextItem = null; const activeItem = getActiveItem(); const activeItemPosition = getPositionOfItem(activeItem); @@ -134,11 +144,12 @@ function createCarousel(selector) { * @returns {void} */ function prev() { onPrevCallback && onPrevCallback(); let prevItem = null; const activeItem = getActiveItem(); const activeItemPosition = getPositionOfItem(activeItem); if (activeItemPosition === 0) { prevItem = carouselItems[carouselItems.length -1]; } else { prevItem = carouselItems[activeItemPosition - 1]; @@ -173,6 +184,7 @@ function createCarousel(selector) { indicator.querySelector('svg').classList.remove('tw-text-white'); } indicator.addEventListener('click', () => { onIndicatorClickCallback && onIndicatorClickCallback(); slideTo(item); }); -
endymion1818 revised this gist
May 12, 2023 . 2 changed files with 226 additions and 208 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,31 +1,164 @@ document.body.addEventListener('load', function () { /** * * @param {HTMLElement} selector * @returns a ✨ new carousel ✨ */ function createCarousel(selector) { // nope if (typeof window === 'undefined') { return; } // also nope if(!selector) { return; } // get some things we're going to need later const carouselItems = selector.querySelectorAll('[data-carousel-item]'); const carouselItemsArray = Array.from(carouselItems); const indicatorTemplate = selector.querySelector('#carousel-indicator'); // define interval timer so we can clear it later let intervalInstance = null; /** * HELPER FUNCTIONS */ /** * Gets the currently active slide * @returns {HTMLElement} item */ function getActiveItem() { return selector.querySelector('[data-carousel-item="active"]'); } /** * * gets the position of the item in the array * @param {HTMLElement} item * @returns {HTMLElement} item */ function getPositionOfItem(item) { return carouselItemsArray.findIndex((carouselItem) => { return carouselItem === item; }); } /** * * Sets the carousel to the next slide * @param {HTMLElement} item * @returns {void} */ function setItemAsActive(item) { item.setAttribute('data-carousel-item', 'active'); item.classList.remove('tw-opacity-0'); item.classList.add('tw-opacity-100'); // update the indicators if available const currentItemIndex = getPositionOfItem(item); const indicators = selector.querySelectorAll('[data-carousel-indicator]'); indicators.length > 0 && Array.from(indicators).map((indicator, index) => { if (index === currentItemIndex) { indicator.setAttribute('aria-current', 'true'); indicator.querySelector('svg').classList.add('tw-text-primary-600'); indicator.querySelector('svg').classList.remove('tw-text-white'); } else { indicator.querySelector('svg').classList.add('tw-text-white'); indicator.setAttribute('aria-current', 'false'); indicator.querySelector('svg').classList.remove('tw-text-primary-600'); indicator.querySelector('svg').classList.add('tw-text-white'); } }); } /** * * @param {HTMLElement} item * @returns null */ function setItemAsInactive(item) { item.setAttribute('data-carousel-item', ''); item.classList.add('tw-opacity-0'); item.classList.remove('tw-opacity-100'); } /** * ACTIONS */ /** * Set an interval to cycle through the carousel items * @returns {void} */ function cycle() { intervalInstance = window.setInterval(() => { next(); }, 3_000); } /** * Clears the cycling interval * @returns {void} */ function pause() { clearInterval(intervalInstance); } /** * Slides to the next position * * @param {number} nextItem * @returns {void} */ function slideTo(nextItem) { const activeItem = getActiveItem(); setItemAsInactive(activeItem); setItemAsActive(nextItem); pause(); cycle(); } /** * Based on the currently active item it will go to the next position * @returns {void} */ function next() { let nextItem = null; const activeItem = getActiveItem(); const activeItemPosition = getPositionOfItem(activeItem); if (activeItemPosition === carouselItems.length - 1) { // if it is the last item, set first item as next nextItem = carouselItems[0]; } else { nextItem = carouselItems[activeItemPosition + 1]; } slideTo(nextItem); } /** * Based on the currently active item it will go to the previous position * @returns {void} */ function prev() { let prevItem = null; const activeItem = getActiveItem(); const activeItemPosition = getPositionOfItem(activeItem); if (activeItemPosition === 1) { prevItem = carouselItems[carouselItems.length -1]; } else { prevItem = carouselItems[activeItemPosition - 1]; } slideTo(prevItem); } /** * INIT FUNCTIONS */ /** * Create the indicators for the carousel * @returns {void} */ function createIndicators() { const indicatorContainer = selector.querySelector('#indicator-container'); carouselItemsArray.map((item, index) => { const indicator = indicatorTemplate.content.firstElementChild.cloneNode(true); indicator.setAttribute('data-carousel-indicator', index); indicator.setAttribute('aria-label', `Slide ${index + 1}`); @@ -44,170 +177,54 @@ document.body.addEventListener('load', function () { }); }); } /** * Function to initialise carousel * @returns {void} */ function init() { const activeItem = getActiveItem(); const items = Array.from(carouselItems) items.map(item => { item.classList.add( 'tw-absolute', 'tw-inset-0', 'tw-transition-transform', 'tw-transform' ) }); /** * if no active item is set then first position is default */ if(activeItem) { slideTo(activeItem); } else { slideTo(0) } /** * Add event listeners to the buttons if they exist */ const nextButton = selector.querySelector('[data-carousel-next]'); nextButton && nextButton.addEventListener('click', () => { next(); }); const prevButton = selector.querySelector('[data-carousel-prev]'); prevButton && prevButton.addEventListener('click', () => { prev(); }); } // initialise the carousel init(); // if we have an indicator template, create the indicators indicatorTemplate && createIndicators(); }; /** * Initialise carousels */ const allCarousels = document.querySelectorAll('[data-carousel]') allCarousels.forEach((carouselElement) => { createCarousel(carouselElement); }); } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,46 +1,47 @@ <div id="animation-carousel" class="tw-relative tw-w-full tw-h-36 md:tw-h-96 tw-my-8" data-carousel> <!-- Carousel wrapper --> <div class="tw-relative tw-overflow-hidden tw-rounded-lg tw-h-36 md:tw-h-96"> <!-- Carousel indicators, if desired --> <div id="indicator-container" class="tw-absolute tw-z-30 tw-flex tw-space-x-3 tw--translate-x-1/2 tw-bottom-5 tw-left-1/2"> <template id="carousel-indicator"> <button type="button" class="tw-w-3 tw-h-3 tw-rounded-full tw-border tw-border-white" aria-current="false" aria-label="Slide 1" data-carousel-slide-to="0"> <svg viewBox="0 0 100 100" class="tw-text-white" xmlns="http://www.w3.org/2000/svg"> <circle cx="50" cy="50" r="50" fill="currentColor" stroke="white" stroke-width="3"/> </svg> </button> </template> </div> <!-- Items --> <div class="tw-opacity-0 tw-transition tw-duration-150 tw-ease-in-out tw-h-36 md:tw-h-96" data-carousel-item="active"> <img src="https://placehold.co/600x400/orange/blue" alt="..." class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-36 md:tw-h-96"> <div class="tw-absolute tw-inset-0 tw-flex"> <blockquote class="tw-m-auto tw-z-40 tw-font-bold tw-text-4xl tw-max-w-xl tw-text-center tw-text-zinc-50"> The <span class="tw-text-primary-400">more I learn</span>, the more I realise how much I don't know <p class="tw-mt-6 tw-text-sm tw-font-normal tw-italic"> Albert Einstein </p> </blockquote> </div> </div> <div class="tw-opacity-0 tw-transition tw-duration-150 tw-ease-in-out tw-h-36 md:tw-h-96" data-carousel-item> <img src="https://placehold.co/600x400/orange/white" alt="..." class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-36 md:tw-h-96"> </div> <div class="tw-opacity-0 tw-transition tw-duration-150 tw-ease-in-out tw-h-36 md:tw-h-96" data-carousel-item> <img src="https://placehold.co/600x400/red/white" alt="..." class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-36 md:tw-h-96"> </div> </div> <!-- Slider controls, if desired --> <button type="button" class="tw-absolute tw-top-0 tw-left-0 tw-z-30 tw-flex tw-items-center tw-justify-center tw-h-full tw-px-4 tw-cursor-pointer tw-group focus:tw-outline-none" data-carousel-prev> <span class="tw-inline-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-full sm:tw-w-10 sm:tw-h-10 tw-bg-white/30 tw-dark:bg-gray-800/30 group-hover:tw-bg-white/50 dark:group-hover:tw-bg-gray-800/60 group-focus:tw-ring-4 group-focus:tw-ring-white dark:group-focus:tw-ring-gray-800/70 group-focus:tw-outline-none tw-transition-all tw-ease-in-out"> <svg aria-hidden="true" class="tw-w-5 tw-h-5 tw-text-zinc-400 hover:tw-text-zinc-800 sm:tw-w-6 sm:tw-h-6 tw-dark:text-gray-800 tw-transition-all tw-ease-in-out" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <span class="tw-sr-only">Previous</span> </span> </button> <button type="button" class="tw-absolute tw-top-0 tw-right-0 tw-z-30 tw-flex tw-items-center tw-justify-center tw-h-full tw-px-4 tw-cursor-pointer tw-group focus:tw-outline-none" data-carousel-next> <span class="tw-inline-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-full sm:tw-w-10 sm:tw-h-10 bg-white/30 dark:tw-bg-gray-800/30 group-hover:tw-bg-white/50 dark:tw-group-hover:tw-bg-gray-800/60 group-focus:tw-ring-4 group-focus:tw-ring-white dark:tw-group-focus:tw-ring-gray-800/70 group-focus:tw-outline-none tw-transition-all tw-ease-in-out"> <svg aria-hidden="true" class="tw-w-5 tw-h-5 tw-text-zinc-400 hover:tw-text-zinc-800 sm:tw-w-6 sm:tw-h-6 dark:tw-text-gray-800 tw-transition-all tw-ease-in-out" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg> <span class="tw-sr-only">Next</span> </span> </button> </div> -
endymion1818 revised this gist
May 12, 2023 . 1 changed file with 0 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -41,8 +41,6 @@ document.body.addEventListener('load', function () { } indicator.addEventListener('click', () => { slideTo(item); }); }); -
endymion1818 revised this gist
May 12, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,7 +40,7 @@ document.body.addEventListener('load', function () { indicator.querySelector('svg').classList.remove('tw-text-white'); } indicator.addEventListener('click', () => { slideTo(item); indicator.setAttribute('aria-current', 'true'); indicator.classList.add('tw-text-primary-800'); -
endymion1818 revised this gist
May 12, 2023 . 1 changed file with 6 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -198,7 +198,13 @@ document.body.addEventListener('load', function () { prev(); }) } /** * Initialise carousels */ init(); /** * Allow public access to some lifecycle events */ return { init, cycle, -
endymion1818 created this gist
May 12, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,209 @@ document.body.addEventListener('load', function () { /** * Initialise carousels */ const allCarousels = document.querySelectorAll('[data-carousel]') allCarousels.forEach((carouselElement) => { createCarousel(carouselElement); }); /** * Let's Go! */ function createCarousel(selector) { if (typeof window === 'undefined') { return; } if(!selector) { return; } const carouselItems = selector.querySelectorAll('[data-carousel-item]'); const carouselItemsArray = Array.from(carouselItems); // init indicators const indicatorTemplate = selector.querySelector('.carousel-indicator'); const indicatorContainer = selector.querySelector('#indicator-container'); carouselItemsArray.map((item, index) => { const indicator = indicatorTemplate.cloneNode(true); indicator.setAttribute('data-carousel-indicator', index); indicator.setAttribute('aria-label', `Slide ${index + 1}`); indicatorContainer.appendChild(indicator); const activeItem = getActiveItem(); const activeItemIndex = getPositionOfItem(activeItem); if(index === activeItemIndex) { indicator.setAttribute('aria-current', 'true'); indicator.querySelector('svg').classList.add('tw-text-primary-600'); indicator.querySelector('svg').classList.remove('tw-text-white'); } indicator.addEventListener('click', () => { slideTo(index); indicator.setAttribute('aria-current', 'true'); indicator.classList.add('tw-text-primary-800'); }); }); // define interval timer so we can clear it later let intervalInstance = null; /** * Gets the currently active slide * @returns {number} the index of the active item */ function getActiveItem() { return selector.querySelector('[data-carousel-item="active"]'); } /** * * @param {number} position * @returns {number} */ function getItemByPosition(position) { return carouselItemsArray[position]; } /** * * @param {HTMLElement} item * @returns {boolean} whether the item is active or not */ function getPositionOfItem(item) { return carouselItemsArray.findIndex((carouselItem) => { return carouselItem === item; }); } /** * * @param {HTMLElement} item * @returns null */ function setItemAsActive(item) { item.setAttribute('data-carousel-item', 'active'); item.classList.remove('tw-hidden'); // update the indicators if available const currentItemIndex = getPositionOfItem(item); const indicators = selector.querySelectorAll('[data-carousel-indicator]'); Array.from(indicators).map((indicator, index) => { if (index === currentItemIndex) { indicator.setAttribute('aria-current', 'true'); indicator.querySelector('svg').classList.add('tw-text-primary-600'); indicator.querySelector('svg').classList.remove('tw-text-white'); } else { indicator.querySelector('svg').classList.add('tw-text-white'); indicator.setAttribute('aria-current', 'false'); indicator.querySelector('svg').classList.remove('tw-text-primary-600'); indicator.querySelector('svg').classList.add('tw-text-white'); } }); } /** * * @param {Item} item * @returns null */ function setItemAsInactive(item) { item.setAttribute('data-carousel-item', ''); item.classList.add('tw-hidden'); } /** * Set an interval to cycle through the carousel items */ function cycle() { intervalInstance = window.setInterval(() => { next(); }, 3_000); } /** * Clears the cycling interval */ function pause() { clearInterval(intervalInstance); } /** * Slides to the next position * * @param {number} nextItem * @returns null */ function slideTo(nextItem) { const activeItem = getActiveItem(); setItemAsInactive(activeItem); setItemAsActive(nextItem); pause(); cycle(); } /** * Based on the currently active item it will go to the next position */ function next() { let nextItem = null; const activeItem = getActiveItem(); const activeItemPosition = getPositionOfItem(activeItem); if (activeItemPosition === carouselItems.length - 1) { // if it is the last item, set first item as next nextItem = carouselItems[0]; } else { nextItem = carouselItems[activeItemPosition + 1]; } slideTo(nextItem); } /** * Based on the currently active item it will go to the previous position */ function prev() { let prevItem = null; const activeItem = getActiveItem(); const activeItemPosition = getPositionOfItem(activeItem); console.log({activeItemPosition}); if (activeItemPosition === 1) { prevItem = carouselItems[carouselItems.length -1]; } else { prevItem = carouselItems[activeItemPosition - 1]; } slideTo(prevItem); } /** * Initialise carousel */ function init() { const activeItem = getActiveItem(); const items = Array.from(carouselItems) items.map(item => { item.classList.add( 'tw-absolute', 'tw-inset-0', 'tw-transition-transform', 'tw-transform' ) }); // if no active item is set then first position is default if(activeItem) { slideTo(activeItem); } else { slideTo(0) } // prev & next buttons const prevButton = selector.querySelector('[data-carousel-prev]'); const nextButton = selector.querySelector('[data-carousel-next]'); nextButton.addEventListener('click', () => { next(); }); prevButton.addEventListener('click', () => { prev(); }) } init(); return { init, cycle, next, prev } }; } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,46 @@ <div id="animation-carousel" class="tw-relative tw-w-full tw-h-96 md:tw-h-[36rem]" data-carousel="static"> <!-- Carousel wrapper --> <div class="tw-relative tw-overflow-hidden tw-rounded-lg tw-h-96 md:tw-h-[36rem]"> <!-- Carousel indicators --> <div id="indicator-container" class="tw-absolute tw-z-30 tw-flex tw-space-x-3 tw--translate-x-1/2 tw-bottom-5 tw-left-1/2"> <button type="button" class="carousel-indicator tw-w-3 tw-h-3 tw-rounded-full tw-border tw-border-white" aria-current="false" aria-label="Slide 1" data-carousel-slide-to="0"> <svg viewBox="0 0 100 100" class="tw-text-white" xmlns="http://www.w3.org/2000/svg"> <circle cx="50" cy="50" r="50" fill="currentColor" stroke="white" stroke-width="3"/> </svg> </button> </div> <!-- Item 1 --> <div class="tw-hidden tw-duration-200 tw-ease-linear tw-h-96 md:tw-h-[36rem]" data-carousel-item> <img src="https://placehold.co/600x400/orange/white" class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-96 md:tw-h-[36rem]" alt="..."> </div> <!-- Item 2 --> <div class="tw-hidden tw-duration-200 tw-ease-linear tw-h-96 md:tw-h-[36rem]" data-carousel-item> <img src="https://placehold.co/600x400/red/white" class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-96 md:tw-h-[36rem]" alt="..."> </div> <!-- Item 3 --> <div class="tw-hidden tw-duration-200 tw-ease-linear tw-h-96 md:tw-h-[36rem]" data-carousel-item="active"> <img src="https://placehold.co/600x400/blue/teal" class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-96 md:tw-h-[36rem]" alt="..."> </div> <!-- Item 4 --> <div class="tw-hidden tw-duration-200 tw-ease-linear tw-h-96 md:tw-h-[36rem]" data-carousel-item> <img src="https://placehold.co/600x400/green/purple" class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-96 md:tw-h-[36rem]" alt="..."> </div> <!-- Item 5 --> <div class="tw-hidden tw-duration-200 tw-ease-linear tw-h-96 md:tw-h-[36rem]" data-carousel-item> <img src="https://placehold.co/600x400/purple/red" class="tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-96 md:tw-h-[36rem]" alt="..."> </div> </div> <!-- Slider controls --> <button type="button" class="tw-absolute tw-top-0 tw-left-0 tw-z-30 tw-flex tw-items-center tw-justify-center tw-h-full tw-px-4 tw-cursor-pointer tw-group focus:tw-outline-none" data-carousel-prev> <span class="tw-inline-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-full sm:tw-w-10 sm:tw-h-10 tw-bg-white/30 tw-dark:bg-gray-800/30 group-hover:tw-bg-white/50 dark:group-hover:tw-bg-gray-800/60 group-focus:tw-ring-4 group-focus:tw-ring-white dark:group-focus:tw-ring-gray-800/70 group-focus:tw-outline-none"> <svg aria-hidden="true" class="tw-w-5 tw-h-5 tw-text-white sm:tw-w-6 sm:tw-h-6 tw-dark:text-gray-800" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg> <span class="tw-sr-only">Previous</span> </span> </button> <button type="button" class="tw-absolute tw-top-0 tw-right-0 tw-z-30 tw-flex tw-items-center tw-justify-center tw-h-full tw-px-4 tw-cursor-pointer tw-group focus:tw-outline-none" data-carousel-next> <span class="tw-inline-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-full sm:tw-w-10 sm:tw-h-10 bg-white/30 dark:tw-bg-gray-800/30 group-hover:tw-bg-white/50 dark:tw-group-hover:tw-bg-gray-800/60 group-focus:tw-ring-4 group-focus:tw-ring-white dark:tw-group-focus:tw-ring-gray-800/70 group-focus:tw-outline-none"> <svg aria-hidden="true" class="tw-w-5 tw-h-5 tw-text-white sm:tw-w-6 sm:tw-h-6 dark:tw-text-gray-800" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg> <span class="tw-sr-only">Next</span> </span> </button> </div>