Web:Common:Components:MediaViewer:Sub-Components:Added PlayerTimeline component
This commit is contained in:
parent
2e0aa10efd
commit
c21cf0a3be
@ -0,0 +1,9 @@
|
||||
interface PlayerTimelineProps {
|
||||
value: number;
|
||||
duration: number;
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onMouseEnter: VoidFunction;
|
||||
onMouseLeave: VoidFunction;
|
||||
}
|
||||
|
||||
export default PlayerTimelineProps;
|
@ -0,0 +1,180 @@
|
||||
import { isMobile } from "react-device-detect";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
export const HoverProgress = styled.div`
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
|
||||
height: 6px;
|
||||
|
||||
border-radius: 5px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
`;
|
||||
|
||||
const mobileCss = css`
|
||||
margin-top: 16px;
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
background: #fff;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
background: #fff;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="range"]::-ms-fill-upper {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
background: #fff;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const PlayerTimelineWrapper = styled.div`
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin-top: 92px;
|
||||
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
time {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: -25px;
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
pointer-events: none;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
/* height: 6px; */
|
||||
input {
|
||||
height: 6px;
|
||||
}
|
||||
${HoverProgress} {
|
||||
display: block;
|
||||
}
|
||||
transition: 0.1s height ease-in;
|
||||
}
|
||||
|
||||
&:hover time {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
|
||||
outline: none;
|
||||
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
|
||||
border-radius: 5px;
|
||||
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
background-image: linear-gradient(#fff, #fff);
|
||||
background-repeat: no-repeat;
|
||||
|
||||
z-index: 1;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
input[type="range"]::-ms-fill-upper {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
input[type="range"]::-webkit-slider-thumb {
|
||||
visibility: visible;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
opacity: 1 !important;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
visibility: visible;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
opacity: 1 !important;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input[type="range"]::-ms-fill-upper {
|
||||
visibility: visible;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
opacity: 1 !important;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
${isMobile && mobileCss}
|
||||
`;
|
@ -0,0 +1,117 @@
|
||||
import React, { useRef } from "react";
|
||||
import { formatTime } from "../../helpers";
|
||||
import PlayerTimelineProps from "./PlayerTimeline.props";
|
||||
import { HoverProgress, PlayerTimelineWrapper } from "./PlayerTimeline.styled";
|
||||
|
||||
function PlayerTimeline({
|
||||
value,
|
||||
duration,
|
||||
onChange,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
}: PlayerTimelineProps) {
|
||||
const timelineTooltipRef = useRef<HTMLTimeElement>(null);
|
||||
const timelineRef = useRef<HTMLDivElement>(null);
|
||||
const hoverProgressRef = useRef<HTMLDivElement>(null);
|
||||
const setTimeoutTimelineTooltipRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
const showTimelineTooltip = () => {
|
||||
if (!timelineTooltipRef.current) return;
|
||||
|
||||
const callback = () => {
|
||||
if (timelineTooltipRef.current) {
|
||||
timelineTooltipRef.current.style.removeProperty("display");
|
||||
setTimeoutTimelineTooltipRef.current = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
if (setTimeoutTimelineTooltipRef.current) {
|
||||
clearTimeout(setTimeoutTimelineTooltipRef.current);
|
||||
setTimeoutTimelineTooltipRef.current = setTimeout(callback, 500);
|
||||
} else {
|
||||
timelineTooltipRef.current.style.display = "block";
|
||||
setTimeoutTimelineTooltipRef.current = setTimeout(callback, 500);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (!timelineTooltipRef.current || !timelineRef.current) return;
|
||||
|
||||
const { clientWidth } = timelineRef.current;
|
||||
|
||||
const percent = Number(event.target.value) / 100;
|
||||
|
||||
const offsetX = clientWidth * percent;
|
||||
|
||||
const time = Math.floor(percent * duration);
|
||||
|
||||
const left =
|
||||
offsetX < 20
|
||||
? 20
|
||||
: offsetX > clientWidth - 20
|
||||
? clientWidth - 20
|
||||
: offsetX;
|
||||
|
||||
timelineTooltipRef.current.style.left = `${left}px`;
|
||||
timelineTooltipRef.current.innerText = formatTime(time);
|
||||
|
||||
showTimelineTooltip();
|
||||
|
||||
onChange(event);
|
||||
};
|
||||
|
||||
const hadleMouseMove = (
|
||||
event: React.MouseEvent<HTMLDivElement, MouseEvent>
|
||||
) => {
|
||||
if (
|
||||
!timelineTooltipRef.current ||
|
||||
!timelineRef.current ||
|
||||
!hoverProgressRef.current
|
||||
)
|
||||
return;
|
||||
|
||||
const { clientWidth } = timelineRef.current;
|
||||
const { max, min } = Math;
|
||||
|
||||
const offsetX = min(max(event.nativeEvent.offsetX, 0), clientWidth);
|
||||
|
||||
const percent = Math.floor((offsetX / clientWidth) * duration);
|
||||
|
||||
hoverProgressRef.current.style.width = `${offsetX}px`;
|
||||
|
||||
const left =
|
||||
offsetX < 20
|
||||
? 20
|
||||
: offsetX > clientWidth - 20
|
||||
? clientWidth - 20
|
||||
: offsetX;
|
||||
|
||||
timelineTooltipRef.current.style.left = `${left}px`;
|
||||
timelineTooltipRef.current.innerText = formatTime(percent);
|
||||
};
|
||||
|
||||
return (
|
||||
<PlayerTimelineWrapper
|
||||
ref={timelineRef}
|
||||
onMouseMove={hadleMouseMove}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<time ref={timelineTooltipRef}>00:00</time>
|
||||
<HoverProgress ref={hoverProgressRef} />
|
||||
<input
|
||||
min="0"
|
||||
max="100"
|
||||
step="any"
|
||||
type="range"
|
||||
value={value}
|
||||
onChange={handleOnChange}
|
||||
style={{
|
||||
backgroundSize: `${value}% 100%`,
|
||||
}}
|
||||
/>
|
||||
</PlayerTimelineWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlayerTimeline;
|
Loading…
Reference in New Issue
Block a user