Video Controls are not working on mobile devices

i am making a custom video player in next js using the react-player library, in addition to the normal controls i am adding a thumbnail preview onhover in desktop and ontouch move in case of touc devices controls are working fine on the desktop, but when i open it in ios or android devices video player is not seeking the video to the touch location and upon moving finger on the seekbar it only shows the preview of the video and slider doesnot changes its position,

VideoPlayer component looks like this

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>
const VideoPlayer = () => {
//refs for video player and video preview
const videoRef = useRef(null);
const previewVideoRef = useRef(null);
const [videoState, setVideoState] = useState({
playing: true,
isPlaying: true, //added to preserve the playing status of the video when seeked
played: 0,
playbackRate: 1.0,
seeking: false,
duration: 0,
loaded: 0,
});
const {
playing,
played,
playbackRate,
seeking,
duration,
isPlaying,
loaded,
} = videoState;
//showing the total duration of video
const handleDuration = (duration) => {
setVideoState((prevValue) => ({ ...prevValue, duration }));
};
//pause and play the video
const playPauseHandler = () => {
setVideoState((prevValue) => ({
...prevValue,
playing: !prevValue.playing,
isPlaying: !prevValue.isPlaying,
}));
};
//seekbar moving with video progress
const progressHandler = (e) => {
if (!seeking) {
setVideoState((prevValue) => ({ ...prevValue, ...e }));
}
};
//changing seekbar position
const seekHandler = (e, newValue) => {
const playedValue = parseFloat(newValue);
if (Number.isFinite(playedValue)) {
setVideoState((prevValue) => ({ ...prevValue, played: playedValue }));
if (seeking)
videoRef.current.seekTo(
previewVideoRef.current.getInternalPlayer().currentTime
);
}
};
//when seekbar is dragged with a click
const seekMouseDownHandler = () => {
setVideoState((prevValue) => ({
...prevValue,
seeking: true,
playing: false,
}));
};
//when click is released
const seekMouseUpHandler = (e) => {
setVideoState((prevValue) => ({
...prevValue,
seeking: false,
playing: isPlaying,
}));
};
//restart the video
const handleRestart = () => {
videoRef.current.seekTo(0);
setVideoState((prevValue) => ({ ...prevValue, playing: true }));
};
//controlling playback rate
const handlePlaybackRateChange = (rate) => {
setVideoState((prevValue) => ({ ...prevValue, playbackRate: rate }));
};
return (
<TransformWrapper>
{({ zoomIn, zoomOut, ...rest }) => (
<>
<div className="react-player">
<Flex
gap={0}
align="center"
justify="center"
vertical={true}
className=""
>
<div className="video-player-box">
<TransformComponent>
<ReactPlayer
ref={videoRef}
fallback={<Loading />}
url="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
playing={playing}
width="100%"
muted={true}
playsinline={true}
onDuration={handleDuration}
onProgress={progressHandler}
playbackRate={playbackRate}
/>
</TransformComponent>
</div>
<ReactPlayer
url="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
style={{ display: "none" }}
ref={previewVideoRef}
playing={false}
loop={true}
/>
<VideoControls
videoRef={videoRef}
previewVideoRef={previewVideoRef}
playing={playing}
onPlayPause={playPauseHandler}
played={played}
onSeek={seekHandler}
onSeekMouseUp={seekMouseUpHandler}
onSeekMouseDown={seekMouseDownHandler}
onRestart={handleRestart}
duration={duration}
playbackRate={playbackRate}
onPlaybackRateChange={handlePlaybackRateChange}
loaded={loaded}
/>
</Flex>
</div>
</>
)}
</TransformWrapper>
);
};
export default VideoPlayer;
</code>
<code> const VideoPlayer = () => { //refs for video player and video preview const videoRef = useRef(null); const previewVideoRef = useRef(null); const [videoState, setVideoState] = useState({ playing: true, isPlaying: true, //added to preserve the playing status of the video when seeked played: 0, playbackRate: 1.0, seeking: false, duration: 0, loaded: 0, }); const { playing, played, playbackRate, seeking, duration, isPlaying, loaded, } = videoState; //showing the total duration of video const handleDuration = (duration) => { setVideoState((prevValue) => ({ ...prevValue, duration })); }; //pause and play the video const playPauseHandler = () => { setVideoState((prevValue) => ({ ...prevValue, playing: !prevValue.playing, isPlaying: !prevValue.isPlaying, })); }; //seekbar moving with video progress const progressHandler = (e) => { if (!seeking) { setVideoState((prevValue) => ({ ...prevValue, ...e })); } }; //changing seekbar position const seekHandler = (e, newValue) => { const playedValue = parseFloat(newValue); if (Number.isFinite(playedValue)) { setVideoState((prevValue) => ({ ...prevValue, played: playedValue })); if (seeking) videoRef.current.seekTo( previewVideoRef.current.getInternalPlayer().currentTime ); } }; //when seekbar is dragged with a click const seekMouseDownHandler = () => { setVideoState((prevValue) => ({ ...prevValue, seeking: true, playing: false, })); }; //when click is released const seekMouseUpHandler = (e) => { setVideoState((prevValue) => ({ ...prevValue, seeking: false, playing: isPlaying, })); }; //restart the video const handleRestart = () => { videoRef.current.seekTo(0); setVideoState((prevValue) => ({ ...prevValue, playing: true })); }; //controlling playback rate const handlePlaybackRateChange = (rate) => { setVideoState((prevValue) => ({ ...prevValue, playbackRate: rate })); }; return ( <TransformWrapper> {({ zoomIn, zoomOut, ...rest }) => ( <> <div className="react-player"> <Flex gap={0} align="center" justify="center" vertical={true} className="" > <div className="video-player-box"> <TransformComponent> <ReactPlayer ref={videoRef} fallback={<Loading />} url="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" playing={playing} width="100%" muted={true} playsinline={true} onDuration={handleDuration} onProgress={progressHandler} playbackRate={playbackRate} /> </TransformComponent> </div> <ReactPlayer url="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" style={{ display: "none" }} ref={previewVideoRef} playing={false} loop={true} /> <VideoControls videoRef={videoRef} previewVideoRef={previewVideoRef} playing={playing} onPlayPause={playPauseHandler} played={played} onSeek={seekHandler} onSeekMouseUp={seekMouseUpHandler} onSeekMouseDown={seekMouseDownHandler} onRestart={handleRestart} duration={duration} playbackRate={playbackRate} onPlaybackRateChange={handlePlaybackRateChange} loaded={loaded} /> </Flex> </div> </> )} </TransformWrapper> ); }; export default VideoPlayer; </code>

const VideoPlayer = () => {

  //refs for video player and video preview
  const videoRef = useRef(null);
  const previewVideoRef = useRef(null);

  const [videoState, setVideoState] = useState({
    playing: true,
    isPlaying: true, //added to preserve the playing status of the video when seeked
    played: 0,
    playbackRate: 1.0,
    seeking: false,
    duration: 0,
    loaded: 0,
  });
  const {
    playing,
    played,
    playbackRate,
    seeking,
    duration,
    isPlaying,
    loaded,
  } = videoState;

  //showing the total duration of video
  const handleDuration = (duration) => {
    setVideoState((prevValue) => ({ ...prevValue, duration }));
  };

  //pause and play the video
  const playPauseHandler = () => {
    setVideoState((prevValue) => ({
      ...prevValue,
      playing: !prevValue.playing,
      isPlaying: !prevValue.isPlaying,
    }));
  };

  //seekbar moving with video progress
  const progressHandler = (e) => {
    if (!seeking) {
      setVideoState((prevValue) => ({ ...prevValue, ...e }));
    }
  };

  //changing seekbar position

  const seekHandler = (e, newValue) => {
    const playedValue = parseFloat(newValue);
    if (Number.isFinite(playedValue)) {
      setVideoState((prevValue) => ({ ...prevValue, played: playedValue }));
      if (seeking)
        videoRef.current.seekTo(
          previewVideoRef.current.getInternalPlayer().currentTime
        );
    }
  };

  //when seekbar is dragged with a click

  const seekMouseDownHandler = () => {
    setVideoState((prevValue) => ({
      ...prevValue,
      seeking: true,
      playing: false,
    }));
  };

  //when click is released

  const seekMouseUpHandler = (e) => {
    setVideoState((prevValue) => ({
      ...prevValue,
      seeking: false,
      playing: isPlaying,
    }));
  };

  //restart the video

  const handleRestart = () => {
    videoRef.current.seekTo(0);
    setVideoState((prevValue) => ({ ...prevValue, playing: true }));
  };

  //controlling playback rate

  const handlePlaybackRateChange = (rate) => {
    setVideoState((prevValue) => ({ ...prevValue, playbackRate: rate }));
  };
  return (
    <TransformWrapper>
      {({ zoomIn, zoomOut, ...rest }) => (
        <>
          <div className="react-player">
            <Flex
              gap={0}
              align="center"
              justify="center"
              vertical={true}
              className=""
            >
              <div className="video-player-box">
                <TransformComponent>
                  <ReactPlayer
                    ref={videoRef}
                    fallback={<Loading />}
                    url="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
                    playing={playing}
                    width="100%"
                    muted={true}
                    playsinline={true}
                    onDuration={handleDuration}
                    onProgress={progressHandler}
                    playbackRate={playbackRate}
                  />
                </TransformComponent>
              </div>
              <ReactPlayer
                url="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
                style={{ display: "none" }}
                ref={previewVideoRef}
                playing={false}
                loop={true}
              />

              <VideoControls
                videoRef={videoRef}
                previewVideoRef={previewVideoRef}
                playing={playing}
                onPlayPause={playPauseHandler}
                played={played}
                onSeek={seekHandler}
                onSeekMouseUp={seekMouseUpHandler}
                onSeekMouseDown={seekMouseDownHandler}
                onRestart={handleRestart}
                duration={duration}
                playbackRate={playbackRate}
                onPlaybackRateChange={handlePlaybackRateChange}
                loaded={loaded}
              />
            </Flex>
          </div>
        </>
      )}
    </TransformWrapper>
  );
};

export default VideoPlayer;

and the VideoControls look like this

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>
function parseDate(date) {
if (date) return dayjs(date).format("hh:mm:ss A");
else return "";
}
const calcSliderPosition = (e) => {
let clientX;
// Check if it's a touch event
if (e.type === "touchmove") {
// Use the clientX property from the first touch point
clientX = e.touches[0].clientX;
} else {
// It's a mouse event, use offsetX
clientX = e.nativeEvent.offsetX;
}
return (
(clientX < 0 ? 0 : clientX / e.target.clientWidth) *
e.target.getAttribute("max")
);
};
const VideoControls = ({
playing,
videoRef,
previewVideoRef,
onPlayPause,
played,
onSeek,
onSeekMouseUp,
onSeekMouseDown,
onRestart,
duration,
playbackRate,
onPlaybackRateChange,
loaded,
}) => {
const { details } = useAppSelector((state) => state.alarms.selectedAlarm);
const [position, setPosition] = useState(0);
const [previewSrc, setPreviewSrc] = useState(null);
function handlePosition(e) {
const THUMBNAIL_MIDDLE_POINT = 88;
const THUMBNAIL_WIDTH = 178;
const PRECISION_VALUE = 1;
const TOTAL_WIDTH = e.target.offsetWidth;
let clientX;
// Check if it's a touch event
if (e.type === "touchmove") {
// Use the clientX property from the first touch point
clientX = e.touches[0].clientX - e.target.getBoundingClientRect().left;
} else {
// It's a mouse event, use offsetX
clientX = e.nativeEvent.offsetX;
}
//change the position only when the pointer is between the video length
if (clientX > 1 && clientX < TOTAL_WIDTH) {
//check for extreme left thumbnail preview
if (clientX > THUMBNAIL_MIDDLE_POINT)
if (clientX < TOTAL_WIDTH - THUMBNAIL_MIDDLE_POINT)
setPosition(clientX - THUMBNAIL_MIDDLE_POINT);
else {
let nextPosition = clientX - clientX + TOTAL_WIDTH - THUMBNAIL_WIDTH;
if (
nextPosition < position - PRECISION_VALUE ||
nextPosition > position + PRECISION_VALUE
)
setPosition(nextPosition);
}
//in case there is not enough space hold the thumnail to the left
else {
// let nextPosition = e.nativeEvent.layerX - e.nativeEvent.offsetX;
let nextPosition = clientX - clientX;
if (
nextPosition < position - PRECISION_VALUE ||
nextPosition > position + PRECISION_VALUE
)
setPosition(nextPosition);
}
}
}
const generatePreview = async (e) => {
try {
let seekTime = calcSliderPosition(e);
let newTime = seekTime * previewVideoRef.current.getDuration();
let prevTime = previewVideoRef.current.getCurrentTime();
const PRECISION_VALUE = 0.01;
if (
newTime > prevTime - PRECISION_VALUE &&
newTime < prevTime + PRECISION_VALUE
) {
// console.log("do not create");
} else {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
previewVideoRef.current.seekTo(seekTime);
const video = previewVideoRef.current.getInternalPlayer();
const videoWidth = 1920 / 6;
const videoHeight = 1080 / 6;
setTimeout(() => {
context.drawImage(video, 0, 0, videoWidth, videoHeight);
setPreviewSrc(canvas.toDataURL());
}, 100);
}
handlePosition(e);
} catch (error) {
console.error("Error generating preview:", error);
return null;
}
};
return (
<div className="video-controls">
<div className="slider-container">
<progress className="seek-bar" max={1} value={loaded} />
<input
className="slider"
type="range"
min={0}
max={0.999999}
step="any"
value={played}
// onChange={(e) => onSeek(e, e.target.value)}
onInput={(e) => onSeek(e, e.target.value)}
onMouseUp={onSeekMouseUp}
onMouseDown={onSeekMouseDown}
onMouseMove={generatePreview}
onMouseLeave={() => {
setPosition(-100);
}}
onTouchStart={(e) => {
e.preventDefault();
onSeekMouseDown(e);
}}
onTouchEnd={(e) => {
e.preventDefault();
const newValue = e.target.value;
onSeekMouseUp(e);
setPosition(-100);
}}
onTouchMove={(e) => {
e.preventDefault();
generatePreview(e);
}}
/>
{previewSrc && position !== -100 && (
<div className="preview-container" style={{ left: position }}>
<Image
priority={true}
width={173}
height={96.89}
className="preview"
src={previewSrc}
alt="Preview"
/>
<span class="preview_time">
{parseDate(
new Date(details?.occurrenceTime).getTime() +
previewVideoRef.current.getCurrentTime() * 1000
)}
</span>
</div>
)}
</div>
<Flex
gap={0}
align="center"
justify="space-between"
vertical={false}
className="video-timer"
>
<div> {parseDate(details?.occurrenceTime)}</div>
<div>
<span className="time-spent">
<Duration seconds={duration * played} />
</span>
<span className="dot" />
<Duration seconds={duration} />
</div>
<div> {parseDate(details?.endTime)}</div>
</Flex>
<Flex gap={28} className="button-controls">
<Image
priority={true}
onClick={() =>
videoRef.current.seekTo(videoRef.current.getCurrentTime() - 5)
}
width={24}
height={24}
src="/svg/skip-video-back.svg"
alt="skip-video-back-icon"
/>
{!playing ? (
<Image
priority={true}
width={24}
height={24}
src="/svg/play-video.svg"
alt="play-icon"
onClick={onPlayPause}
/>
) : (
<Image
priority={true}
width={24}
height={24}
src="/svg/pause-video.svg"
alt="pause-icon"
onClick={onPlayPause}
/>
)}
<Image
priority={true}
onClick={() =>
videoRef.current.seekTo(videoRef.current.getCurrentTime() + 5)
}
width={24}
height={24}
src="/svg/skip-video-forward.svg"
alt="skip-forward-icon"
/>
<Image
priority={true}
onClick={onRestart}
width={24}
height={24}
src="/svg/restart-video.svg"
alt="restart-icon"
/>
<PlaybackSpeed
playbackRate={playbackRate}
onPlaybackRateChange={onPlaybackRateChange}
/>
</Flex>
</div>
);
};
export default VideoControls;
</code>
<code> function parseDate(date) { if (date) return dayjs(date).format("hh:mm:ss A"); else return ""; } const calcSliderPosition = (e) => { let clientX; // Check if it's a touch event if (e.type === "touchmove") { // Use the clientX property from the first touch point clientX = e.touches[0].clientX; } else { // It's a mouse event, use offsetX clientX = e.nativeEvent.offsetX; } return ( (clientX < 0 ? 0 : clientX / e.target.clientWidth) * e.target.getAttribute("max") ); }; const VideoControls = ({ playing, videoRef, previewVideoRef, onPlayPause, played, onSeek, onSeekMouseUp, onSeekMouseDown, onRestart, duration, playbackRate, onPlaybackRateChange, loaded, }) => { const { details } = useAppSelector((state) => state.alarms.selectedAlarm); const [position, setPosition] = useState(0); const [previewSrc, setPreviewSrc] = useState(null); function handlePosition(e) { const THUMBNAIL_MIDDLE_POINT = 88; const THUMBNAIL_WIDTH = 178; const PRECISION_VALUE = 1; const TOTAL_WIDTH = e.target.offsetWidth; let clientX; // Check if it's a touch event if (e.type === "touchmove") { // Use the clientX property from the first touch point clientX = e.touches[0].clientX - e.target.getBoundingClientRect().left; } else { // It's a mouse event, use offsetX clientX = e.nativeEvent.offsetX; } //change the position only when the pointer is between the video length if (clientX > 1 && clientX < TOTAL_WIDTH) { //check for extreme left thumbnail preview if (clientX > THUMBNAIL_MIDDLE_POINT) if (clientX < TOTAL_WIDTH - THUMBNAIL_MIDDLE_POINT) setPosition(clientX - THUMBNAIL_MIDDLE_POINT); else { let nextPosition = clientX - clientX + TOTAL_WIDTH - THUMBNAIL_WIDTH; if ( nextPosition < position - PRECISION_VALUE || nextPosition > position + PRECISION_VALUE ) setPosition(nextPosition); } //in case there is not enough space hold the thumnail to the left else { // let nextPosition = e.nativeEvent.layerX - e.nativeEvent.offsetX; let nextPosition = clientX - clientX; if ( nextPosition < position - PRECISION_VALUE || nextPosition > position + PRECISION_VALUE ) setPosition(nextPosition); } } } const generatePreview = async (e) => { try { let seekTime = calcSliderPosition(e); let newTime = seekTime * previewVideoRef.current.getDuration(); let prevTime = previewVideoRef.current.getCurrentTime(); const PRECISION_VALUE = 0.01; if ( newTime > prevTime - PRECISION_VALUE && newTime < prevTime + PRECISION_VALUE ) { // console.log("do not create"); } else { const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); previewVideoRef.current.seekTo(seekTime); const video = previewVideoRef.current.getInternalPlayer(); const videoWidth = 1920 / 6; const videoHeight = 1080 / 6; setTimeout(() => { context.drawImage(video, 0, 0, videoWidth, videoHeight); setPreviewSrc(canvas.toDataURL()); }, 100); } handlePosition(e); } catch (error) { console.error("Error generating preview:", error); return null; } }; return ( <div className="video-controls"> <div className="slider-container"> <progress className="seek-bar" max={1} value={loaded} /> <input className="slider" type="range" min={0} max={0.999999} step="any" value={played} // onChange={(e) => onSeek(e, e.target.value)} onInput={(e) => onSeek(e, e.target.value)} onMouseUp={onSeekMouseUp} onMouseDown={onSeekMouseDown} onMouseMove={generatePreview} onMouseLeave={() => { setPosition(-100); }} onTouchStart={(e) => { e.preventDefault(); onSeekMouseDown(e); }} onTouchEnd={(e) => { e.preventDefault(); const newValue = e.target.value; onSeekMouseUp(e); setPosition(-100); }} onTouchMove={(e) => { e.preventDefault(); generatePreview(e); }} /> {previewSrc && position !== -100 && ( <div className="preview-container" style={{ left: position }}> <Image priority={true} width={173} height={96.89} className="preview" src={previewSrc} alt="Preview" /> <span class="preview_time"> {parseDate( new Date(details?.occurrenceTime).getTime() + previewVideoRef.current.getCurrentTime() * 1000 )} </span> </div> )} </div> <Flex gap={0} align="center" justify="space-between" vertical={false} className="video-timer" > <div> {parseDate(details?.occurrenceTime)}</div> <div> <span className="time-spent"> <Duration seconds={duration * played} /> </span> <span className="dot" /> <Duration seconds={duration} /> </div> <div> {parseDate(details?.endTime)}</div> </Flex> <Flex gap={28} className="button-controls"> <Image priority={true} onClick={() => videoRef.current.seekTo(videoRef.current.getCurrentTime() - 5) } width={24} height={24} src="/svg/skip-video-back.svg" alt="skip-video-back-icon" /> {!playing ? ( <Image priority={true} width={24} height={24} src="/svg/play-video.svg" alt="play-icon" onClick={onPlayPause} /> ) : ( <Image priority={true} width={24} height={24} src="/svg/pause-video.svg" alt="pause-icon" onClick={onPlayPause} /> )} <Image priority={true} onClick={() => videoRef.current.seekTo(videoRef.current.getCurrentTime() + 5) } width={24} height={24} src="/svg/skip-video-forward.svg" alt="skip-forward-icon" /> <Image priority={true} onClick={onRestart} width={24} height={24} src="/svg/restart-video.svg" alt="restart-icon" /> <PlaybackSpeed playbackRate={playbackRate} onPlaybackRateChange={onPlaybackRateChange} /> </Flex> </div> ); }; export default VideoControls; </code>

function parseDate(date) {
  if (date) return dayjs(date).format("hh:mm:ss A");
  else return "";
}

const calcSliderPosition = (e) => {
  let clientX;
  // Check if it's a touch event
  if (e.type === "touchmove") {
    // Use the clientX property from the first touch point
    clientX = e.touches[0].clientX;
  } else {
    // It's a mouse event, use offsetX
    clientX = e.nativeEvent.offsetX;
  }
  return (
    (clientX < 0 ? 0 : clientX / e.target.clientWidth) *
    e.target.getAttribute("max")
  );
};

const VideoControls = ({
  playing,
  videoRef,
  previewVideoRef,
  onPlayPause,
  played,
  onSeek,
  onSeekMouseUp,
  onSeekMouseDown,
  onRestart,
  duration,
  playbackRate,
  onPlaybackRateChange,
  loaded,
}) => {
  const { details } = useAppSelector((state) => state.alarms.selectedAlarm);
  const [position, setPosition] = useState(0);
  const [previewSrc, setPreviewSrc] = useState(null);

  function handlePosition(e) {
    const THUMBNAIL_MIDDLE_POINT = 88;
    const THUMBNAIL_WIDTH = 178;
    const PRECISION_VALUE = 1;
    const TOTAL_WIDTH = e.target.offsetWidth;

    let clientX;
    // Check if it's a touch event
    if (e.type === "touchmove") {
      // Use the clientX property from the first touch point
      clientX = e.touches[0].clientX - e.target.getBoundingClientRect().left;
    } else {
      // It's a mouse event, use offsetX
      clientX = e.nativeEvent.offsetX;
    }

    //change the position only when the pointer is between the video length
    if (clientX > 1 && clientX < TOTAL_WIDTH) {
      //check for extreme left thumbnail preview
      if (clientX > THUMBNAIL_MIDDLE_POINT)
        if (clientX < TOTAL_WIDTH - THUMBNAIL_MIDDLE_POINT)
          setPosition(clientX - THUMBNAIL_MIDDLE_POINT);
        else {
          let nextPosition = clientX - clientX + TOTAL_WIDTH - THUMBNAIL_WIDTH;
          if (
            nextPosition < position - PRECISION_VALUE ||
            nextPosition > position + PRECISION_VALUE
          )
            setPosition(nextPosition);
        }
      //in case there is not enough space hold the thumnail to the left
      else {
        // let nextPosition = e.nativeEvent.layerX - e.nativeEvent.offsetX;
        let nextPosition = clientX - clientX;
        if (
          nextPosition < position - PRECISION_VALUE ||
          nextPosition > position + PRECISION_VALUE
        )
          setPosition(nextPosition);
      }
    }
  }

  const generatePreview = async (e) => {
    try {
      let seekTime = calcSliderPosition(e);
      let newTime = seekTime * previewVideoRef.current.getDuration();
      let prevTime = previewVideoRef.current.getCurrentTime();

      const PRECISION_VALUE = 0.01;
      if (
        newTime > prevTime - PRECISION_VALUE &&
        newTime < prevTime + PRECISION_VALUE
      ) {
        // console.log("do not create");
      } else {
        const canvas = document.createElement("canvas");
        const context = canvas.getContext("2d");

        previewVideoRef.current.seekTo(seekTime);
        const video = previewVideoRef.current.getInternalPlayer();
        const videoWidth = 1920 / 6;
        const videoHeight = 1080 / 6;
        setTimeout(() => {
          context.drawImage(video, 0, 0, videoWidth, videoHeight);
          setPreviewSrc(canvas.toDataURL());
        }, 100);
      }
      handlePosition(e);
    } catch (error) {
      console.error("Error generating preview:", error);
      return null;
    }
  };

  return (
    <div className="video-controls">
      <div className="slider-container">
        <progress className="seek-bar" max={1} value={loaded} />
        <input
          className="slider"
          type="range"
          min={0}
          max={0.999999}
          step="any"
          value={played}
          // onChange={(e) => onSeek(e, e.target.value)}
          onInput={(e) => onSeek(e, e.target.value)}
          onMouseUp={onSeekMouseUp}
          onMouseDown={onSeekMouseDown}
          onMouseMove={generatePreview}
          onMouseLeave={() => {
            setPosition(-100);
          }}
          onTouchStart={(e) => {
            e.preventDefault();
            onSeekMouseDown(e);
          }}
          onTouchEnd={(e) => {
            e.preventDefault();
            const newValue = e.target.value;
            onSeekMouseUp(e);
            setPosition(-100);
          }}
          onTouchMove={(e) => {
            e.preventDefault();
            generatePreview(e);
          }}
        />

        {previewSrc && position !== -100 && (
          <div className="preview-container" style={{ left: position }}>
            <Image
              priority={true}
              width={173}
              height={96.89}
              className="preview"
              src={previewSrc}
              alt="Preview"
            />
            <span class="preview_time">
              {parseDate(
                new Date(details?.occurrenceTime).getTime() +
                  previewVideoRef.current.getCurrentTime() * 1000
              )}
            </span>
          </div>
        )}
      </div>

      <Flex
        gap={0}
        align="center"
        justify="space-between"
        vertical={false}
        className="video-timer"
      >
        <div> {parseDate(details?.occurrenceTime)}</div>
        <div>
          <span className="time-spent">
            <Duration seconds={duration * played} />
          </span>
          <span className="dot" />
          <Duration seconds={duration} />
        </div>
        <div> {parseDate(details?.endTime)}</div>
      </Flex>
      <Flex gap={28} className="button-controls">
        <Image
          priority={true}
          onClick={() =>
            videoRef.current.seekTo(videoRef.current.getCurrentTime() - 5)
          }
          width={24}
          height={24}
          src="/svg/skip-video-back.svg"
          alt="skip-video-back-icon"
        />

        {!playing ? (
          <Image
            priority={true}
            width={24}
            height={24}
            src="/svg/play-video.svg"
            alt="play-icon"
            onClick={onPlayPause}
          />
        ) : (
          <Image
            priority={true}
            width={24}
            height={24}
            src="/svg/pause-video.svg"
            alt="pause-icon"
            onClick={onPlayPause}
          />
        )}

        <Image
          priority={true}
          onClick={() =>
            videoRef.current.seekTo(videoRef.current.getCurrentTime() + 5)
          }
          width={24}
          height={24}
          src="/svg/skip-video-forward.svg"
          alt="skip-forward-icon"
        />

        <Image
          priority={true}
          onClick={onRestart}
          width={24}
          height={24}
          src="/svg/restart-video.svg"
          alt="restart-icon"
        />
        <PlaybackSpeed
          playbackRate={playbackRate}
          onPlaybackRateChange={onPlaybackRateChange}
        />
      </Flex>
    </div>
  );
};

export default VideoControls;

i have tried using the ref values directly into these touch functions and also tried without the preventDefault function, but still it is not working

New contributor

Muhammad Ishaq Saqib is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật