diff --git a/snippets/horizontal-gallery.md b/snippets/horizontal-gallery.md new file mode 100644 index 000000000..bcc3d22cd --- /dev/null +++ b/snippets/horizontal-gallery.md @@ -0,0 +1,123 @@ +--- +title: Image gallery with horizontal scroll +tags: visual,interactivity +expertise: advanced +firstSeen: 2022-05-01T05:00:00-04:00 +--- + +Creates a horizontally scrollable image gallery. + +- Position the `.thumbnails` at the bottom of the container using `position: absolute`. +- Use `scroll-snap-type: x mandatory` and `overscroll-behavior-x: contain` to create a snap effect on horizontal scroll. Snap elements to the start of the container using `scroll-snap-align: start`. +- Hide scrollbars using `scrollbar-width: none` and styling the pseudo-element `::-webkit-scrollbar` to `display: none`. +- Use `Element.scrollTo()` to define a `scrollToElement` function, that scrolls the gallery to the given item. +- Use `Array.prototype.map()` and `Array.prototype.join()` to populate the `.thumbnails` element. Give each thumbnail a `data-id` attribute with the index of the image. +- Use `Document.querySelectorAll()` to get all the thumbnail elements. Use `Array.prototype.forEach()` to register a handler for the `'click'` event on each thumbnail, using `EventTarget.addEventListener()` and the `scrollToElement` function. +- Use `Document.querySelector()` and `EventTarget.addEventListener()` to register a handler for the `'scroll'` event. Update the `.thumbnails` element to match the current scroll position, using the `highlightThumbnail` function. + +```html + +``` + +```css +.gallery-container { + position: relative; + display: flex; + justify-content: center; +} + +.thumbnails { + position: absolute; + bottom: 8px; + display: flex; + flex-direction: row; + gap: 6px; +} + +.thumbnails div { + width: 8px; + height: 8px; + cursor: pointer; + background: #aaa; + border-radius: 100%; +} + +.thumbnails div.highlighted { + background-color: #777; +} + +.slides { + margin: 0 16px; + display: grid; + grid-auto-flow: column; + gap: 1rem; + width: 540px; + padding: 0 0.25rem; + height: 720px; + overflow-y: auto; + overscroll-behavior-x: contain; + scroll-snap-type: x mandatory; + scrollbar-width: none; +} + +.slides > div { + scroll-snap-align: start; +} + +.slides img { + width: 540px; + object-fit: contain; +} + +.slides::-webkit-scrollbar { + display: none; +} +``` + +```js +const slideGallery = document.querySelector('.slides'); +const slides = slideGallery.querySelectorAll('div'); +const thumbnailContainer = document.querySelector('.thumbnails'); +const slideCount = slides.length; +const slideWidth = 540; + +const highlightThumbnail = () => { + thumbnailContainer + .querySelectorAll('div.highlighted') + .forEach(el => el.classList.remove('highlighted')); + const index = Math.floor(slideGallery.scrollLeft / slideWidth); + thumbnailContainer + .querySelector(`div[data-id="${index}"]`) + .classList.add('highlighted'); +}; + +const scrollToElement = el => { + const index = parseInt(el.dataset.id, 10); + slideGallery.scrollTo(index * slideWidth, 0); +}; + +thumbnailContainer.innerHTML += [...slides] + .map((slide, i) => `
`) + .join(''); + +thumbnailContainer.querySelectorAll('div').forEach(el => { + el.addEventListener('click', () => scrollToElement(el)); +}); + +slideGallery.addEventListener('scroll', e => highlightThumbnail()); + +highlightThumbnail(); +``` diff --git a/snippets/vertical-gallery.md b/snippets/vertical-gallery.md new file mode 100644 index 000000000..b23eaf04e --- /dev/null +++ b/snippets/vertical-gallery.md @@ -0,0 +1,131 @@ +--- +title: Image gallery with vertical scroll +tags: visual,interactivity +expertise: advanced +firstSeen: 2022-05-05T05:00:00-04:00 +--- + +Creates a horizontally scrollable image gallery. + +- Use `display: flex` and `justify-content: center` to setup the layout for the container. +- Use `display: flex` and `flex-direction: column` to setup the layout for the slides. +- Use `scroll-snap-type: y mandatory` and `overscroll-behavior-y: contain` to create a snap effect on vertical scroll. Snap elements to the start of the container using `scroll-snap-align: start`. +- Hide scrollbars using `scrollbar-width: none` and styling the pseudo-element `::-webkit-scrollbar` to `display: none`. +- Use `Element.scrollTo()` to define a `scrollToElement` function, that scrolls the gallery to the given item. +- Use `Array.prototype.map()` and `Array.prototype.join()` to populate the `.thumbnails` element. Give each thumbnail a `data-id` attribute with the index of the image. +- Use `Document.querySelectorAll()` to get all the thumbnail elements. Use `Array.prototype.forEach()` to register a handler for the `'click'` event on each thumbnail, using `EventTarget.addEventListener()` and the `scrollToElement` function. +- Use `Document.querySelector()` and `EventTarget.addEventListener()` to register a handler for the `'scroll'` event. Update the `.thumbnails` and `.scrollbar` elements to match the current scroll position, using the `scrollThumb` function. + +```html + +``` + +```css +.gallery-container { + display: flex; + justify-content: center; +} + +.thumbnails { + display: flex; + flex-direction: column; + gap: 8px; +} + +.thumbnails img { + width: 40px; + height: 40px; + cursor: pointer; +} + +.scrollbar { + width: 1px; + height: 720px; + background: #ccc; + display: block; + margin: 0 0 0 8px; +} + +.thumb { + width: 1px; + position: absolute; + height: 0; + background: #000; +} + +.slides { + margin: 0 16px; + display: grid; + grid-auto-flow: row; + gap: 1rem; + width: calc(540px + 1rem); + padding: 0 0.25rem; + height: 720px; + overflow-y: auto; + overscroll-behavior-y: contain; + scroll-snap-type: y mandatory; + scrollbar-width: none; +} + +.slides > div { + scroll-snap-align: start; +} + +.slides img { + width: 540px; + object-fit: contain; +} + +.slides::-webkit-scrollbar { + display: none; +} +``` + +```js +const slideGallery = document.querySelector('.slides'); +const slides = slideGallery.querySelectorAll('div'); +const scrollbarThumb = document.querySelector('.thumb'); +const slideCount = slides.length; +const slideHeight = 720; +const marginTop = 16; + +const scrollThumb = () => { + const index = Math.floor(slideGallery.scrollTop / slideHeight); + scrollbarThumb.style.height = `${((index + 1) / slideCount) * slideHeight}px`; +}; + +const scrollToElement = el => { + const index = parseInt(el.dataset.id, 10); + slideGallery.scrollTo(0, index * slideHeight + marginTop); +}; + +document.querySelector('.thumbnails').innerHTML += [...slides] + .map( + (slide, i) => `` + ) + .join(''); + +document.querySelectorAll('.thumbnails img').forEach(el => { + el.addEventListener('click', () => scrollToElement(el)); +}); + +slideGallery.addEventListener('scroll', e => scrollThumb()); + +scrollThumb(); +```