123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- import React, {useRef, useEffect, useState} from 'react'
- export default ({images = [], className, onChange, onClick, ...props}) => {
- const divRef = useRef()
- const containerRef = useRef()
- const autoscroll = useRef(false)
- const inTouch = useRef(false)
- const timeout = useRef(false)
- const [imgs, setImgs] = useState([])
- const [current, setCurrent] = useState(0)
- const currentScrollPosition = useRef(0)
- useEffect(() => {
- if (divRef.current){
- const div = divRef.current
- const totalWidth = imgs.reduce((total, img) => total + (img?.getBoundingClientRect().width || 0),0)
- div.style.minWidth = totalWidth + 'px'
- }
- }, [divRef, imgs])
- const scrollToCurrent = () => {
- const el = containerRef.current
- autoscroll.current = true
- const step = () => {
- if (!autoscroll.current || !el) return;
- const {scrollLeft} = el
- const diff = (currentScrollPosition.current - scrollLeft) /5
- if (Math.abs(diff) > 1){
- el.scrollTo(scrollLeft + diff, 0)
- setTimeout(step, 20)
- }
- else {
- el.scrollTo(currentScrollPosition.current, 0)
- autoscroll.current = false
- }
- }
- step()
- //if (milaCount !== current +1) set(current +1)
- }
- const onTouchEnd = () => {
- if (inTouch.current){
- if (timeout.current) clearInterval(timeout.current)
- timeout.current = setTimeout(onTouchEnd, 200)
- return;
- }
- const {scrollLeft, scrollWidth} = containerRef.current;
- const viewPortWidth = containerRef.current.getBoundingClientRect().width
- let newCurrent;
- if (scrollLeft === 0) {
- newCurrent = 0
- currentScrollPosition.current = 0
- }
- else if (scrollLeft >= scrollWidth - viewPortWidth) {
- newCurrent = (images.length -1)
- currentScrollPosition.current = scrollWidth - viewPortWidth
- }
- else {
- let imgsWidth = 0
- let i = 0
- for (const img of imgs){
- const imgWidth = img?.getBoundingClientRect().width || 0
- imgsWidth += imgWidth
- if (imgsWidth > scrollLeft){
- break;
- }
- i++
- }
- const offset = imgsWidth - scrollLeft
- newCurrent = i
- currentScrollPosition.current = imgsWidth - (imgs?.[i].getBoundingClientRect().width || 0)
- if (offset < viewPortWidth/2){
- currentScrollPosition.current = imgsWidth
- newCurrent++
- }
- }
- setCurrent(newCurrent)
- if (newCurrent === current) scrollToCurrent()
- else if (typeof onChange === 'function') onChange(newCurrent)
- }
- const onScroll = e => {
- //autoscroll.current = false
- if (autoscroll.current) return;
- if (timeout.current) clearInterval(timeout.current)
- timeout.current = setTimeout(onTouchEnd, 200)
- }
- useEffect(scrollToCurrent, [current])
- const isTouchDevice = () => (navigator.maxTouchPoints || 'ontouchstart' in document.documentElement);
- const onImgClick = (e, i) => {
- const touchDevice = isTouchDevice()
- if (touchDevice && typeof onClick === 'function'){
- onClick(i)
- }
- else {
- const viewPortWidth = containerRef.current.getBoundingClientRect().width
- const clickX = e.clientX -e.target.getBoundingClientRect().x
- const {scrollLeft} = containerRef.current;
- if (clickX > viewPortWidth * 0.75) {
- if (i < images.length -1){
- currentScrollPosition.current = scrollLeft + (imgs?.[i].getBoundingClientRect().width || 0)
- setCurrent(i +1)
- scrollToCurrent()
- if (typeof onChange === 'function') onChange(i +1)
- }
- }
- else if (clickX < viewPortWidth * 0.25) {
- if (i > 0){
- currentScrollPosition.current = scrollLeft - (imgs?.[i-1].getBoundingClientRect().width || 0)
- setCurrent(i -1)
- scrollToCurrent()
- if (typeof onChange === 'function') onChange(i -1)
- }
- }
- else if (typeof onClick === 'function'){
- onClick(i)
- }
- }
- }
- return (
- <div className={className}
- style={{cssText: `overflow: auto; -ms-overflow-style: none; scrollbar-width: none; overscroll-behavior-x: none;`}}
- onScroll={onScroll} //TODO: do it only on desktop, on touch devices use onTouchEnd
- ref={containerRef}
- {...isTouchDevice() ? {
- onTouchStart(){
- inTouch.current = true
- autoscroll.current = false
- },
- onTouchEnd(){
- inTouch.current = false
- }
- }: {}}
- >
- <div ref={divRef}>
- {images.map((image, i) => <img src={image}
- key={`image_${i}`}
- alt={`image ${i}`}
- onLoad={e => {imgs[i] = e.target; setImgs([...imgs]) }}
- onClick={e => onImgClick(e, i)}
- />)}
- </div>
- </div>
- )
- }
|