index.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import React, {useRef, useEffect, useState} from 'react'
  2. export const SliderImage = ({images = [], className, onChange, onClick, ...props}) => {
  3. const divRef = useRef()
  4. const containerRef = useRef()
  5. const autoscroll = useRef(false)
  6. const inTouch = useRef(false)
  7. const timeout = useRef(false)
  8. const [imgs, setImgs] = useState([])
  9. const [current, setCurrent] = useState(0)
  10. const currentScrollPosition = useRef(0)
  11. useEffect(() => {
  12. if (divRef.current){
  13. const div = divRef.current
  14. const totalWidth = imgs.reduce((total, img) => total + (img?.getBoundingClientRect().width || 0),0)
  15. div.style.minWidth = totalWidth + 'px'
  16. }
  17. }, [divRef, imgs])
  18. const scrollToCurrent = () => {
  19. if (autoscroll.current || !containerRef.current) return;
  20. const el = containerRef.current
  21. const step = () => {
  22. if (!el) return;
  23. autoscroll.current = true
  24. const {scrollLeft} = el
  25. const diff = (currentScrollPosition.current - scrollLeft) /5
  26. if (Math.abs(diff) > 1){
  27. el.scrollTo(scrollLeft + diff, 0)
  28. setTimeout(step, 20)
  29. }
  30. else {
  31. el.scrollTo(currentScrollPosition.current, 0)
  32. autoscroll.current = false
  33. }
  34. }
  35. step()
  36. //if (milaCount !== current +1) set(current +1)
  37. }
  38. const onTouchEnd = () => {
  39. if (inTouch.current){
  40. if (timeout.current) clearInterval(timeout.current)
  41. timeout.current = setTimeout(onTouchEnd, 200)
  42. return;
  43. }
  44. const {scrollLeft, scrollWidth} = containerRef.current;
  45. const viewPortWidth = containerRef.current.getBoundingClientRect().width
  46. let newCurrent;
  47. if (scrollLeft === 0) {
  48. newCurrent = 0
  49. currentScrollPosition.current = 0
  50. }
  51. else if (scrollLeft >= scrollWidth - viewPortWidth) {
  52. newCurrent = (images.length -1)
  53. currentScrollPosition.current = scrollWidth - viewPortWidth
  54. }
  55. else {
  56. let imgsWidth = 0
  57. let i = 0
  58. for (const img of imgs){
  59. const imgWidth = img?.getBoundingClientRect().width || 0
  60. imgsWidth += imgWidth
  61. if (imgsWidth > scrollLeft){
  62. break;
  63. }
  64. i++
  65. }
  66. const offset = imgsWidth - scrollLeft
  67. newCurrent = i
  68. currentScrollPosition.current = imgsWidth - (imgs?.[i].getBoundingClientRect().width || 0)
  69. if (offset < viewPortWidth/2){
  70. currentScrollPosition.current = imgsWidth
  71. newCurrent++
  72. }
  73. }
  74. setCurrent(newCurrent)
  75. if (newCurrent === current) scrollToCurrent()
  76. else if (typeof onChange === 'function') onChange(newCurrent)
  77. }
  78. const onScroll = e => {
  79. if (autoscroll.current) return;
  80. if (timeout.current) clearInterval(timeout.current)
  81. timeout.current = setTimeout(onTouchEnd, 200)
  82. }
  83. useEffect(scrollToCurrent, [current])
  84. const isTouchDevice = () => (navigator.maxTouchPoints || 'ontouchstart' in document.documentElement);
  85. const onImgClick = (e, i) => {
  86. const touchDevice = isTouchDevice()
  87. if (touchDevice && typeof onClick === 'function'){
  88. onClick(i)
  89. }
  90. else {
  91. const viewPortWidth = containerRef.current.getBoundingClientRect().width
  92. const clickX = e.clientX -e.target.getBoundingClientRect().x
  93. const {scrollLeft} = containerRef.current;
  94. if (clickX > viewPortWidth * 0.75) {
  95. if (i < images.length -1){
  96. currentScrollPosition.current = scrollLeft + (imgs?.[i].getBoundingClientRect().width || 0)
  97. setCurrent(i +1)
  98. scrollToCurrent()
  99. if (typeof onChange === 'function') onChange(i +1)
  100. }
  101. }
  102. else if (clickX < viewPortWidth * 0.25) {
  103. if (i > 0){
  104. currentScrollPosition.current = scrollLeft - (imgs?.[i-1].getBoundingClientRect().width || 0)
  105. setCurrent(i -1)
  106. scrollToCurrent()
  107. if (typeof onChange === 'function') onChange(i -1)
  108. }
  109. }
  110. else if (typeof onClick === 'function'){
  111. onClick(i)
  112. }
  113. }
  114. }
  115. return (
  116. <div className={className}
  117. style={{cssText: `overflow: auto; -ms-overflow-style: none; scrollbar-width: none`}}
  118. onScroll={onScroll} //TODO: do it only on desktop, on touch devices use onTouchEnd
  119. ref={containerRef}
  120. {...isTouchDevice() ? {
  121. onTouchStart(){
  122. inTouch.current = true
  123. },
  124. onTouchEnd(){
  125. inTouch.current = false
  126. }
  127. }: {}}
  128. >
  129. <div ref={divRef}>
  130. {images.map((image, i) => <img src={image}
  131. key={`image_${i}`}
  132. alt={`image ${i}`}
  133. onLoad={e => {imgs[i] = e.target; setImgs([...imgs]) }}
  134. onClick={e => onImgClick(e, i)}
  135. />)}
  136. </div>
  137. </div>
  138. )
  139. }