diff --git a/README.md b/README.md index 9a77536..35652a7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,58 @@ # Js Carousel: + A pure Js customisable carousel, that snaps! uses CSS scroll snap on mobile. +## Usage: + +Here's a simple usage of the Carousel. Note: parent element should be wrapped by +a wrapper element. + +HTML: + +``` +
+ +
+``` + +JS: + +``` +Carousel({ + parent: ".carousel-parent", + child: ".carousel-item", +}); +``` + +CSS: + +``` +.wrapper{ + display: flex; + align-items: center; + justify-content: stretch; + width: 100%; +} + +.carousel-parent{ + display: flex; + align-items: center; +} + +.carousel-item{ + width: 150px; + height: auto; + flex: 0 0 auto; + scroll-snap-align: start; + position: relative +} +``` + ## Features: + - Supports snapping of carousel. - Supports autoplay with a specified interval. - Supports scrolling and dragging. @@ -9,6 +60,7 @@ A pure Js customisable carousel, that snaps! uses CSS scroll snap on mobile. - Alignment is alterable. ## Dependencies: + - Use Gesture: https://use-gesture.netlify.app/docs/#vanilla-javascript - Anime js: https://animejs.com/ @@ -80,7 +132,9 @@ the carousel to snap at the center of each child of the carousel. > Type: string -> Options: 'start'||'center'||'end' Default: 'start' +> Options: 'start'||'center'||'end' + +> Default: 'start' ### direction: diff --git a/package.json b/package.json index d3b76a8..f870d45 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { - "name": "browser-js-library-template", + "name": "js-carousel", "version": "1.0.0", "type": "module", - "description": "Template for production ready vanilla js library", + "description": "A pure Js customisable carousel, that snaps! uses CSS scroll snap on mobile.", "repository": { "type": "git", - "url": "git+https://github.com/timelessco/browser-js-library-template.git" + "url": "git+https://github.com/timelessco/js-carousel" }, - "homepage": "https://github.com/timelessco/browser-js-library-template#readme", + "homepage": "https://github.com/timelessco/js-carousel#readme", "bugs": { - "url": "https://github.com/timelessco/browser-js-library-template/issues" + "url": "https://github.com/timelessco/js-carousel/issues" }, "author": "Timeless (https://timeless.co/)", "license": "MIT", @@ -33,7 +33,11 @@ "library", "production", "template", - "vanilla" + "vanilla", + "carousel", + "slider", + "js-slider", + "js-carousel" ], "scripts": { "build": "vite build --config vite-lib.config.js", diff --git a/src/carousel.js b/src/carousel.js index 3e9a4e4..55ddc8c 100644 --- a/src/carousel.js +++ b/src/carousel.js @@ -57,6 +57,7 @@ function Carousel(props) { displayDots = false, selectedState = false, startIndex = 0, + whileDragEnd = () => {}, direction = "ltr", dotsHTML = ` @@ -68,8 +69,12 @@ function Carousel(props) { springConfig = `spring(1,90,20,13)`, child, alignment = "start", + customDragAction = "translate", // clickEvent = false, + minWebWidth = 700, onClicking = () => {}, + scrollOnClick = false, + initialRotateValue = 0, } = props; let { parent, selectedScrollSnapIndex = 0, autoplay = false } = props; @@ -85,8 +90,6 @@ function Carousel(props) { let scrollProgress = 0; let lastScrolledTo = getCurrentPosition(parent); const lastChild = children[children.length - 1]; - const minWebWidth = 700; - addSelectedStateClassName( selectedScrollClassName, children, @@ -114,22 +117,17 @@ function Carousel(props) { parent.removeChild(firstChild); parent.appendChild(firstChild); }, 200); - - // if (clickEvent) { - children.forEach((i, index) => { - i.addEventListener("click", () => { - if (!isDragging) { - onClicking(scrollProgress, index); - } - }); - }); - // } - - // scrolls the slider to a particular mentioned value - function scrollTo(scrollValue, animate, selectedStateValue) { + function scrollTo( + scrollValue, + animate, + selectedStateValue, + customScrollValue, + ) { + // console.log("scroll to", customDragAction, customArray[scrollValue]); + // let offsetArray = customArray?.length > 0 ? customArray : leftOffsetArray; if (window.innerWidth > minWebWidth) { if (selectedStateValue) { - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -140,23 +138,62 @@ function Carousel(props) { } if (axis === "x") { if (animate) { + // if (customDragAction !== "rotate") moveToSnapPoint( -leftOffsetArray[scrollValue], axis, parent, slidesToScroll, + customDragAction, springConfig, ); + // else } else { parent.style.transform = `translateX(${-leftOffsetArray[ scrollValue ]}px)`; } - } else - parent.style.transform = `translateY(${-leftOffsetArray[ - scrollValue - ]}px)`; - lastScrolledTo = leftOffsetArray[scrollValue]; + } else { + if ( + customDragAction === "rotate" && + typeof customScrollValue === "number" + ) { + // if (!(Math.abs(customScrollValue) > Math.abs(rotateValue))) { + // rotationIndex = rotationIndex + 1; + // } + if (animate) { + moveToSnapPoint( + initialRotateValue + -40 * scrollValue, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + 1000, + ); + } else { + parent.style.transform = `rotateZ(${ + initialRotateValue + -40 * scrollValue + }deg)`; + } + // rotateValue = customScrollValue; + } else if (animate) { + moveToSnapPoint( + -leftOffsetArray[scrollValue], + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); + } else { + console.log("init"); + parent.style.transform = `translateY(${-leftOffsetArray[ + scrollValue + ]}px) rotateZ(${initialRotateValue}deg)`; + } + lastScrolledTo = leftOffsetArray[scrollValue]; + } } else { if (axis === "x") { if (scrollValue <= leftOffsetArray.length) @@ -166,7 +203,7 @@ function Carousel(props) { lastScrolledTo = leftOffsetArray[scrollValue]; if (selectedStateValue) { - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -177,6 +214,21 @@ function Carousel(props) { } } } + // if (clickEvent) { + children.forEach((i, index) => { + i.addEventListener("click", () => { + if (scrollOnClick) { + scrollTo(index, true); + } + if (!isDragging) { + onClicking(scrollProgress, index); + } + }); + }); + // } + // let rotateValue = 0; + // let rotationIndex = 0; + // scrolls the slider to a particular mentioned value if (direction === "rtl") { reverseChildren(parent); @@ -343,6 +395,7 @@ function Carousel(props) { parent.style.transform = `translateY(${-lastChildX.offsetTop}px)`; parent.style.transition = `none 0s ease 0s`; lastChildX.style.transition = `none 0s ease 0s`; + anime({ targets: parent, translateY: @@ -417,10 +470,18 @@ function Carousel(props) { dotsArray, getclosestSliderElement(parent.scrollLeft, leftOffsetArray), ); + lastScrolledTo = getclosestSliderElement( parent.scrollLeft, leftOffsetArray, ); + // removeSelectedStateClassName(children, selectedScrollClassName); + addSelectedStateClassName( + selectedScrollClassName, + children, + leftOffsetArray.indexOf(lastScrolledTo), + selectedState, + ); scrollProgress = getScrollProgress( parent, children, @@ -446,6 +507,7 @@ function Carousel(props) { function handleWindowWidth() { removeScrollClassNames(parent); + if (window.innerWidth < minWebWidth && slidesToScroll < 2) { addScrollClassNames( axis, @@ -496,16 +558,17 @@ function Carousel(props) { if (currentIndex === undefined || currentIndex === 0) currentIndex = uniqueArray.length - 1; loopingActionPrevious(-uniqueArray[currentIndex - 1]); - - moveToSnapPoint( - -uniqueArray[currentIndex - 1], - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + -uniqueArray[currentIndex - 1], + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -525,15 +588,17 @@ function Carousel(props) { } if (axis === "x") { if (currentIndex - 1 >= 0) { - moveToSnapPoint( - -leftOffsetArray[currentIndex - 1], - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + -leftOffsetArray[currentIndex - 1], + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -561,15 +626,17 @@ function Carousel(props) { ); } } else if (currentIndex - 1 >= 0) { - moveToSnapPoint( - -leftOffsetArray[currentIndex - 1], - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + -leftOffsetArray[currentIndex - 1], + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -579,7 +646,6 @@ function Carousel(props) { ); lastScrolledTo = leftOffsetArray[currentIndex - 1]; dotsFunctionality(dotsArray, lastScrolledTo); - } } else { if (currentIndex >= 0) { @@ -589,8 +655,7 @@ function Carousel(props) { } else if (parent.scrolTop !== leftOffsetArray[currentIndex - 1]) parent.scrollTo(0, leftOffsetArray[currentIndex - 1]); lastScrolledTo = leftOffsetArray[currentIndex - 1]; - dotsFunctionality(dotsArray, lastScrolledTo); - + dotsFunctionality(dotsArray, lastScrolledTo); } addSelectedStateClassName( @@ -604,20 +669,21 @@ function Carousel(props) { let currentPosition = 0; const carouselItems = parent.children; currentPosition += carouselItems[0].offsetWidth; - - moveToSnapPoint( - currentPosition, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + currentPosition, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - children.forEach(i => { - if (i.classList.contains(selectedScrollClassName)) { - i?.classList?.remove(selectedScrollClassName); - } - }); + // children.forEach(i => { + // if (i.classList.contains(selectedScrollClassName)) { + // i?.classList?.remove(selectedScrollClassName); + // } + // }); addSelectedStateClassName( selectedScrollClassName, @@ -631,20 +697,21 @@ function Carousel(props) { const lastItem = carouselItems[carouselItems.length - 1]; parent.insertBefore(lastItem, carouselItems[0]); currentPosition -= carouselItems[0].offsetWidth; - - moveToSnapPoint( - currentPosition, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + currentPosition, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - children.forEach(i => { - if (i.classList.contains(selectedScrollClassName)) { - i?.classList?.remove(selectedScrollClassName); - } - }); + // children.forEach(i => { + // if (i.classList.contains(selectedScrollClassName)) { + // i?.classList?.remove(selectedScrollClassName); + // } + // }); addSelectedStateClassName( selectedScrollClassName, @@ -653,15 +720,13 @@ function Carousel(props) { selectedState, ); lastScrolledTo = currentPosition; - dotsFunctionality(dotsArray, lastScrolledTo); - + dotsFunctionality(dotsArray, lastScrolledTo); } } // scrolling to next slide function scrollNext(looping) { let currentIndex; - addSelectedStateClassName( selectedScrollClassName, children, @@ -688,17 +753,18 @@ function Carousel(props) { if (expLoop) { if (currentIndex === undefined) currentIndex = 0; loopingActionNext(-leftOffsetArray[currentIndex + 1], true); - - moveToSnapPoint( - -leftOffsetArray[currentIndex + 1], - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + -leftOffsetArray[currentIndex + 1], + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -707,7 +773,7 @@ function Carousel(props) { selectedState, ); lastScrolledTo = leftOffsetArray[currentIndex + 1]; - dotsFunctionality(dotsArray, lastScrolledTo); + dotsFunctionality(dotsArray, lastScrolledTo); addSelectedStateClassName( selectedScrollClassName, @@ -718,7 +784,7 @@ function Carousel(props) { } if ( parent.parentNode.clientWidth - - document.querySelector(".inner").getBoundingClientRect().right <= + parent.getBoundingClientRect().right <= 0 ) { if ( @@ -729,15 +795,17 @@ function Carousel(props) { parent.clientHeight - leftOffsetArray[currentIndex] > parent.parentNode.clientHeight) ) { - moveToSnapPoint( - -leftOffsetArray[currentIndex + 1], - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + -leftOffsetArray[currentIndex + 1], + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -746,7 +814,7 @@ function Carousel(props) { selectedState, ); lastScrolledTo = leftOffsetArray[currentIndex + 1]; - dotsFunctionality(dotsArray, lastScrolledTo); + dotsFunctionality(dotsArray, lastScrolledTo); addSelectedStateClassName( selectedScrollClassName, @@ -791,19 +859,21 @@ function Carousel(props) { const carouselItems = parent.children; currentPosition -= carouselItems[0].offsetWidth; if (window.innerWidth > minWebWidth) { - moveToSnapPoint( - currentPosition, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + currentPosition, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - children.forEach(i => { - if (i.classList.contains(selectedScrollClassName)) { - i?.classList?.remove(selectedScrollClassName); - } - }); + // children.forEach(i => { + // if (i.classList.contains(selectedScrollClassName)) { + // i?.classList?.remove(selectedScrollClassName); + // } + // }); addSelectedStateClassName( selectedScrollClassName, @@ -817,20 +887,21 @@ function Carousel(props) { currentPosition += carouselItems[0].offsetWidth; if (window.innerWidth > minWebWidth) { parent.appendChild(firstItem); - - moveToSnapPoint( - currentPosition, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + currentPosition, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - children.forEach(i => { - if (i.classList.contains(selectedScrollClassName)) { - i?.classList?.remove(selectedScrollClassName); - } - }); + // children.forEach(i => { + // if (i.classList.contains(selectedScrollClassName)) { + // i?.classList?.remove(selectedScrollClassName); + // } + // }); addSelectedStateClassName( selectedScrollClassName, @@ -863,7 +934,6 @@ function Carousel(props) { i.addEventListener("click", () => { lastScrolledTo = leftOffsetArray[index]; dotsFunctionality(dotsArray, lastScrolledTo); - addSelectedStateClassName( selectedScrollClassName, children, @@ -871,19 +941,21 @@ function Carousel(props) { selectedState, ); if (window.innerWidth > minWebWidth) { - moveToSnapPoint( - -lastScrolledTo, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + -lastScrolledTo, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - children.forEach(x => { - if (x.classList.contains(selectedScrollClassName)) { - x?.classList?.remove(selectedScrollClassName); - } - }); + // children.forEach(x => { + // if (x.classList.contains(selectedScrollClassName)) { + // x?.classList?.remove(selectedScrollClassName); + // } + // }); addSelectedStateClassName( selectedScrollClassName, @@ -934,11 +1006,10 @@ function Carousel(props) { if (!expLoop && direction === "ltr") scrollTo(startIndex); - // adding gestures like drag, scroll Gesture( parent, { - onDrag: ({ active, offset: [ox, oy], direction: [dx] }) => { + onDrag: ({ active, offset: [ox, oy], direction: [dx], delta }) => { autoplay = false; isDragging = true; scrollProgress = getScrollProgress( @@ -947,10 +1018,24 @@ function Carousel(props) { leftOffsetArray, lastScrolledTo, ); + + if (customDragAction === "rotate" && active) { + moveToSnapPoint( + `-=${Math.round(axis === "x" ? delta[0] : delta[1]) % 360}`, + axis, + parent, + slidesToScroll, + customDragAction, + function (el, i, total) { + return function (t) { + return Math.sin(t * (i + 100)) ** total; + }; + }, + ); + } + clearTimeout(timeout); - // setTimeout(() => { - // }, 200); leftOffsetArray = []; allLeftOffsets = []; const offsetValue = axis === "x" ? ox : oy; @@ -971,19 +1056,21 @@ function Carousel(props) { ) { if (active) { if (offsetValue < 10) { - moveToSnapPoint( - offsetValue, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + offsetValue, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - children.forEach(i => { - if (i.classList.contains(selectedScrollClassName)) { - i?.classList?.remove(selectedScrollClassName); - } - }); + // children.forEach(i => { + // if (i.classList.contains(selectedScrollClassName)) { + // i?.classList?.remove(selectedScrollClassName); + // } + // }); addSelectedStateClassName( selectedScrollClassName, children, @@ -1026,15 +1113,17 @@ function Carousel(props) { leftOffsetArray, ); } - moveToSnapPoint( - snapValue, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + snapValue, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -1109,7 +1198,6 @@ function Carousel(props) { } } dotsFunctionality(dotsArray, lastScrolledTo); - setTimeout(() => { whileDragging( getScrollProgress( @@ -1123,13 +1211,27 @@ function Carousel(props) { ); }, 100); }, - onWheel: ({ offset: [ox, oy], active, direction: [dx] }) => { + onWheel: ({ offset: [ox, oy], active, direction: [dx], delta }) => { scrollProgress = getScrollProgress( parent, children, leftOffsetArray, lastScrolledTo, ); + if (customDragAction === "rotate" && active) { + moveToSnapPoint( + `-=${Math.round(delta[0]) % 360}`, + axis, + parent, + slidesToScroll, + customDragAction, + function (el, i, total) { + return function (t) { + return Math.sin(t * (i + 100)) ** total; + }; + }, + ); + } const offsetValue = axis === "x" ? -ox : -oy; @@ -1151,15 +1253,17 @@ function Carousel(props) { if (dx > 0) loopingActionNext(axis === "x" ? -ox : oy); else loopingActionPrev(axis === "x" ? ox : oy); if (active) { - moveToSnapPoint( - offsetValue, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + offsetValue, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -1179,16 +1283,17 @@ function Carousel(props) { } else { snapValue = -getclosestSliderElement(-offsetValue, leftOffsetArray); } - - moveToSnapPoint( - snapValue, - axis, - parent, - slidesToScroll, - springConfig, - ); + if (customDragAction !== "rotate") + moveToSnapPoint( + snapValue, + axis, + parent, + slidesToScroll, + customDragAction, + springConfig, + ); selectedScrollSnapIndex += 1; - removeSelectedStateClassName(children, selectedScrollClassName); + // removeSelectedStateClassName(children, selectedScrollClassName); addSelectedStateClassName( selectedScrollClassName, @@ -1240,11 +1345,11 @@ function Carousel(props) { lastScrolledTo, ); handleCursor(); - whileDragging( - scrollProgress, - leftOffsetArray.indexOf(lastScrolledTo), - lastScrolledTo, - ); + // whileDragging( + // scrollProgress, + // leftOffsetArray.indexOf(lastScrolledTo), + // lastScrolledTo, + // ); if ( window.innerWidth > minWebWidth || (window.innerWidth < minWebWidth && slidesToScroll > 1) @@ -1290,22 +1395,32 @@ function Carousel(props) { }, 100); } } - setTimeout(() => { - whileDragging( - getScrollProgress( - parent, - children, - leftOffsetArray, - lastScrolledTo, - ), - leftOffsetArray.indexOf(lastScrolledTo), - lastScrolledTo, - ); - }, 100); + // setTimeout(() => { + // whileDragging( + // getScrollProgress( + // parent, + // children, + // leftOffsetArray, + // lastScrolledTo, + // ), + // leftOffsetArray.indexOf(lastScrolledTo), + // lastScrolledTo, + // ); + // }, 100); setTimeout(() => { isDragging = false; }, 200); + removeSelectedStateClassName(children, selectedScrollClassName); + addSelectedStateClassName( + selectedScrollClassName, + children, + leftOffsetArray.indexOf(lastScrolledTo), + selectedState, + ); + setTimeout(() => { + whileDragEnd(); + }, 100); }, onWheelEnd: ({ offset: [ox, oy], direction: [dx] }) => { if (axis === "y") { @@ -1391,6 +1506,20 @@ function Carousel(props) { } } + function displayOrHideArrows(carousel, leftArrow, rightArrow) { + if (!carousel.canScrollPrev()) { + document.querySelector(leftArrow).style.display = "none"; + } else { + document.querySelector(leftArrow).style.display = "block"; + } + + if (!carousel.canScrollNext()) { + document.querySelector(rightArrow).style.display = "none"; + } else { + document.querySelector(rightArrow).style.display = "block"; + } + } + const self = { scrollPrev, scrollNext, @@ -1404,6 +1533,7 @@ function Carousel(props) { containerNode, getScrollProgress, addSelectedStateClass, + displayOrHideArrows, }; return self; } diff --git a/src/helpers/actions.js b/src/helpers/actions.js index 9318f82..7a67564 100644 --- a/src/helpers/actions.js +++ b/src/helpers/actions.js @@ -37,6 +37,36 @@ export function getCurrentPosition(elem, y = false) { if (y) return matrix.m42; return matrix.m41; } +export function getCurrentRotation(elem) { + const el = elem; + const st = window.getComputedStyle(el, null); + const tr = + st.getPropertyValue("-webkit-transform") || + st.getPropertyValue("-moz-transform") || + st.getPropertyValue("-ms-transform") || + st.getPropertyValue("-o-transform") || + st.getPropertyValue("transform") || + "fail..."; + // eslint-disable-next-line no-use-before-define + let values = tr.split("(")[1]; + // eslint-disable-next-line no-use-before-define + values = values.split(")")[0]; // eslint-disable-line + // eslint-disable-next-line no-use-before-define + values = values.split(","); + const a = values[0]; + const b = values[1]; + let angle; + if (tr !== "none") { + angle = Math.round(Math.atan2(b, a) * (180 / Math.PI)); + /**/ + } else { + angle = 0; + } + + // works! + return angle; + // $('#results').append('

Rotate: ' + angle + 'deg

'); +} // gets scroll progress of slider export function getScrollProgress(elem, children) { @@ -55,7 +85,9 @@ export function moveToSnapPoint( axisValue, parent, slidesToScroll, + customDragAction, easing = springConfig, + duration = 1000, ) { if (axisValue === "x") { if (slidesToScroll === 0 && snapValue > 0) { @@ -64,21 +96,37 @@ export function moveToSnapPoint( translateX: `${0}px`, translateY: 0, easing, + duration, }); - } else { + } else if (customDragAction === "rotate") { + anime({ + targets: parent, + rotateY: `${snapValue}`, + easing, + duration, + }); + } else anime({ targets: parent, translateX: `${snapValue}px`, translateY: 0, easing, + duration, }); - } + } else if (customDragAction === "rotate") { + anime({ + targets: parent, + rotateZ: `${snapValue}`, + easing, + duration, + }); } else { anime({ targets: parent, translateX: 0, translateY: `${snapValue}px`, easing, + duration, }); } } diff --git a/vite.config.js b/vite.config.js index ac2222d..511d5b9 100644 --- a/vite.config.js +++ b/vite.config.js @@ -14,6 +14,8 @@ export default defineConfig(() => { rollupOptions: { input: { main: resolve(__dirname, "website/index.html"), + skew: resolve(__dirname, "website/skew.html"), + example: resolve(__dirname, "website/example.html"), }, }, }, diff --git a/website/example.css b/website/example.css new file mode 100644 index 0000000..18ba009 --- /dev/null +++ b/website/example.css @@ -0,0 +1,169 @@ +.flex { + display: flex; + align-items: flex-start; + justify-content: start; +} + +body { + font-family: "Albert Sans", sans-serif; + display: flex; + align-items: center; + height: 100vh; +} + +li { + list-style-type: none; + font-weight: var(--framer-font-weight, 400); + font-style: var(--framer-font-style, normal); + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; + z-index: 800; +} + +.number { + font-size: 42px; + font-weight: 700; + line-height: 1; + cursor: pointer; +} + +.counter-li:not(.selected) { + opacity: 0.3; + transition: all 0.4s ease-in-out; +} + +.counter-li.selected { + opacity: 1; + transition: all 1s ease-in-out; +} + +.para, +.heading { + position: relative; +} + +.counter-li:not(.selected) .flex-col .heading { + opacity: 0; + + transition: all 0.7s ease-in-out; + left: -20px; +} +.counter-li.selected .flex-col .heading { + opacity: 1; + transition: all 0.7s ease-in-out 0.7s; + left: 0; +} + +.counter-li:not(.selected) .flex-col .para { + opacity: 0; + transition: all 0.5s ease-in-out; + left: -26px; +} + +.counter-li.selected .flex-col .para { + opacity: 1; + transition: all 0.5s ease-in-out 0.7s; + left: 0; +} + +p { + margin: 0; +} + +.container { + /* perspective: 1000px; */ + position: relative; + height: 815px; + width: 993px; + overflow: hidden; +} + +.carousel { + /* position: absolute; */ + transform-style: preserve-3d; + transform-origin: left; + transform: rotateZ(90deg); +} + +li:nth-of-type(1) { + transform: rotateZ(-90deg); + transform-origin: left; +} + +li:nth-of-type(2) { + top: -3%; + position: absolute; + left: 21%; + transform: rotateZ(-50deg); +} + +li:nth-of-type(3) { + top: 39%; + position: absolute; + left: 37%; + transform: rotateZ(-9deg); +} + +li:nth-of-type(4) { + position: absolute; + top: 83%; + left: 27%; + transform: rotateZ(30deg); +} + +li:nth-of-type(5) { + top: 109%; + position: absolute; + left: -3%; + transform: rotateZ(70deg); +} + +li:nth-of-type(6) { + top: 106%; + position: absolute; + left: -41%; + transform: rotateZ(110deg); +} + +li:nth-of-type(7) { + top: 73%; + position: absolute; + left: -67%; + transform: rotateZ(150deg); +} + +li:nth-of-type(8) { + top: 28%; + position: absolute; + left: -70%; + transform: rotateZ(190deg); +} + +li:nth-of-type(9) { + top: -8%; + position: absolute; + left: -49%; + transform: rotateZ(230deg); +} + +.flex-col { + padding-left: 35px; + display: flex; + align-items: start; + justify-content: flex-start; + flex-direction: column; + max-width: 270px; +} + +.heading { + font-size: 20px; + font-weight: bolder; +} + +.para { + font-size: 14px; + font-weight: 200; + padding-top: 5px; + color: rgb(122, 122, 122); +} diff --git a/website/example.html b/website/example.html new file mode 100644 index 0000000..11d8b27 --- /dev/null +++ b/website/example.html @@ -0,0 +1,133 @@ + + + + + + + + Scroll snap carousel + + + + +
+ + + +
+ + + diff --git a/website/example.js b/website/example.js new file mode 100644 index 0000000..19c2195 --- /dev/null +++ b/website/example.js @@ -0,0 +1,32 @@ +import Carousel from "../src/carousel"; + +let counter = ""; +counter = Carousel({ + parent: ".carousel", + child: ".counter-li", + axis: "y", + selectedState: true, + customDragAction: "rotate", + scrollOnClick: false, + initialRotateValue: 90, + customRotateArray: [0, -40, -80, -120, -160, -200, -240, -280, -320, -360], + onClicking: (scrollProgress, index) => { + counter.scrollTo(index, true, false, -index * 40); + counter.addSelectedStateClass(index); + }, + springConfig: "easeInCirc", + dragActive: false, +}); + +// document.querySelectorAll(".counter-li").forEach((i, index) => { +// const element = i; +// const circleDivision = 360 / document.querySelectorAll(".counter-li").length; +// if (index === 0) { +// element.style.transform = `rotateX(${circleDivision}deg) translatez(${-window.innerWidth}px)`; +// } else if (index === 1) { +// element.style.transform = `rotateX(${0}deg) translatez(${-window.innerWidth}px)`; +// } else +// element.style.transform = `rotateX(${ +// (index - 1) * -circleDivision +// }deg) translatez(${-window.innerWidth}px)`; +// }); diff --git a/website/index.html b/website/index.html index b83a0b2..18ca27a 100644 --- a/website/index.html +++ b/website/index.html @@ -4,7 +4,7 @@ - + Scroll snap carousel diff --git a/website/public/Frame 837837.png b/website/public/Frame 837837.png new file mode 100644 index 0000000..0895537 Binary files /dev/null and b/website/public/Frame 837837.png differ diff --git a/website/public/Inter-V.woff2 b/website/public/Inter-V.woff2 new file mode 100644 index 0000000..5879b53 Binary files /dev/null and b/website/public/Inter-V.woff2 differ diff --git a/website/public/Rectangle 1628.png b/website/public/Rectangle 1628.png new file mode 100644 index 0000000..854c870 Binary files /dev/null and b/website/public/Rectangle 1628.png differ diff --git a/website/public/Rectangle 1629.png b/website/public/Rectangle 1629.png new file mode 100644 index 0000000..2fabfdb Binary files /dev/null and b/website/public/Rectangle 1629.png differ diff --git a/website/public/Rectangle 1630 (1).png b/website/public/Rectangle 1630 (1).png new file mode 100644 index 0000000..489c6e7 Binary files /dev/null and b/website/public/Rectangle 1630 (1).png differ diff --git a/website/public/Rectangle 1630 (2).png b/website/public/Rectangle 1630 (2).png new file mode 100644 index 0000000..e4942de Binary files /dev/null and b/website/public/Rectangle 1630 (2).png differ diff --git a/website/public/Rectangle 1630.png b/website/public/Rectangle 1630.png new file mode 100644 index 0000000..e2ffae7 Binary files /dev/null and b/website/public/Rectangle 1630.png differ diff --git a/website/public/avatar (2).png b/website/public/avatar (2).png new file mode 100644 index 0000000..2e96a21 Binary files /dev/null and b/website/public/avatar (2).png differ diff --git a/website/public/avatar (4).png b/website/public/avatar (4).png new file mode 100644 index 0000000..1f1fae0 Binary files /dev/null and b/website/public/avatar (4).png differ diff --git a/website/public/image 32 (1).png b/website/public/image 32 (1).png new file mode 100644 index 0000000..5403110 Binary files /dev/null and b/website/public/image 32 (1).png differ diff --git a/website/public/image 32 (2).png b/website/public/image 32 (2).png new file mode 100644 index 0000000..791593c Binary files /dev/null and b/website/public/image 32 (2).png differ diff --git a/website/public/image 33.png b/website/public/image 33.png new file mode 100644 index 0000000..9cb172f Binary files /dev/null and b/website/public/image 33.png differ diff --git a/website/public/image 34.png b/website/public/image 34.png new file mode 100644 index 0000000..7309662 Binary files /dev/null and b/website/public/image 34.png differ diff --git a/website/public/image 35.png b/website/public/image 35.png new file mode 100644 index 0000000..dbb5f1a Binary files /dev/null and b/website/public/image 35.png differ diff --git a/website/public/image 37.png b/website/public/image 37.png new file mode 100644 index 0000000..137b24f Binary files /dev/null and b/website/public/image 37.png differ diff --git a/website/public/image 38.png b/website/public/image 38.png new file mode 100644 index 0000000..517ea9b Binary files /dev/null and b/website/public/image 38.png differ diff --git a/website/public/image 39.png b/website/public/image 39.png new file mode 100644 index 0000000..6e76928 Binary files /dev/null and b/website/public/image 39.png differ diff --git a/website/public/image 40.png b/website/public/image 40.png new file mode 100644 index 0000000..a78e882 Binary files /dev/null and b/website/public/image 40.png differ diff --git a/website/public/image 41.png b/website/public/image 41.png new file mode 100644 index 0000000..e7cdfca Binary files /dev/null and b/website/public/image 41.png differ diff --git a/website/public/image 42.png b/website/public/image 42.png new file mode 100644 index 0000000..1609e81 Binary files /dev/null and b/website/public/image 42.png differ diff --git a/website/skew.html b/website/skew.html new file mode 100644 index 0000000..d9de3d2 --- /dev/null +++ b/website/skew.html @@ -0,0 +1,160 @@ + + + + + + + + Scroll snap carousel + + + +
+ + +
+ +
+ + +
+

Artists

+ +
+
+ + + + diff --git a/website/skew.js b/website/skew.js new file mode 100644 index 0000000..7aab7c9 --- /dev/null +++ b/website/skew.js @@ -0,0 +1,52 @@ +import Carousel from "../src/carousel"; + +Carousel({ + parent: ".skew .inner", + child: ".skew .slider", + selectedState: true, + minWebWidth: 100, + springConfig: `spring(1,90,20,19)`, + whileDragging: () => { + document.querySelectorAll(".skew .slider").forEach(i => { + i.classList.add("while-drag"); + }); + }, + whileDragEnd: () => { + if (window.innerWidth < 700) { + document.querySelectorAll(".skew .slider").forEach(i => { + if (i.classList.contains("while-drag")) { + i.classList.remove("while-drag"); + } + }); + } else { + setTimeout(() => { + document.querySelector( + ".skew .inner", + ).style.transform = `rotateY(${0}deg)`; + }, 600); + } + }, +}); + +Carousel({ + parent: ".container .carousel", + child: ".carousel .item", + customDragAction: "rotate", +}); + +Carousel({ + parent: ".artists-carousel", + child: ".each-artist", +}); + +document.querySelectorAll(".carousel .item").forEach((i, index) => { + const element = i; + if (index === 0) { + element.style.transform = `rotateY(${36}deg) translatez(${-window.innerWidth}px)`; + } else if (index === 1) { + element.style.transform = `rotateY(${0}deg) translatez(${-window.innerWidth}px)`; + } else + element.style.transform = `rotateY(${ + (index - 1) * -36 + }deg) translatez(${-window.innerWidth}px)`; +}); diff --git a/website/style.css b/website/style.css index 812a8a7..5ee7b08 100644 --- a/website/style.css +++ b/website/style.css @@ -65,20 +65,76 @@ h3 { user-select: none; } -body { - margin: 0; - line-height: inherit; - background: black; +.container { + margin: 0 auto; + width: 1050px; + height: 500px; + position: relative; + perspective: 1000px; + display: block; } -hr { - height: 0; - color: inherit; - border-top-width: 1px; +.carousel { + height: 100%; + width: 100%; + position: absolute; + transform-style: preserve-3d; + transition: transform 1s; +} + +.item { + display: block; + position: absolute; + width: 40vw; + height: auto; + line-height: 200px; + font-size: 5em; + text-align: center; + color: #fff; + border-radius: 10px; + z-index: 800; +} + +.next, +.prev { + color: #444; + position: absolute; + top: 100px; + padding: 1em 2em; + cursor: pointer; + background: #ccc; + border-radius: 5px; + border-top: 1px solid #fff; + box-shadow: 0 5px 0 #999; + transition: box-shadow 0.1s, top 0.1s; } -abbr:where([title]) { - text-decoration: underline dotted; +.prev { + left: 5em; +} + +.next { + position: absolute; + z-index: 10; + right: 0; +} + +.next:hover, +.prev:hover { + color: #000; +} + +.next:active, +.prev:active { + top: 104px; + box-shadow: 0 1px 0 #999; +} + +body { + margin: 0; + line-height: inherit; + background: white; + overflow-x: hidden; } h1, @@ -162,42 +218,6 @@ select { text-transform: none; } -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; - background-color: transparent; - background-image: none; -} - -:-moz-focusring { - outline: auto; -} - -:-moz-ui-invalid { - box-shadow: none; -} - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - -webkit-appearance: textfield; - outline-offset: -2px; -} - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - -webkit-appearance: button; - font: inherit; -} - summary { display: list-item; } @@ -331,7 +351,8 @@ video { .direction, .align-c, .thumbnail, -.thumb-top { +.thumb-top, +.skew { /* max-width: 1090px; */ max-width: 660px; @@ -342,9 +363,7 @@ video { justify-content: stretch; width: 100%; flex-wrap: nowrap; - overflow-x: hidden; cursor: grab; - overflow-y: hidden; white-space: nowrap; touch-action: none; will-change: transform; @@ -392,11 +411,6 @@ video { fill: white; stroke: white; } -@media only screen and (max-width: 700px) { - .slider.selected { - transform: scale(1) !important; - } -} .previous { position: absolute; @@ -404,12 +418,6 @@ video { left: 0; } -.next { - position: absolute; - z-index: 10; - right: 0; -} - .previous svg { transform: rotate(180deg); } @@ -418,6 +426,35 @@ video { cursor: grabbing; } +.each-artist { + margin-right: 16px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; + min-width: 90px; + scroll-snap-align: start; +} + +.each-artist:last-of-type { + margin-right: 0; +} + +.artist-text { + font-family: "Inter Variable", sans-serif; + font-style: normal; + font-weight: 415; + font-size: 15px; + line-height: 160%; + letter-spacing: 0.03em; + padding-top: 10px; + color: #979797; + flex: none; + order: 1; + flex-grow: 0; +} + .slider { width: 150px; margin: 10px; @@ -428,6 +465,312 @@ video { position: relative; } +.artists-carousel { + width: 100vw; + overflow: scroll; + display: flex; + justify-content: space-between; + margin-top: 16px; +} + +.artists-container { + padding-top: 65px; + padding-left: 20px; + display: none; +} + +.skew-section { + overflow-x: hidden; + box-sizing: border-box; + max-width: 100%; +} + +@media only screen and (min-width: 700px) { + .skew-section { + overflow-x: hidden; + overflow-y: visible; + } + + .skew { + display: none; + transform-style: preserve-3d; + transform: translate(-50%, -50%); + left: 80%; + top: 50%; + position: absolute; + } + + .skew .slider { + transform-origin: 50% 50% 500px; + position: absolute; + } + + .skew .inner { + position: absolute; + } + + .skew .slider:nth-of-type(1) { + opacity: 1; + transform: translate3d(0, 0, -500px); + backface-visibility: hidden; + background-position: -256.072px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(2) { + transform: translate3d(293.893px, 0, -404.509px) rotateY(-36deg); + backface-visibility: hidden; + background-position: -303.417px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(3) { + transform: translate3d(475.528px, 0, -154.508px) rotateY(-72deg); + backface-visibility: hidden; + background-position: -253.417px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(4) { + opacity: 1; + transform: translate3d(475.528px, 0, 154.508px) rotateY(-108deg); + backface-visibility: hidden; + background-position: -203.417px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(5) { + opacity: 1; + transform: translate3d(293.893px, 0, 404.509px) rotateY(-144deg); + backface-visibility: hidden; + background-position: -153.417px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(6) { + opacity: 1; + transform: translate3d(0, 0, 500px) rotateY(-180deg); + backface-visibility: hidden; + background-position: -103.417px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(7) { + opacity: 1; + transform: translate3d(-293.893px, 0, 404.509px) rotateY(-216deg); + backface-visibility: hidden; + background-position: -53.4171px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(8) { + opacity: 1; + transform: translate3d(-475.528px, 0, 154.508px) rotateY(-252deg); + backface-visibility: hidden; + background-position: -3.4171px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(9) { + opacity: 1; + transform: translate3d(-475.528px, 0, -154.508px) rotateY(-288deg); + backface-visibility: hidden; + background-position: 46.5829px 0; + transform-origin: 50% 50%; + } + + .skew .slider:nth-of-type(10) { + opacity: 1; + transform: translate3d(-293.893px, 0, -404.509px) rotateY(-324deg); + backface-visibility: hidden; + background-position: 96.5829px 0; + transform-origin: 50% 50%; + } +} + +.inner, +.carousel-item, +.selected-state-div, +.loop-exp-div, +.progress-inner, +.start-index-div, +.direction-div, +.align-c-div, +.thumbnail-inner, +.thumb-top-div { + display: flex; + align-items: center; + z-index: 1; +} + +.skew .inner { + /* margin-left: 20px; + margin-top: 28px; */ + perspective: 100px; + transform-style: preserve-3d; + perspective-origin: center; + padding-bottom: 50px; + padding-right: 50px; +} + +.carousel img { + box-shadow: 0 2.7673px 2.2138px 0 #00000005, 0 6.6501px 5.32px 0 #00000007, + 0 12.521px 10.0172px 0 #00000009, 0 22.3363px 17.869px 0 #0000000b, + 0 41.7776px 33.4221px 0 #0000000d; +} + +@media only screen and (max-width: 700px) { + .container { + display: none; + } + + .artists-container { + display: block !important; + } + + .skew-section { + margin-left: 20px; + color: black; + } + + .skew-section .flex { + padding-right: 0; + } + + .skew .slider:nth-of-type(2) { + margin-left: -27px; + } + + .skew .slider.selected { + transition: transform 0.5s ease; + } + + .skew .slider:not(.selected) { + /* margin-left:-30px; */ + transform: rotateY(-3deg); + + /* transform-origin: center; */ + transition: transform 0.5s ease; + } + + .skew .slider.while-drag { + transform: rotateY(-1deg); + } + + .skew .slider:nth-of-type(3) { + margin-left: -14px; + } + + .skew .slider:nth-of-type(4) { + margin-left: 3px; + } + + .skew .slider:nth-of-type(5) { + margin-left: 18px; + } + + .skew .slider:nth-of-type(6) { + margin-left: 36px; + } + + .skew .slider:nth-of-type(7) { + margin-left: 56px; + } + + .skew .slider:nth-of-type(8) { + margin-left: 76px; + } + + .skew .slider:nth-of-type(9) { + margin-left: 64px; + } + + .skew .slider:nth-of-type(10) { + margin-left: 68px; + } + + .skew .slider:nth-of-type(10):not(.selected) { + transform: rotateY(-1deg); + } + + .skew .slider:nth-of-type(9):not(.selected) { + transform: rotateY(-1deg); + } + + .skew-section p { + /* color:black */ + text-wrap: nowrap; + } + + .skew .slider.extra { + width: 20px; + } + + .skew .slider img { + box-shadow: 0 2.7673px 2.2138px 0 #00000005, 0 6.6501px 5.3201px 0 #00000007, + 0 12.5215px 10.0172px 0 #00000009, 0 22.3363px 17.869px 0 #0000000b, + 0 41.7776px 33.4221px 0 #0000000d; + } +} + +.skew-p { + font-family: "Inter Variable", sans-serif; + font-size: 22px; + font-weight: 580; + line-height: 25px; + letter-spacing: 0.015em; + text-align: left; + color: #171717; +} + +.skew-p2 { + font-family: "Inter Variable", sans-serif; + font-style: normal; + font-weight: 415; + font-size: 15px; + line-height: 160%; + display: flex; + align-items: center; + letter-spacing: 0.03em; + color: #979797 !important; + font-variation-settings: "opsz" 20; +} +@font-face { + font-family: "Inter Variable", sans-serif; + font-weight: 100 900; + font-display: block; + font-style: normal; + src: url("./public/Inter-V.woff2"); +} + +.skew-h2 { + font-family: "Inter Variable", sans-serif; + font-size: 28px; + font-weight: 676; + line-height: 45px; + letter-spacing: 0.005em; + text-align: left; + color: #171717; +} + +.flex { + padding: 10px 20px 27px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.skew .slider { + transition: transform 0.5s ease; + width: 249px; + height: 249px; +} + +.skew { + max-width: none; + padding-left: 20px; +} + .dots-parent .slider, .progress .slider, .loop-exp .slider, @@ -476,23 +819,8 @@ progress::-webkit-progress-value { background-color: green; } -.inner, -.carousel-item, -.selected-state-div, -.loop-exp-div, -.progress-inner, -.start-index-div, -.direction-div, -.align-c-div, -.thumbnail-inner, -.thumb-top-div { - display: flex; - align-items: center; - z-index: 1; -} - .slider.selected { - transform: scale(1.2); + /* transform: scale(1.2); */ } .slider:first-of-type { @@ -508,6 +836,33 @@ progress::-webkit-progress-value { opacity: 0.3; } +.skew .slider.selected { + transition: transform 0.5s ease; +} + +.skew .slider:not(.selected) { + /* margin-left:-30px; */ + transform: rotateY(-3deg); + + /* transform-origin: center; */ + transition: transform 0.5s ease; +} + +.skew .slider.while-drag { + transform: rotateY(-1deg); +} + +.album-name { + opacity: 1; + transition: opacity 0.5s ease; + padding-top: 23px; +} + +.skew .slider:not(.selected) .album-name { + opacity: 0; + transition: opacity 0.5s ease; +} + .axis-container, .axis-loop-container { max-height: 500px; diff --git a/website/styles.css b/website/styles.css new file mode 100644 index 0000000..621a7ed --- /dev/null +++ b/website/styles.css @@ -0,0 +1,567 @@ +/* From + https://raw.githubusercontent.com/tailwindlabs/tailwindcss/master/src/css/preflight.css +*/ + +/* +1. Prevent padding and border from affecting element width. + (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. +(https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: #e5e7eb; +} + +html { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + -moz-tab-size: 4; + tab-size: 4; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ + + font-feature-settings: normal; /* 5 */ +} + +img { + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + object-fit: cover; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ +h3 { + color: white; + margin: 25px auto 10px !important; + text-align: left; + max-width: 660px; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; +} + +body { + margin: 0; + line-height: inherit; + background: black; +} + +hr { + height: 0; + color: inherit; + border-top-width: 1px; +} + +abbr:where([title]) { + text-decoration: underline dotted; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +a { + color: inherit; + text-decoration: inherit; +} + +b, +strong { + font-weight: bolder; +} + +p { + color: white; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; +} + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + font-size: 1em; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + font-weight: inherit; + line-height: inherit; + color: inherit; + margin: 0; + padding: 0; +} + +button, +select { + text-transform: none; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; + background-color: transparent; + background-image: none; +} + +:-moz-focusring { + outline: auto; +} + +:-moz-ui-invalid { + box-shadow: none; +} + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +summary { + display: list-item; +} + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +textarea { + resize: vertical; +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #9ca3af; +} + +button, +[role="button"] { + cursor: pointer; +} + +:disabled { + cursor: default; +} + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; +} + +.svg { + width: 100%; + height: 100%; +} + +.fig { + width: 20px; + height: 20px; +} + +img, +video { + max-width: 100%; + height: auto; +} + +[hidden] { + display: none; +} + +/* ====== App CSS ====== */ + +#app { + min-height: 100%; +} + +.parent { + display: flex; + align-items: center; + justify-content: stretch; + width: 100%; + flex-wrap: nowrap; + overflow-x: hidden; + scroll-behavior: smooth; + cursor: grab; + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; +} + +.axis-container { + margin-bottom: 60px; +} + +.parent2, +.loop, +.autoplay, +.dragfree, +.dots-parent, +.carousel-container, +.selected-state, +.parent-1, +.parent-0, +.scroll-1, +.loop-exp, +.progress, +.start-index, +.direction, +.align-c, +.thumbnail, +.thumb-top { + /* max-width: 1090px; */ + + max-width: 660px; + z-index: 1; + margin: auto; + display: flex; + align-items: center; + justify-content: stretch; + width: 100%; + flex-wrap: nowrap; + overflow-x: hidden; + cursor: grab; + overflow-y: hidden; + white-space: nowrap; + touch-action: none; + will-change: transform; + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + position: relative; +} + +.inner { + /* touch-action: none; */ + padding-bottom: 20px; +} + +.dots-flex { + display: flex; + position: absolute; + bottom: 2px; + left: 50%; + transform: translateX(-50%); + z-index: 100; + background: black; + padding: 5px; + border-radius: 10px; +} + +.dots { + cursor: pointer; +} + +.dots-parent { + padding-bottom: 20px; +} + +.scroll { + overflow-x: scroll; +} + +.scroll-behaviour { + scroll-behavior: smooth; +} + +.selected-dot circle { + fill: white; + stroke: white; +} +@media only screen and (max-width: 700px) { + .slider.selected { + transform: scale(1) !important; + } +} + +.previous { + position: absolute; + z-index: 10; + left: 0; +} + +.next { + position: absolute; + z-index: 10; + right: 0; +} + +.previous svg { + transform: rotate(180deg); +} + +.parent:active { + cursor: grabbing; +} + +.slider { + width: 150px; + margin: 10px; + height: auto; + flex: 0 0 auto; + scroll-snap-align: start; + z-index: 800; + position: relative; +} + +.dots-parent .slider, +.progress .slider, +.loop-exp .slider, +.thumb-top-div .slider { + width: 660px; + max-width: 100%; + max-height: 300px; +} + +.axis-loop .slider, +.axis-loop .slider img { + height: 480px; +} + +.loop-exp .slider { + position: relative; +} + +.loop-exp p { + position: absolute; + z-index: 3300; + top: 10px; + left: 10px; + font-size: 30px; +} + +progress { + position: absolute; + z-index: 20; + bottom: 0; + left: 50%; + transform: translateX(-50%); + transition: width 5s ease; + border-radius: 12px; +} + +progress::-webkit-progress-value { + transition: width 0.4s ease; +} + +.two { + background-color: blue; +} + +.three { + background-color: green; +} + +.inner, +.carousel-item, +.selected-state-div, +.loop-exp-div, +.progress-inner, +.start-index-div, +.direction-div, +.align-c-div, +.thumbnail-inner, +.thumb-top-div { + display: flex; + align-items: center; + z-index: 1; +} + +.slider.selected { + transform: scale(1.2); +} + +.slider:first-of-type { + margin-left: 0; +} + +.thumbnail-inner .slider.selected { + transform: scale(1); + opacity: 1; +} + +.thumbnail-inner .slider:not(.selected) { + opacity: 0.3; +} + +.axis-container, +.axis-loop-container { + max-height: 500px; + overflow: hidden; + display: flex; + justify-content: center; + position: relative; +} + +span { + color: white; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; +} + +.active { + scroll-behavior: smooth; +} + +.embla { + overflow: hidden; + cursor: grab; +} + +.left-arrow, +.left-arrow-1, +.left-arrow-0, +.left-arrow-s1 { + position: absolute; + z-index: 2; + cursor: pointer; + display: block; +} + +.up-arrow, +.up-arrow-loop { + transform: rotateZ(270deg); + position: absolute; + z-index: 970; + cursor: pointer; +} + +.down-arrow, +.down-arrow-loop { + transform: rotateZ(90deg); + position: absolute; + z-index: 970; + cursor: pointer; + bottom: 0; +} + +.right-arrow, +.right-arrow-1, +.right-arrow-0, +.right-arrow-s1 { + position: absolute; + z-index: 1; + right: 0; + cursor: pointer; +} + +.axis-container .slider:first-of-type, +.axis-loop-containe .slider:first-of-type { + margin: 0 !important; + margin-left: 13px !important; +} diff --git a/website/ticker.html b/website/ticker.html new file mode 100644 index 0000000..16ca4af --- /dev/null +++ b/website/ticker.html @@ -0,0 +1,31 @@ + + + + + + + + Scroll snap carousel + + + +
+
+
    +
  • 1
  • +
  • 2
  • +
  • 3
  • +
  • 4
  • +
  • 5
  • +
  • 6
  • +
  • 7
  • +
  • 8
  • +
  • 9
  • +
  • 10
  • +
  • 11
  • +
  • 12
  • +
+
+
+ +