@snap-carousel/core
Advanced tools
Comparing version 0.0.19-next.0 to 0.0.19-next.1
@@ -1,4 +0,4 @@ | ||
export declare const getActiveSnap: ({ root, onChange, }: { | ||
export declare const getActiveSnap: ({ root, onChange, snapAlign }: { | ||
root: HTMLDivElement; | ||
snapPerPage?: number | undefined; | ||
snapAlign?: "start" | "center" | "end" | undefined; | ||
onChange?: ((snapIndex: number) => void) | undefined; | ||
@@ -5,0 +5,0 @@ }) => { |
@@ -46,11 +46,2 @@ const mergeStyles = (...classnames) => classnames.filter(Boolean).join(' '); | ||
(navigator.maxTouchPoints || navigator.msMaxTouchPoints)); | ||
var utils = /*#__PURE__*/Object.freeze({ | ||
__proto__: null, | ||
mergeStyles: mergeStyles, | ||
mapStyles: mapStyles, | ||
mapItem: mapItem, | ||
isTouchDevice: isTouchDevice | ||
}); | ||
const debounceHOF = (callback, ms) => { | ||
@@ -67,45 +58,17 @@ let timeout; | ||
}; | ||
const getVisibleChildren = ($viewport) => { | ||
if (!$viewport) | ||
return { children: [], childrenInCenter: 0 }; | ||
const viewport = { | ||
left: $viewport.scrollLeft, | ||
width: $viewport.offsetWidth, | ||
right: $viewport.scrollLeft + $viewport.offsetWidth, | ||
top: $viewport.scrollTop, | ||
height: $viewport.offsetHeight, | ||
bottom: $viewport.scrollTop + $viewport.offsetHeight, | ||
offsetLeft: $viewport.offsetLeft, | ||
offsetTop: $viewport.offsetTop, | ||
centerHorizontal: $viewport.scrollLeft + $viewport.offsetWidth / 2, | ||
centerVertical: $viewport.scrollTop + $viewport.offsetHeight / 2, | ||
}; | ||
const children = []; | ||
const elements = $viewport.children; | ||
let childrenInCenter = 0; | ||
for (let index = 0; index < elements.length; index++) { | ||
const element = elements[index]; | ||
const elementBounds = mapItem({ element, viewport }); | ||
const isVisibleHorizontally = elementBounds.left >= viewport.left && | ||
elementBounds.right <= viewport.right; | ||
const isVisibleVertically = elementBounds.top >= viewport.top && | ||
elementBounds.bottom <= viewport.bottom; | ||
const isInCenterHorizontally = elementBounds.left <= viewport.centerHorizontal && | ||
elementBounds.right >= viewport.centerHorizontal; | ||
const isInCenterVertically = elementBounds.top <= viewport.centerVertical && | ||
elementBounds.bottom >= viewport.centerVertical; | ||
if (isVisibleHorizontally && isVisibleVertically) { | ||
children.push(index); | ||
} | ||
if (isInCenterHorizontally && isInCenterVertically) { | ||
childrenInCenter = index; | ||
} | ||
} | ||
return { children, childrenInCenter }; | ||
}; | ||
const getActiveSnap = ({ root, onChange, }) => { | ||
var utils = /*#__PURE__*/Object.freeze({ | ||
__proto__: null, | ||
mergeStyles: mergeStyles, | ||
mapStyles: mapStyles, | ||
mapItem: mapItem, | ||
isTouchDevice: isTouchDevice, | ||
debounceHOF: debounceHOF | ||
}); | ||
const getActiveSnap = ({ root, onChange, snapAlign = 'start' }) => { | ||
let activeSnapObserver; | ||
let firstSlideObserver; | ||
let lastSlideObserver; | ||
let activeSnapIndex; | ||
let timeout = null; | ||
let isScrolling = false; | ||
const children = root.children; | ||
@@ -120,4 +83,2 @@ const triggerChange = (snapIndex) => { | ||
activeSnapObserver.disconnect(); | ||
firstSlideObserver.disconnect(); | ||
lastSlideObserver.disconnect(); | ||
window.removeEventListener('resize', onResizeWithDebounce); | ||
@@ -134,97 +95,98 @@ }; | ||
const onResizeWithDebounce = debounceHOF(onResize, 100); | ||
const handleScrolling = () => { | ||
isScrolling = true; | ||
if (timeout) | ||
clearTimeout(timeout); | ||
timeout = setTimeout(() => { | ||
// if (root.scrollLeft === 0) { | ||
// setSnapIndex(0); | ||
// } else if (root.scrollLeft === root.scrollWidth - root.offsetWidth) { | ||
// setSnapIndex(children.length - 1); | ||
// } | ||
isScrolling = false; | ||
}, 50); | ||
}; | ||
const init = () => { | ||
const rootWidth = root.offsetWidth; | ||
const rootMargin = `0px 0px 0px -${rootWidth / 2}px`; | ||
const marginLeft = getComputedStyle(children[0]).marginLeft; | ||
const marginRight = getComputedStyle(children[children.length - 1]) | ||
.marginRight; | ||
const rootMarginEdges = `0px -${marginLeft} 0px -${marginRight}`; | ||
activeSnapIndex = root.scrollLeft | ||
? getVisibleChildren(root).childrenInCenter | ||
: 0; | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
// entry.rootBounds is wrong on Webkit if there is a padding on the root | ||
// so we use the BoundingClientRect instead | ||
const rootBb = root.getBoundingClientRect(); | ||
if (activeSnapIndex !== null) { | ||
// If next | ||
if (entry.intersectionRatio <= 0.51 && | ||
entryIndex > activeSnapIndex && | ||
entry.boundingClientRect.left < rootBb.left + rootBb.width / 2) { | ||
// console.log('next'); | ||
activeSnapObserver.unobserve(entry.target); | ||
children[entryIndex - 2] && | ||
activeSnapObserver.unobserve(children[entryIndex - 2]); | ||
if (children[entryIndex + 1]) { | ||
activeSnapObserver.observe(children[entryIndex + 1]); | ||
activeSnapIndex = 0; | ||
switch (snapAlign) { | ||
case 'start': | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
if (activeSnapIndex === null) | ||
return; | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
if (entry.isIntersecting && entry.boundingClientRect.right > 0) { | ||
setSnapIndex(entryIndex + 1); | ||
return; | ||
} | ||
if (!entry.isIntersecting && | ||
entry.intersectionRatio > 0 && | ||
entry.boundingClientRect.right > 0) { | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
if (children[entryIndex - 1]) | ||
activeSnapObserver.observe(children[entryIndex - 1]); | ||
return; | ||
} | ||
// If previous | ||
if (entryIndex <= activeSnapIndex && | ||
entry.intersectionRatio >= 0.49 && | ||
entry.rootBounds && | ||
entry.boundingClientRect.right > rootBb.left + rootBb.width / 2) { | ||
// console.log('previous'); | ||
activeSnapObserver.unobserve(entry.target); | ||
children[entryIndex + 2] && | ||
activeSnapObserver.unobserve(children[entryIndex + 2]); | ||
if (children[entryIndex - 1]) { | ||
activeSnapObserver.observe(children[entryIndex - 1]); | ||
}); | ||
}, { | ||
root, | ||
rootMargin: `0px -100% 0px 50%`, | ||
threshold: [0.5], | ||
}); | ||
break; | ||
case 'end': | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
if (activeSnapIndex === null || !isScrolling) | ||
return; | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
if (entry.isIntersecting && | ||
entry.boundingClientRect.left < rootWidth) { | ||
setSnapIndex(entryIndex - 1); | ||
return; | ||
} | ||
if (!entry.isIntersecting && | ||
entry.intersectionRatio > 0 && | ||
entry.boundingClientRect.left < rootWidth) { | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
if (children[entryIndex + 1]) | ||
activeSnapObserver.observe(children[entryIndex + 1]); | ||
} | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin, | ||
threshold: [0.49, 0.51], | ||
}); | ||
}, { | ||
root, | ||
rootMargin: `0px 50% 0px -100%`, | ||
threshold: [0.5], | ||
}); | ||
break; | ||
case 'center': | ||
default: | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
if (activeSnapIndex === null || !isScrolling || !entry.rootBounds) | ||
return; | ||
if (entry.intersectionRatio >= 0.49 && | ||
entry.intersectionRatio <= 0.51 && | ||
entry.boundingClientRect.left <= | ||
entry.rootBounds.left + | ||
entry.boundingClientRect.width * 0.01 && | ||
entry.boundingClientRect.right >= | ||
entry.rootBounds.left + entry.boundingClientRect.width * 0.01) { | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin: `0px 0px 0px -50%`, | ||
threshold: [0.49, 0.51], | ||
}); | ||
break; | ||
} | ||
root.addEventListener('scroll', handleScrolling); | ||
Array.from(root.children).forEach((child) => { | ||
activeSnapObserver.observe(child); | ||
}); | ||
firstSlideObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
if (entry.isIntersecting && | ||
entry.intersectionRatio >= 0.99 && | ||
activeSnapIndex !== 0) { | ||
activeSnapObserver.unobserve(children[activeSnapIndex]); | ||
activeSnapObserver.observe(children[1]); | ||
setSnapIndex(0); | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin: rootMarginEdges, | ||
threshold: [0, 0.99], | ||
}); | ||
lastSlideObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
if (entry.isIntersecting && | ||
entry.intersectionRatio >= 0.99 && | ||
activeSnapIndex !== children.length - 1) { | ||
activeSnapObserver.unobserve(children[activeSnapIndex]); | ||
activeSnapObserver.observe(children[children.length - 2]); | ||
setSnapIndex(children.length - 1); | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin: rootMarginEdges, | ||
threshold: [0, 0.99], | ||
}); | ||
// Set intersection observers | ||
if (children[activeSnapIndex - 1]) | ||
activeSnapObserver.observe(children[activeSnapIndex - 1]); | ||
activeSnapObserver.observe(children[activeSnapIndex + 1] | ||
? children[activeSnapIndex + 1] | ||
: children[activeSnapIndex]); | ||
firstSlideObserver.observe(children[0]); | ||
lastSlideObserver.observe(children[children.length - 1]); | ||
window.addEventListener('resize', onResizeWithDebounce); | ||
@@ -329,3 +291,3 @@ }; | ||
left: item.left - | ||
item.paddingLeft - | ||
// item.paddingLeft - | ||
viewport.paddingLeft - | ||
@@ -375,3 +337,3 @@ viewport.scrollPaddingLeft, | ||
const cancel = goTo(index); | ||
return { cancel, index }; | ||
return { cancel }; | ||
}; | ||
@@ -383,3 +345,11 @@ | ||
const item = mapItem({ element, viewport }); | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
switch (item.snapAlign) { | ||
case 'start': | ||
return item.left; | ||
case 'end': | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
case 'center': | ||
default: | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
} | ||
}; | ||
@@ -397,3 +367,5 @@ const getElementPositionY = (viewport, element) => { | ||
const setIsDragging = (value) => (isDragging = value); | ||
let initialTargetIndex = 0; | ||
let isDown = false; | ||
let velocity = 0; | ||
let startX = 0; | ||
@@ -409,3 +381,3 @@ let startY = 0; | ||
timeout = setTimeout(() => { | ||
if (!root) | ||
if (!root || isDown) | ||
return; | ||
@@ -433,2 +405,8 @@ root.removeEventListener('scroll', handleScrolling); | ||
slideY = root.scrollTop; | ||
const scrollTargetX = getClosest(elementPositionsX, slideX); | ||
const scrollTargetY = getClosest(elementPositionsY, slideY); | ||
initialTargetIndex = | ||
scrollTargetX > 0 | ||
? elementPositionsX.indexOf(scrollTargetX) | ||
: elementPositionsY.indexOf(scrollTargetY); | ||
}; | ||
@@ -440,2 +418,3 @@ const handleMouseMove = (event) => { | ||
return; | ||
velocity = event.movementX; | ||
const distanceMoved = Math.abs(startX - (event.pageX - root.offsetLeft)); | ||
@@ -473,3 +452,6 @@ if (distanceMoved < dragThreshold) | ||
const dragEndPositionY = root.scrollTop; | ||
const scrollTargetX = getClosest(elementPositionsX, dragEndPositionX); | ||
const isDraggedAllTheWay = root.scrollWidth - root.offsetWidth === root.scrollLeft; | ||
const scrollTargetX = isDraggedAllTheWay | ||
? elementPositionsX[elementPositionsX.length - 1] | ||
: getClosest(elementPositionsX, dragEndPositionX); | ||
const scrollTargetY = getClosest(elementPositionsY, dragEndPositionY); | ||
@@ -479,3 +461,10 @@ const targetIndex = scrollTargetX > 0 | ||
: elementPositionsY.indexOf(scrollTargetY); | ||
scrollTo({ root, index: targetIndex }); | ||
if (targetIndex === initialTargetIndex && | ||
(velocity > 10 || velocity < -10)) { | ||
const targetIndex = velocity > 10 ? initialTargetIndex - 1 : initialTargetIndex + 1; | ||
scrollTo({ root, index: targetIndex }); | ||
} | ||
else { | ||
scrollTo({ root, index: targetIndex }); | ||
} | ||
}; | ||
@@ -482,0 +471,0 @@ const handleClick = (event) => { |
@@ -50,11 +50,2 @@ 'use strict'; | ||
(navigator.maxTouchPoints || navigator.msMaxTouchPoints)); | ||
var utils = /*#__PURE__*/Object.freeze({ | ||
__proto__: null, | ||
mergeStyles: mergeStyles, | ||
mapStyles: mapStyles, | ||
mapItem: mapItem, | ||
isTouchDevice: isTouchDevice | ||
}); | ||
const debounceHOF = (callback, ms) => { | ||
@@ -71,45 +62,17 @@ let timeout; | ||
}; | ||
const getVisibleChildren = ($viewport) => { | ||
if (!$viewport) | ||
return { children: [], childrenInCenter: 0 }; | ||
const viewport = { | ||
left: $viewport.scrollLeft, | ||
width: $viewport.offsetWidth, | ||
right: $viewport.scrollLeft + $viewport.offsetWidth, | ||
top: $viewport.scrollTop, | ||
height: $viewport.offsetHeight, | ||
bottom: $viewport.scrollTop + $viewport.offsetHeight, | ||
offsetLeft: $viewport.offsetLeft, | ||
offsetTop: $viewport.offsetTop, | ||
centerHorizontal: $viewport.scrollLeft + $viewport.offsetWidth / 2, | ||
centerVertical: $viewport.scrollTop + $viewport.offsetHeight / 2, | ||
}; | ||
const children = []; | ||
const elements = $viewport.children; | ||
let childrenInCenter = 0; | ||
for (let index = 0; index < elements.length; index++) { | ||
const element = elements[index]; | ||
const elementBounds = mapItem({ element, viewport }); | ||
const isVisibleHorizontally = elementBounds.left >= viewport.left && | ||
elementBounds.right <= viewport.right; | ||
const isVisibleVertically = elementBounds.top >= viewport.top && | ||
elementBounds.bottom <= viewport.bottom; | ||
const isInCenterHorizontally = elementBounds.left <= viewport.centerHorizontal && | ||
elementBounds.right >= viewport.centerHorizontal; | ||
const isInCenterVertically = elementBounds.top <= viewport.centerVertical && | ||
elementBounds.bottom >= viewport.centerVertical; | ||
if (isVisibleHorizontally && isVisibleVertically) { | ||
children.push(index); | ||
} | ||
if (isInCenterHorizontally && isInCenterVertically) { | ||
childrenInCenter = index; | ||
} | ||
} | ||
return { children, childrenInCenter }; | ||
}; | ||
const getActiveSnap = ({ root, onChange, }) => { | ||
var utils = /*#__PURE__*/Object.freeze({ | ||
__proto__: null, | ||
mergeStyles: mergeStyles, | ||
mapStyles: mapStyles, | ||
mapItem: mapItem, | ||
isTouchDevice: isTouchDevice, | ||
debounceHOF: debounceHOF | ||
}); | ||
const getActiveSnap = ({ root, onChange, snapAlign = 'start' }) => { | ||
let activeSnapObserver; | ||
let firstSlideObserver; | ||
let lastSlideObserver; | ||
let activeSnapIndex; | ||
let timeout = null; | ||
let isScrolling = false; | ||
const children = root.children; | ||
@@ -124,4 +87,2 @@ const triggerChange = (snapIndex) => { | ||
activeSnapObserver.disconnect(); | ||
firstSlideObserver.disconnect(); | ||
lastSlideObserver.disconnect(); | ||
window.removeEventListener('resize', onResizeWithDebounce); | ||
@@ -138,97 +99,98 @@ }; | ||
const onResizeWithDebounce = debounceHOF(onResize, 100); | ||
const handleScrolling = () => { | ||
isScrolling = true; | ||
if (timeout) | ||
clearTimeout(timeout); | ||
timeout = setTimeout(() => { | ||
// if (root.scrollLeft === 0) { | ||
// setSnapIndex(0); | ||
// } else if (root.scrollLeft === root.scrollWidth - root.offsetWidth) { | ||
// setSnapIndex(children.length - 1); | ||
// } | ||
isScrolling = false; | ||
}, 50); | ||
}; | ||
const init = () => { | ||
const rootWidth = root.offsetWidth; | ||
const rootMargin = `0px 0px 0px -${rootWidth / 2}px`; | ||
const marginLeft = getComputedStyle(children[0]).marginLeft; | ||
const marginRight = getComputedStyle(children[children.length - 1]) | ||
.marginRight; | ||
const rootMarginEdges = `0px -${marginLeft} 0px -${marginRight}`; | ||
activeSnapIndex = root.scrollLeft | ||
? getVisibleChildren(root).childrenInCenter | ||
: 0; | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
// entry.rootBounds is wrong on Webkit if there is a padding on the root | ||
// so we use the BoundingClientRect instead | ||
const rootBb = root.getBoundingClientRect(); | ||
if (activeSnapIndex !== null) { | ||
// If next | ||
if (entry.intersectionRatio <= 0.51 && | ||
entryIndex > activeSnapIndex && | ||
entry.boundingClientRect.left < rootBb.left + rootBb.width / 2) { | ||
// console.log('next'); | ||
activeSnapObserver.unobserve(entry.target); | ||
children[entryIndex - 2] && | ||
activeSnapObserver.unobserve(children[entryIndex - 2]); | ||
if (children[entryIndex + 1]) { | ||
activeSnapObserver.observe(children[entryIndex + 1]); | ||
activeSnapIndex = 0; | ||
switch (snapAlign) { | ||
case 'start': | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
if (activeSnapIndex === null) | ||
return; | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
if (entry.isIntersecting && entry.boundingClientRect.right > 0) { | ||
setSnapIndex(entryIndex + 1); | ||
return; | ||
} | ||
if (!entry.isIntersecting && | ||
entry.intersectionRatio > 0 && | ||
entry.boundingClientRect.right > 0) { | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
if (children[entryIndex - 1]) | ||
activeSnapObserver.observe(children[entryIndex - 1]); | ||
return; | ||
} | ||
// If previous | ||
if (entryIndex <= activeSnapIndex && | ||
entry.intersectionRatio >= 0.49 && | ||
entry.rootBounds && | ||
entry.boundingClientRect.right > rootBb.left + rootBb.width / 2) { | ||
// console.log('previous'); | ||
activeSnapObserver.unobserve(entry.target); | ||
children[entryIndex + 2] && | ||
activeSnapObserver.unobserve(children[entryIndex + 2]); | ||
if (children[entryIndex - 1]) { | ||
activeSnapObserver.observe(children[entryIndex - 1]); | ||
}); | ||
}, { | ||
root, | ||
rootMargin: `0px -100% 0px 50%`, | ||
threshold: [0.5], | ||
}); | ||
break; | ||
case 'end': | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
if (activeSnapIndex === null || !isScrolling) | ||
return; | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
if (entry.isIntersecting && | ||
entry.boundingClientRect.left < rootWidth) { | ||
setSnapIndex(entryIndex - 1); | ||
return; | ||
} | ||
if (!entry.isIntersecting && | ||
entry.intersectionRatio > 0 && | ||
entry.boundingClientRect.left < rootWidth) { | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
if (children[entryIndex + 1]) | ||
activeSnapObserver.observe(children[entryIndex + 1]); | ||
} | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin, | ||
threshold: [0.49, 0.51], | ||
}); | ||
}, { | ||
root, | ||
rootMargin: `0px 50% 0px -100%`, | ||
threshold: [0.5], | ||
}); | ||
break; | ||
case 'center': | ||
default: | ||
activeSnapObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
if (activeSnapIndex === null || !isScrolling || !entry.rootBounds) | ||
return; | ||
if (entry.intersectionRatio >= 0.49 && | ||
entry.intersectionRatio <= 0.51 && | ||
entry.boundingClientRect.left <= | ||
entry.rootBounds.left + | ||
entry.boundingClientRect.width * 0.01 && | ||
entry.boundingClientRect.right >= | ||
entry.rootBounds.left + entry.boundingClientRect.width * 0.01) { | ||
const entryIndex = Array.prototype.indexOf.call(children, entry.target); | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin: `0px 0px 0px -50%`, | ||
threshold: [0.49, 0.51], | ||
}); | ||
break; | ||
} | ||
root.addEventListener('scroll', handleScrolling); | ||
Array.from(root.children).forEach((child) => { | ||
activeSnapObserver.observe(child); | ||
}); | ||
firstSlideObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
if (entry.isIntersecting && | ||
entry.intersectionRatio >= 0.99 && | ||
activeSnapIndex !== 0) { | ||
activeSnapObserver.unobserve(children[activeSnapIndex]); | ||
activeSnapObserver.observe(children[1]); | ||
setSnapIndex(0); | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin: rootMarginEdges, | ||
threshold: [0, 0.99], | ||
}); | ||
lastSlideObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
if (entry.isIntersecting && | ||
entry.intersectionRatio >= 0.99 && | ||
activeSnapIndex !== children.length - 1) { | ||
activeSnapObserver.unobserve(children[activeSnapIndex]); | ||
activeSnapObserver.observe(children[children.length - 2]); | ||
setSnapIndex(children.length - 1); | ||
} | ||
}); | ||
}, { | ||
root, | ||
rootMargin: rootMarginEdges, | ||
threshold: [0, 0.99], | ||
}); | ||
// Set intersection observers | ||
if (children[activeSnapIndex - 1]) | ||
activeSnapObserver.observe(children[activeSnapIndex - 1]); | ||
activeSnapObserver.observe(children[activeSnapIndex + 1] | ||
? children[activeSnapIndex + 1] | ||
: children[activeSnapIndex]); | ||
firstSlideObserver.observe(children[0]); | ||
lastSlideObserver.observe(children[children.length - 1]); | ||
window.addEventListener('resize', onResizeWithDebounce); | ||
@@ -333,3 +295,3 @@ }; | ||
left: item.left - | ||
item.paddingLeft - | ||
// item.paddingLeft - | ||
viewport.paddingLeft - | ||
@@ -379,3 +341,3 @@ viewport.scrollPaddingLeft, | ||
const cancel = goTo(index); | ||
return { cancel, index }; | ||
return { cancel }; | ||
}; | ||
@@ -387,3 +349,11 @@ | ||
const item = mapItem({ element, viewport }); | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
switch (item.snapAlign) { | ||
case 'start': | ||
return item.left; | ||
case 'end': | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
case 'center': | ||
default: | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
} | ||
}; | ||
@@ -401,3 +371,5 @@ const getElementPositionY = (viewport, element) => { | ||
const setIsDragging = (value) => (isDragging = value); | ||
let initialTargetIndex = 0; | ||
let isDown = false; | ||
let velocity = 0; | ||
let startX = 0; | ||
@@ -413,3 +385,3 @@ let startY = 0; | ||
timeout = setTimeout(() => { | ||
if (!root) | ||
if (!root || isDown) | ||
return; | ||
@@ -437,2 +409,8 @@ root.removeEventListener('scroll', handleScrolling); | ||
slideY = root.scrollTop; | ||
const scrollTargetX = getClosest(elementPositionsX, slideX); | ||
const scrollTargetY = getClosest(elementPositionsY, slideY); | ||
initialTargetIndex = | ||
scrollTargetX > 0 | ||
? elementPositionsX.indexOf(scrollTargetX) | ||
: elementPositionsY.indexOf(scrollTargetY); | ||
}; | ||
@@ -444,2 +422,3 @@ const handleMouseMove = (event) => { | ||
return; | ||
velocity = event.movementX; | ||
const distanceMoved = Math.abs(startX - (event.pageX - root.offsetLeft)); | ||
@@ -477,3 +456,6 @@ if (distanceMoved < dragThreshold) | ||
const dragEndPositionY = root.scrollTop; | ||
const scrollTargetX = getClosest(elementPositionsX, dragEndPositionX); | ||
const isDraggedAllTheWay = root.scrollWidth - root.offsetWidth === root.scrollLeft; | ||
const scrollTargetX = isDraggedAllTheWay | ||
? elementPositionsX[elementPositionsX.length - 1] | ||
: getClosest(elementPositionsX, dragEndPositionX); | ||
const scrollTargetY = getClosest(elementPositionsY, dragEndPositionY); | ||
@@ -483,3 +465,10 @@ const targetIndex = scrollTargetX > 0 | ||
: elementPositionsY.indexOf(scrollTargetY); | ||
scrollTo({ root, index: targetIndex }); | ||
if (targetIndex === initialTargetIndex && | ||
(velocity > 10 || velocity < -10)) { | ||
const targetIndex = velocity > 10 ? initialTargetIndex - 1 : initialTargetIndex + 1; | ||
scrollTo({ root, index: targetIndex }); | ||
} | ||
else { | ||
scrollTo({ root, index: targetIndex }); | ||
} | ||
}; | ||
@@ -486,0 +475,0 @@ const handleClick = (event) => { |
@@ -7,3 +7,2 @@ export declare const scrollTo: ({ root, index, duration, }: { | ||
cancel: (() => void) | undefined; | ||
index: number; | ||
}; |
@@ -33,1 +33,2 @@ export declare const mergeStyles: (...classnames: (string | null | undefined)[]) => string; | ||
export declare const isTouchDevice: () => boolean; | ||
export declare const debounceHOF: (callback: () => void, ms: number) => () => void; |
{ | ||
"name": "@snap-carousel/core", | ||
"version": "0.0.19-next.0", | ||
"version": "0.0.19-next.1", | ||
"main": "dist/index.js", | ||
@@ -18,11 +18,11 @@ "module": "dist/index.es.js", | ||
"devDependencies": { | ||
"@rollup/plugin-commonjs": "^13.0.0", | ||
"@rollup/plugin-node-resolve": "^8.0.1", | ||
"@rollup/plugin-commonjs": "^22.0.1", | ||
"@rollup/plugin-node-resolve": "^13.3.0", | ||
"@rollup/plugin-typescript": "^8.3.2", | ||
"@typescript-eslint/eslint-plugin": "^3.2.0", | ||
"@typescript-eslint/parser": "^3.2.0", | ||
"rollup": "^2.74.1", | ||
"rollup-plugin-peer-deps-external": "^2.2.2" | ||
"rollup": "^2.75.7", | ||
"rollup-plugin-peer-deps-external": "^2.2.4" | ||
}, | ||
"gitHead": "be0241b11d19060dc05fcb18868d0f61400f19f5" | ||
"gitHead": "41cab1bc8a6d15d5d7d3cccdc2fe5a8e1890cbea" | ||
} |
@@ -13,3 +13,12 @@ import { isTouchDevice, mapItem } from './utils'; | ||
const item = mapItem({ element, viewport }); | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
switch (item.snapAlign) { | ||
case 'start': | ||
return item.left; | ||
case 'end': | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
case 'center': | ||
default: | ||
return item.left - (viewport.offsetWidth / 2 - item.width / 2); | ||
} | ||
}; | ||
@@ -45,3 +54,5 @@ | ||
let initialTargetIndex = 0; | ||
let isDown = false; | ||
let velocity = 0; | ||
let startX = 0; | ||
@@ -58,3 +69,3 @@ let startY = 0; | ||
timeout = setTimeout(() => { | ||
if (!root) return; | ||
if (!root || isDown) return; | ||
@@ -85,2 +96,9 @@ root.removeEventListener('scroll', handleScrolling); | ||
slideY = root.scrollTop; | ||
const scrollTargetX = getClosest(elementPositionsX, slideX); | ||
const scrollTargetY = getClosest(elementPositionsY, slideY); | ||
initialTargetIndex = | ||
scrollTargetX > 0 | ||
? elementPositionsX.indexOf(scrollTargetX) | ||
: elementPositionsY.indexOf(scrollTargetY); | ||
}; | ||
@@ -92,2 +110,4 @@ | ||
velocity = event.movementX; | ||
const distanceMoved = Math.abs(startX - (event.pageX - root.offsetLeft)); | ||
@@ -131,3 +151,7 @@ | ||
const dragEndPositionY = root.scrollTop; | ||
const scrollTargetX = getClosest(elementPositionsX, dragEndPositionX); | ||
const isDraggedAllTheWay = | ||
root.scrollWidth - root.offsetWidth === root.scrollLeft; | ||
const scrollTargetX = isDraggedAllTheWay | ||
? elementPositionsX[elementPositionsX.length - 1] | ||
: getClosest(elementPositionsX, dragEndPositionX); | ||
const scrollTargetY = getClosest(elementPositionsY, dragEndPositionY); | ||
@@ -140,3 +164,13 @@ | ||
scrollTo({ root, index: targetIndex }); | ||
if ( | ||
targetIndex === initialTargetIndex && | ||
(velocity > 10 || velocity < -10) | ||
) { | ||
const targetIndex = | ||
velocity > 10 ? initialTargetIndex - 1 : initialTargetIndex + 1; | ||
scrollTo({ root, index: targetIndex }); | ||
} else { | ||
scrollTo({ root, index: targetIndex }); | ||
} | ||
}; | ||
@@ -143,0 +177,0 @@ |
@@ -1,70 +0,16 @@ | ||
import { mapItem } from './utils'; | ||
import { debounceHOF } from './utils'; | ||
const debounceHOF = (callback: () => void, ms: number) => { | ||
let timeout: any; | ||
return () => { | ||
if (timeout) clearTimeout(timeout); | ||
timeout = setTimeout(() => { | ||
timeout = null; | ||
callback(); | ||
}, ms); | ||
}; | ||
}; | ||
const getVisibleChildren = ($viewport: HTMLDivElement) => { | ||
if (!$viewport) return { children: [], childrenInCenter: 0 }; | ||
const viewport = { | ||
left: $viewport.scrollLeft, | ||
width: $viewport.offsetWidth, | ||
right: $viewport.scrollLeft + $viewport.offsetWidth, | ||
top: $viewport.scrollTop, | ||
height: $viewport.offsetHeight, | ||
bottom: $viewport.scrollTop + $viewport.offsetHeight, | ||
offsetLeft: $viewport.offsetLeft, | ||
offsetTop: $viewport.offsetTop, | ||
centerHorizontal: $viewport.scrollLeft + $viewport.offsetWidth / 2, | ||
centerVertical: $viewport.scrollTop + $viewport.offsetHeight / 2, | ||
}; | ||
const children = []; | ||
const elements = $viewport.children; | ||
let childrenInCenter = 0; | ||
for (let index = 0; index < elements.length; index++) { | ||
const element = elements[index] as HTMLElement; | ||
const elementBounds = mapItem({ element, viewport }); | ||
const isVisibleHorizontally = | ||
elementBounds.left >= viewport.left && | ||
elementBounds.right <= viewport.right; | ||
const isVisibleVertically = | ||
elementBounds.top >= viewport.top && | ||
elementBounds.bottom <= viewport.bottom; | ||
const isInCenterHorizontally = | ||
elementBounds.left <= viewport.centerHorizontal && | ||
elementBounds.right >= viewport.centerHorizontal; | ||
const isInCenterVertically = | ||
elementBounds.top <= viewport.centerVertical && | ||
elementBounds.bottom >= viewport.centerVertical; | ||
if (isVisibleHorizontally && isVisibleVertically) { | ||
children.push(index); | ||
} | ||
if (isInCenterHorizontally && isInCenterVertically) { | ||
childrenInCenter = index; | ||
} | ||
} | ||
return { children, childrenInCenter }; | ||
}; | ||
export const getActiveSnap = ({ | ||
root, | ||
onChange, | ||
snapAlign = 'start' | ||
}: { | ||
root: HTMLDivElement; | ||
snapPerPage?: number; | ||
snapAlign?: 'start' | 'center' | 'end'; | ||
onChange?: (snapIndex: number) => void; | ||
}) => { | ||
let activeSnapObserver: IntersectionObserver; | ||
let firstSlideObserver: IntersectionObserver; | ||
let lastSlideObserver: IntersectionObserver; | ||
let activeSnapIndex: number; | ||
let timeout: number | null = null; | ||
let isScrolling = false; | ||
@@ -84,4 +30,2 @@ const children = root.children; | ||
activeSnapObserver.disconnect(); | ||
firstSlideObserver.disconnect(); | ||
lastSlideObserver.disconnect(); | ||
window.removeEventListener('resize', onResizeWithDebounce); | ||
@@ -103,135 +47,139 @@ }; | ||
const handleScrolling = () => { | ||
isScrolling = true; | ||
if (timeout) clearTimeout(timeout); | ||
timeout = setTimeout(() => { | ||
// if (root.scrollLeft === 0) { | ||
// setSnapIndex(0); | ||
// } else if (root.scrollLeft === root.scrollWidth - root.offsetWidth) { | ||
// setSnapIndex(children.length - 1); | ||
// } | ||
isScrolling = false; | ||
}, 50) as any; | ||
}; | ||
const init = () => { | ||
const rootWidth = root.offsetWidth; | ||
const rootMargin = `0px 0px 0px -${rootWidth / 2}px`; | ||
const marginLeft = getComputedStyle(children[0]).marginLeft; | ||
const marginRight = getComputedStyle(children[children.length - 1]) | ||
.marginRight; | ||
const rootMarginEdges = `0px -${marginLeft} 0px -${marginRight}`; | ||
activeSnapIndex = root.scrollLeft | ||
? getVisibleChildren(root).childrenInCenter | ||
: 0; | ||
activeSnapIndex = 0; | ||
activeSnapObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
switch (snapAlign) { | ||
case 'start': | ||
activeSnapObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
const entryIndex = Array.prototype.indexOf.call( | ||
children, | ||
entry.target | ||
); | ||
if (activeSnapIndex === null) return; | ||
// entry.rootBounds is wrong on Webkit if there is a padding on the root | ||
// so we use the BoundingClientRect instead | ||
const rootBb = root.getBoundingClientRect(); | ||
const entryIndex = Array.prototype.indexOf.call( | ||
children, | ||
entry.target | ||
); | ||
if (activeSnapIndex !== null) { | ||
// If next | ||
if ( | ||
entry.intersectionRatio <= 0.51 && | ||
entryIndex > activeSnapIndex && | ||
entry.boundingClientRect.left < rootBb.left + rootBb.width / 2 | ||
) { | ||
// console.log('next'); | ||
activeSnapObserver.unobserve(entry.target); | ||
children[entryIndex - 2] && | ||
activeSnapObserver.unobserve(children[entryIndex - 2]); | ||
if (entry.isIntersecting && entry.boundingClientRect.right > 0) { | ||
setSnapIndex(entryIndex + 1); | ||
return; | ||
} | ||
if (children[entryIndex + 1]) { | ||
activeSnapObserver.observe(children[entryIndex + 1]); | ||
if ( | ||
!entry.isIntersecting && | ||
entry.intersectionRatio > 0 && | ||
entry.boundingClientRect.right > 0 | ||
) { | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
}); | ||
}, | ||
{ | ||
root, | ||
rootMargin: `0px -100% 0px 50%`, | ||
threshold: [0.5], | ||
} | ||
); | ||
break; | ||
case 'end': | ||
activeSnapObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
if (children[entryIndex - 1]) | ||
activeSnapObserver.observe(children[entryIndex - 1]); | ||
if (activeSnapIndex === null || !isScrolling) return; | ||
return; | ||
} | ||
const entryIndex = Array.prototype.indexOf.call( | ||
children, | ||
entry.target | ||
); | ||
// If previous | ||
if ( | ||
entryIndex <= activeSnapIndex && | ||
entry.intersectionRatio >= 0.49 && | ||
entry.rootBounds && | ||
entry.boundingClientRect.right > rootBb.left + rootBb.width / 2 | ||
) { | ||
// console.log('previous'); | ||
activeSnapObserver.unobserve(entry.target); | ||
children[entryIndex + 2] && | ||
activeSnapObserver.unobserve(children[entryIndex + 2]); | ||
if ( | ||
entry.isIntersecting && | ||
entry.boundingClientRect.left < rootWidth | ||
) { | ||
setSnapIndex(entryIndex - 1); | ||
return; | ||
} | ||
if (children[entryIndex - 1]) { | ||
activeSnapObserver.observe(children[entryIndex - 1]); | ||
if ( | ||
!entry.isIntersecting && | ||
entry.intersectionRatio > 0 && | ||
entry.boundingClientRect.left < rootWidth | ||
) { | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
if (children[entryIndex + 1]) | ||
activeSnapObserver.observe(children[entryIndex + 1]); | ||
} | ||
}); | ||
}, | ||
{ | ||
root, | ||
rootMargin: `0px 50% 0px -100%`, | ||
threshold: [0.5], | ||
} | ||
}); | ||
}, | ||
{ | ||
root, | ||
rootMargin, | ||
threshold: [0.49, 0.51], | ||
} | ||
); | ||
); | ||
break; | ||
case 'center': | ||
default: | ||
activeSnapObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
// console.log('active snap: ', entry); | ||
firstSlideObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
if ( | ||
entry.isIntersecting && | ||
entry.intersectionRatio >= 0.99 && | ||
activeSnapIndex !== 0 | ||
) { | ||
activeSnapObserver.unobserve(children[activeSnapIndex]); | ||
activeSnapObserver.observe(children[1]); | ||
if (activeSnapIndex === null || !isScrolling || !entry.rootBounds) | ||
return; | ||
setSnapIndex(0); | ||
} | ||
}); | ||
}, | ||
{ | ||
root, | ||
rootMargin: rootMarginEdges, | ||
threshold: [0, 0.99], | ||
} | ||
); | ||
if ( | ||
entry.intersectionRatio >= 0.49 && | ||
entry.intersectionRatio <= 0.51 && | ||
entry.boundingClientRect.left <= | ||
entry.rootBounds.left + | ||
entry.boundingClientRect.width * 0.01 && | ||
entry.boundingClientRect.right >= | ||
entry.rootBounds.left + entry.boundingClientRect.width * 0.01 | ||
) { | ||
const entryIndex = Array.prototype.indexOf.call( | ||
children, | ||
entry.target | ||
); | ||
lastSlideObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
if ( | ||
entry.isIntersecting && | ||
entry.intersectionRatio >= 0.99 && | ||
activeSnapIndex !== children.length - 1 | ||
) { | ||
activeSnapObserver.unobserve(children[activeSnapIndex]); | ||
activeSnapObserver.observe(children[children.length - 2]); | ||
setSnapIndex(children.length - 1); | ||
setSnapIndex(entryIndex); | ||
return; | ||
} | ||
}); | ||
}, | ||
{ | ||
root, | ||
rootMargin: `0px 0px 0px -50%`, | ||
threshold: [0.49, 0.51], | ||
} | ||
}); | ||
}, | ||
{ | ||
root, | ||
rootMargin: rootMarginEdges, | ||
threshold: [0, 0.99], | ||
} | ||
); | ||
); | ||
break; | ||
} | ||
// Set intersection observers | ||
if (children[activeSnapIndex - 1]) | ||
activeSnapObserver.observe(children[activeSnapIndex - 1]); | ||
activeSnapObserver.observe( | ||
children[activeSnapIndex + 1] | ||
? children[activeSnapIndex + 1] | ||
: children[activeSnapIndex] | ||
); | ||
firstSlideObserver.observe(children[0]); | ||
lastSlideObserver.observe(children[children.length - 1]); | ||
root.addEventListener('scroll', handleScrolling); | ||
Array.from(root.children).forEach((child) => { | ||
activeSnapObserver.observe(child); | ||
}); | ||
window.addEventListener('resize', onResizeWithDebounce); | ||
@@ -238,0 +186,0 @@ }; |
@@ -57,10 +57,7 @@ import { mapItem, mapStyles } from './utils'; | ||
let target = { left: 0, top: 0 }; | ||
switch (item.snapAlign) { | ||
case 'start': | ||
target = { | ||
left: | ||
item.left - | ||
item.paddingLeft - | ||
viewport.paddingLeft - | ||
viewport.scrollPaddingLeft, | ||
left: item.left - viewport.paddingLeft - viewport.scrollPaddingLeft, | ||
top: | ||
@@ -119,3 +116,3 @@ item.top - | ||
return { cancel, index }; | ||
return { cancel }; | ||
}; |
@@ -81,1 +81,12 @@ export const mergeStyles = (...classnames: (string | null | undefined)[]) => | ||
); | ||
export const debounceHOF = (callback: () => void, ms: number) => { | ||
let timeout: any; | ||
return () => { | ||
if (timeout) clearTimeout(timeout); | ||
timeout = setTimeout(() => { | ||
timeout = null; | ||
callback(); | ||
}, ms); | ||
}; | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
139650
1763