mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 18:21:12 +00:00
scrubber is now an HOC
This commit is contained in:
1 parent
cda1302466
commit
bfeb6d391f
4 files changed
+121
-96
No files matched your search
@@ -1,101 +1,36 @@
|
||||
import React, {
|
||||
useRef,
|
||||
useState,
|
||||
PointerEvent
|
||||
} from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
faGripDots
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { QuickTrimData } from './types';
|
||||
import {withScrubbing, withScrubbingPropsI} from './withScrubbing';
|
||||
|
||||
export default function TrimScrubber({
|
||||
trimStart,
|
||||
trimDuration,
|
||||
duration,
|
||||
width,
|
||||
boundingWidth,
|
||||
onChange
|
||||
}:{
|
||||
interface TrimScrubberPropsI extends withScrubbingPropsI {
|
||||
trimStart:number;
|
||||
trimDuration: number;
|
||||
duration: number;
|
||||
boundingWidth: number;
|
||||
width:number;
|
||||
onChange:(val:QuickTrimData)=>void
|
||||
}){
|
||||
const [{
|
||||
left,
|
||||
leftOffset,
|
||||
pointerStart
|
||||
}, setState] = useState<{
|
||||
left: number,
|
||||
leftOffset:number
|
||||
pointerStart:number
|
||||
}>({
|
||||
left: trimStart/duration*boundingWidth,
|
||||
leftOffset: trimStart/duration*boundingWidth,
|
||||
pointerStart: -1
|
||||
});
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
if(ref.current){
|
||||
ref.current.style.cursor = pointerStart >= 0
|
||||
? 'grabbing' : 'grab';
|
||||
}
|
||||
|
||||
function handleScrubStart(e: PointerEvent<HTMLDivElement>){
|
||||
e.persist();
|
||||
// console.log(`start: ${pointerStart}` );
|
||||
setState((curr)=>({
|
||||
...curr,
|
||||
pointerStart: e.clientX
|
||||
}));
|
||||
}
|
||||
function handleScrubEnd(e: PointerEvent<HTMLDivElement>){
|
||||
e.persist();
|
||||
// console.log(`end` );
|
||||
setState((curr)=>({
|
||||
...curr,
|
||||
pointerStart: -1,
|
||||
leftOffset: left,
|
||||
}));
|
||||
console.log(`${duration} * ${left} / ${boundingWidth} = ${duration * left / boundingWidth}`)
|
||||
onChange(({
|
||||
trimStartSeconds: duration * left / boundingWidth,
|
||||
trimEndSeconds: duration * left / boundingWidth + trimDuration,
|
||||
}))
|
||||
}
|
||||
function handleScrubMove(e: PointerEvent<HTMLDivElement>){
|
||||
e.persist();
|
||||
if(pointerStart >= 0 && pointerStart!==null && ref.current){
|
||||
// console.log(`${e.clientX} - ${pointerStart} = ${e.clientX-pointerStart}`)
|
||||
let newLeft = leftOffset + e.clientX - pointerStart;
|
||||
// console.log(`${newLeft} + ${boundingWidth * widthPercent/100} > ${boundingWidth}`)
|
||||
if (newLeft + width > boundingWidth) {
|
||||
newLeft = boundingWidth - width;
|
||||
}else if(newLeft < 0) {
|
||||
newLeft = 0;
|
||||
}
|
||||
setState((curr)=>({
|
||||
...curr,
|
||||
left: newLeft
|
||||
}));
|
||||
}
|
||||
}
|
||||
return(
|
||||
<div className="trim-scrubber"
|
||||
ref={ref}
|
||||
style={{
|
||||
width: width + "px",
|
||||
left: left,
|
||||
}}
|
||||
onPointerDown={handleScrubStart}
|
||||
onPointerUp={handleScrubEnd}
|
||||
onPointerLeave={handleScrubEnd}
|
||||
onPointerMove={handleScrubMove}
|
||||
>
|
||||
<FontAwesomeIcon icon={faGripDots} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export const TrimScrubber = (props:TrimScrubberPropsI)=>{
|
||||
|
||||
const TrimScrubberWithScrubbing =
|
||||
withScrubbing<TrimScrubberPropsI>(() => {
|
||||
return(
|
||||
<div className="trim-scrubber">
|
||||
<FontAwesomeIcon icon={faGripDots} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<TrimScrubberWithScrubbing
|
||||
styleOverride={{
|
||||
top: '-1rem',
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { VideoFakeyouProps } from "../VideoFakeyou/VideoFakeyou";
|
||||
|
||||
import { QuickTrimData, TrimStates, PlaybarStates } from "./types";
|
||||
import { formatSecondsToHHMMSSCS } from "./helpers";
|
||||
import TrimScrbber from "./TrimScrubber";
|
||||
import {TrimScrubber} from "./TrimScrubber";
|
||||
import './styles.scss'
|
||||
|
||||
interface VideoQuickTrimProps extends VideoFakeyouProps {
|
||||
@@ -225,10 +225,10 @@ export default memo(function VideoQuickTrim({
|
||||
{trimScrubberWidth > 0 &&
|
||||
playbarWidth > 0 &&
|
||||
maxDuration > 0 &&
|
||||
<TrimScrbber
|
||||
<TrimScrubber
|
||||
key={trimReset.toString()}
|
||||
boundingWidth={playbarWidth}
|
||||
width={trimScrubberWidth}
|
||||
scrubberWidth={trimScrubberWidth}
|
||||
trimStart={trimStart}
|
||||
trimDuration={trimDuration}
|
||||
duration={maxDuration}
|
||||
|
||||
@@ -82,15 +82,12 @@ $playbar-height: 2rem;
|
||||
border-radius: 0 0 0.5rem 0.5rem;
|
||||
}
|
||||
.trim-scrubber{
|
||||
position: absolute;
|
||||
top: -1rem;
|
||||
height: 3rem;
|
||||
background-color: #e66462;
|
||||
opacity: 0.8;
|
||||
outline: 0.125rem $white solid;
|
||||
border-top: 1rem $white solid;
|
||||
border-radius: 0.25rem 0.25rem 0.125rem 0.125rem;
|
||||
cursor: grab;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import React,{
|
||||
useRef,
|
||||
useState,
|
||||
PointerEvent,
|
||||
} from 'react';
|
||||
|
||||
export interface withScrubbingPropsI {
|
||||
boundingWidth: number;
|
||||
scrubberWidth: number;
|
||||
styleOverride?: {[key: string]: string|number };
|
||||
onScrubEnds?: ()=>number; //return scrubber location as %
|
||||
onScrubChanges?: ()=>number; //return scrubber location as %
|
||||
}
|
||||
type withSrcubbingStates = {
|
||||
currLeftOffset: number,
|
||||
prevLeftOffset: number;
|
||||
pointerStartPos: number;
|
||||
}
|
||||
|
||||
export const withScrubbing = <P extends withScrubbingPropsI>(Component: React.ComponentType<P>) => ({
|
||||
boundingWidth,
|
||||
scrubberWidth,
|
||||
styleOverride = {},
|
||||
onScrubEnds,
|
||||
onScrubChanges,
|
||||
...rest
|
||||
}: withScrubbingPropsI) => {
|
||||
// return (props: Omit<P, keyof withScrubbingPropsI>)=>{
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
const [{
|
||||
currLeftOffset, prevLeftOffset, pointerStartPos
|
||||
}, setStates] = useState<withSrcubbingStates>({
|
||||
currLeftOffset: 0, // in pixels
|
||||
prevLeftOffset: 0, //in pixels
|
||||
pointerStartPos: -1 // negative denotes pointer not engaged
|
||||
});
|
||||
|
||||
function handleScrubStart(e: PointerEvent<HTMLDivElement>){
|
||||
e.persist();
|
||||
console.log(`start: ${pointerStartPos} -> ${e.clientX}` );
|
||||
setStates((curr)=>({
|
||||
...curr,
|
||||
pointerStartPos: e.clientX
|
||||
}));
|
||||
}
|
||||
function handleScrubEnd(e: PointerEvent<HTMLDivElement>){
|
||||
e.persist();
|
||||
console.log(`end` );
|
||||
setStates((curr)=>({
|
||||
...curr,
|
||||
pointerStartPos: -1,
|
||||
prevLeftOffset: currLeftOffset,
|
||||
}));
|
||||
}
|
||||
function handleScrubMove(e: PointerEvent<HTMLDivElement>){
|
||||
e.persist();
|
||||
if(pointerStartPos >= 0 && pointerStartPos!==null){
|
||||
let newLeftOffset = prevLeftOffset + e.clientX - pointerStartPos;
|
||||
if (newLeftOffset + scrubberWidth > boundingWidth) {
|
||||
newLeftOffset = boundingWidth - scrubberWidth;
|
||||
}else if(newLeftOffset < 0) {
|
||||
newLeftOffset = 0;
|
||||
}
|
||||
setStates((curr)=>({
|
||||
...curr,
|
||||
currLeftOffset: newLeftOffset
|
||||
}));
|
||||
}
|
||||
}
|
||||
return(
|
||||
<div
|
||||
ref={ref}
|
||||
className="scrubber-wrapper"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top:0,
|
||||
width: scrubberWidth + 'px',
|
||||
left: currLeftOffset,
|
||||
cursor: pointerStartPos >=0 ? 'grabbing': 'grab',
|
||||
...styleOverride
|
||||
}}
|
||||
onPointerDown={handleScrubStart}
|
||||
onPointerUp={handleScrubEnd}
|
||||
onPointerLeave={handleScrubEnd}
|
||||
onPointerMove={handleScrubMove}
|
||||
>
|
||||
<Component
|
||||
{...rest as P}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
// }
|
||||
};
|
||||
Reference in new issue
Block a user