Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

136 строки
5.0KB

  1. import * as React from "react";
  2. import {createRef, useEffect, useState} from "react";
  3. import "./photo-viewer.scss"
  4. import {ArrowLeft, ArrowRight, Close} from "@mui/icons-material";
  5. import {useCurrentInstance} from "../../services/current-instance/use-current-instance";
  6. export function PhotoViewer() {
  7. const {selectedAlbum, selectedPhotoIndex, setSelectedPhotoIndex} = useCurrentInstance();
  8. const [touchStartX, setTouchStartX] = useState();
  9. const [offset, setOffset] = useState(0);
  10. const elPhotoViewer = createRef();
  11. const hasNext = selectedPhotoIndex < (selectedAlbum?.photos.length || 0) - 1;
  12. const hasPrevious = selectedPhotoIndex > 0;
  13. useEffect(() => {
  14. if (elPhotoViewer.current) {
  15. elPhotoViewer.current.focus();
  16. }
  17. }, [elPhotoViewer]);
  18. const handleClick = (e) => {
  19. if (e.target === elPhotoViewer.current) {
  20. setSelectedPhotoIndex(-1);
  21. }
  22. }
  23. const handleGoPrevious = () => {
  24. if (hasPrevious) {
  25. setSelectedPhotoIndex(selectedPhotoIndex - 1);
  26. }
  27. }
  28. const handleGoNext = () => {
  29. if (hasNext) {
  30. setSelectedPhotoIndex(selectedPhotoIndex + 1);
  31. }
  32. }
  33. const handleClose = () => {
  34. setSelectedPhotoIndex(-1);
  35. }
  36. const handleKeyUp = (e) => {
  37. switch (e.code) {
  38. case "ArrowLeft":
  39. handleGoPrevious();
  40. break;
  41. case "ArrowRight":
  42. handleGoNext();
  43. break;
  44. case "Escape":
  45. setSelectedPhotoIndex(-1);
  46. break;
  47. default:
  48. break;
  49. }
  50. }
  51. const handleTouchStart = (/** TouchEvent **/e) => {
  52. if (e.touches.length === 1) {
  53. //just one finger touched
  54. setTouchStartX(e.touches.item(0).clientX);
  55. } else {
  56. //a second finger hit the screen, abort the touch
  57. setTouchStartX(null);
  58. }
  59. }
  60. const handleTouchMove = (/** TouchEvent **/e) => {
  61. if (e.touches.length === 1) {
  62. const end = e.changedTouches.item(0).clientX;
  63. setOffset(end - touchStartX);
  64. } else {
  65. setTouchStartX(null);
  66. setOffset(0);
  67. }
  68. }
  69. const handleTouchEnd = (/** TouchEvent **/e) => {
  70. const minOffset = 100; //at least 100px are a swipe
  71. if (touchStartX) {
  72. //the only finger that hit the screen left it
  73. const end = e.changedTouches.item(0).clientX;
  74. if (end > touchStartX + minOffset) {
  75. //a left -> right swipe
  76. handleGoPrevious();
  77. } else if (end < touchStartX - minOffset) {
  78. //a right -> left swipe
  79. handleGoNext();
  80. }
  81. }
  82. setOffset(0);
  83. }
  84. const previousPhotoStyle = {
  85. right: `calc(100% + ${15 - offset}px)`
  86. }
  87. const nextPhotoStyle = {
  88. left: `calc(100% - ${-offset}px)`
  89. }
  90. const photoStyle = {
  91. marginLeft: `${offset}px`
  92. }
  93. return <>
  94. {selectedPhotoIndex >= 0 ?
  95. <div tabIndex={1} ref={elPhotoViewer} id="photoViewer" onClick={handleClick} onKeyUp={handleKeyUp}
  96. onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} onTouchMove={handleTouchMove}>
  97. <div className="body-overlay"></div>
  98. <div className="goto-btn goto-btn--previous" onClick={handleGoPrevious}>
  99. {hasPrevious && <ArrowLeft/>}
  100. </div>
  101. <div id="photoSlider">
  102. {hasPrevious &&
  103. <div className="photo photo--placeholder photo--previous" style={previousPhotoStyle}>
  104. <img src={`${selectedAlbum.photos[selectedPhotoIndex - 1].url}`}
  105. alt={selectedAlbum.photos[selectedPhotoIndex - 1].name}/>
  106. </div>}
  107. <div className="photo photo--invisible">
  108. <img src={`${selectedAlbum.photos[selectedPhotoIndex].url}`}
  109. alt={selectedAlbum.photos[selectedPhotoIndex].name}/>
  110. </div>
  111. <div className="photo photo--placeholder" style={photoStyle}>
  112. <img src={`${selectedAlbum.photos[selectedPhotoIndex].url}`}
  113. alt={selectedAlbum.photos[selectedPhotoIndex].name}/>
  114. </div>
  115. {hasNext && <div className="photo photo--placeholder photo--next" style={nextPhotoStyle}>
  116. <img src={`${selectedAlbum.photos[selectedPhotoIndex + 1].url}`}
  117. alt={selectedAlbum.photos[selectedPhotoIndex + 1].name}/>
  118. </div>}
  119. </div>
  120. <div className="goto-btn goto-btn--next" onClick={handleGoNext}>
  121. {hasNext && <ArrowRight/>}
  122. </div>
  123. <div className="close-btn" onClick={handleClose}>
  124. {hasNext && <Close/>}
  125. </div>
  126. </div> : null}
  127. </>
  128. }