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:
+
+```
+
+
+ carousel element
+ carousel element
+
+
+```
+
+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
+
+
+
+
+
+
+
+
+
+
01
+
+
About us
+
+ Our team is a mix of diverse talents, each bringing a unique
+ perspective to the table.
+
+
+
+
+
+
+
02
+
+
About us
+
+ Our team is a mix of diverse talents, each bringing a unique
+ perspective to the table.
+
+
+
+
+
+
+
03
+
+
About us
+
+ Our team is a mix of diverse talents, each bringing a unique
+ perspective to the table.
+
+
+
+
+
+
+
04
+
+
About us
+
+ Our team is a mix of diverse talents, each bringing a unique
+ perspective to the table.
+
+
+
+
+
+
+
05
+
+
About us
+
+ Our team is a mix of diverse talents, each bringing a unique
+ perspective to the table.
+
+
+
+
+
+
+
06
+
+
About us
+
+ Our team is a mix of diverse talents, each bringing a unique
+ perspective to the table.
+
+
+
+
+
+
+
07
+
+
About us
+
+ Our team is a mix of diverse talents, each bringing a unique
+ perspective to the table.
+
+
+
+
+
+
+
+
+
+
+
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
+
+
+
+
+
+ Library
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Sunkissed
+
12 tracks
+
+
+
+
+
+
Summer Waves
+
7 tracks
+
+
+
+
+
+
Summer Sunshine
+
2 tracks
+
+
+
+
+
+
Summer Fit
+
9 tracks
+
+
+
+
+
+
+
+
+
+
+
Friday Feeling
+
12 tracks
+
+
+
+
+
+
Midnight city
+
12 tracks
+
+
+
+
+
+
Africa Now
+
6 tracks
+
+
+
+
+
+
Butterfly
+
12 tracks
+
+
+
+
+
+
Indi anthem
+
7 tracks
+
+
+
+
+
+
+
+
Artists
+
+
+
+ Chill&Go
+
+
+
+ Garbriel G.
+
+
+
+ Solo San
+
+
+
+ Daniel O.
+
+
+
+ Chill&Go
+
+
+
+ Garbriel G.
+
+
+
+ Solo San
+
+
+
+ Daniel O.
+
+
+
+
+
+
+
+
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
+
+
+
+
+