improve cut section rendering 💪

pull/111/head
Mikael Finstad 8 years ago
parent d401ec888b
commit 89cc588714

@ -84,7 +84,7 @@ input, button, textarea, :focus {
#current-time-display { #current-time-display {
text-align: center; text-align: center;
color: rgba(255, 255, 255, 0.3); color: rgba(255, 255, 255, 0.8);
padding: .5em; padding: .5em;
} }
@ -94,22 +94,25 @@ input, button, textarea, :focus {
background-color: #444; background-color: #444;
} }
.timeline-wrapper .current-time, .timeline-wrapper .cut-start-time { .timeline-wrapper .current-time, .timeline-wrapper .cut-section, .timeline-wrapper .cut-time-marker {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
top: 0; top: 0;
width: 1px;
} }
.timeline-wrapper .current-time { .timeline-wrapper .cut-section {
background-color: red; z-index: 1;
background-color: rgba(0, 255, 149, 0.5);
}
.timeline-wrapper .cut-time-marker {
z-index: 2; z-index: 2;
box-sizing: border-box;
background: rgba(0, 255, 149, 0.5);
} }
.timeline-wrapper .cut-start-time { .timeline-wrapper .current-time {
background-color: rgba(0, 0, 0, 0.3); z-index: 3;
border-left: 1px solid black; background-color: rgba(255, 255, 255, 1);
border-right: 1px solid black; width: 1px;
z-index: 1;
} }
#drag-drop-field { #drag-drop-field {

@ -68,7 +68,7 @@ const localState = {
playing: false, playing: false,
currentTime: undefined, currentTime: undefined,
duration: undefined, duration: undefined,
cutStartTime: 0, cutStartTime: undefined,
cutStartTimeManual: undefined, cutStartTimeManual: undefined,
cutEndTime: undefined, cutEndTime: undefined,
cutEndTimeManual: undefined, cutEndTimeManual: undefined,
@ -274,6 +274,11 @@ class App extends React.Component {
return `${this.getRotation()}°`; return `${this.getRotation()}°`;
} }
getApparentCutStartTime() {
if (this.state.cutStartTime !== undefined) return this.state.cutStartTime;
return 0;
}
getApparentCutEndTime() { getApparentCutEndTime() {
if (this.state.cutEndTime !== undefined) return this.state.cutEndTime; if (this.state.cutEndTime !== undefined) return this.state.cutEndTime;
if (this.state.duration !== undefined) return this.state.duration; if (this.state.duration !== undefined) return this.state.duration;
@ -302,7 +307,7 @@ class App extends React.Component {
toggleKeyframeCut = () => this.setState(({ keyframeCut }) => ({ keyframeCut: !keyframeCut })); toggleKeyframeCut = () => this.setState(({ keyframeCut }) => ({ keyframeCut: !keyframeCut }));
jumpCutStart = () => { jumpCutStart = () => {
seekAbs(this.state.cutStartTime); seekAbs(this.getApparentCutStartTime());
} }
jumpCutEnd = () => { jumpCutEnd = () => {
@ -314,7 +319,7 @@ class App extends React.Component {
const $target = $('.timeline-wrapper'); const $target = $('.timeline-wrapper');
const parentOffset = $target.offset(); const parentOffset = $target.offset();
const relX = e.srcEvent.pageX - parentOffset.left; const relX = e.srcEvent.pageX - parentOffset.left;
setCursor((relX / $target[0].offsetWidth) * this.state.duration); setCursor((relX / $target[0].offsetWidth) * (this.state.duration || 0));
}, 200); }, 200);
/* eslint-enable react/sort-comp */ /* eslint-enable react/sort-comp */
@ -351,13 +356,13 @@ class App extends React.Component {
} }
const { const {
cutStartTime, cutEndTime, filePath, customOutDir, fileFormat, duration, includeAllStreams, cutEndTime, cutStartTime, filePath, customOutDir, fileFormat, duration, includeAllStreams,
stripAudio, keyframeCut, stripAudio, keyframeCut,
} = this.state; } = this.state;
const rotation = this.isRotationSet() ? this.getRotation() : undefined; const rotation = this.isRotationSet() ? this.getRotation() : undefined;
if (!this.isCutRangeValid()) { if (!(this.isCutRangeValid() || cutEndTime === undefined || cutStartTime === undefined)) {
errorToast('Start time must be before end time'); errorToast('Start time must be before end time');
return; return;
} }
@ -368,7 +373,7 @@ class App extends React.Component {
customOutDir, customOutDir,
filePath, filePath,
format: fileFormat, format: fileFormat,
cutFrom: cutStartTime, cutFrom: this.getApparentCutStartTime(),
cutTo: cutEndTime, cutTo: cutEndTime,
cutToApparent: this.getApparentCutEndTime(), cutToApparent: this.getApparentCutEndTime(),
videoDuration: duration, videoDuration: duration,
@ -430,7 +435,7 @@ class App extends React.Component {
} }
isCutRangeValid() { isCutRangeValid() {
return this.state.cutStartTime < this.getApparentCutEndTime(); return this.getApparentCutStartTime() < this.getApparentCutEndTime();
} }
toggleHelp() { toggleHelp() {
@ -463,7 +468,7 @@ class App extends React.Component {
})); }));
}; };
const cutTime = type === 'start' ? this.state.cutStartTime : this.getApparentCutEndTime(); const cutTime = type === 'start' ? this.getApparentCutStartTime() : this.getApparentCutEndTime();
return ( return (
<input <input
@ -486,23 +491,59 @@ class App extends React.Component {
background: 'rgba(255, 255, 255, 0.4)', padding: '.1em .4em', margin: '0 3px', fontSize: 13, borderRadius: '.3em', background: 'rgba(255, 255, 255, 0.4)', padding: '.1em .4em', margin: '0 3px', fontSize: 13, borderRadius: '.3em',
}; };
const {
working, filePath, duration: durationRaw, cutProgress, currentTime, playing,
fileFormat, playbackRate, keyframeCut, includeAllStreams, stripAudio, captureFormat,
helpVisible, cutStartTime, cutEndTime,
} = this.state;
const markerWidth = 4;
const apparentCutStart = this.getApparentCutStartTime();
const apprentCutEnd = this.getApparentCutEndTime();
const duration = durationRaw || 1;
const currentTimePos = currentTime !== undefined && `${(currentTime / duration) * 100}%`;
const cutSectionWidth = `calc(${((apprentCutEnd - apparentCutStart) / duration) * 100}% - ${markerWidth * 2}px)`;
const isCutRangeValid = this.isCutRangeValid();
const startTimePos = `${(apparentCutStart / duration) * 100}%`;
const endTimePos = `${(apprentCutEnd / duration) * 100}%`;
const markerBorder = '2px solid rgb(0, 255, 149)';
const markerBorderRadius = 5;
const startMarkerStyle = {
width: markerWidth,
left: startTimePos,
borderLeft: markerBorder,
borderTopLeftRadius: markerBorderRadius,
borderBottomLeftRadius: markerBorderRadius,
};
const endMarkerStyle = {
width: markerWidth,
marginLeft: -markerWidth,
left: endTimePos,
borderRight: markerBorder,
borderTopRightRadius: markerBorderRadius,
borderBottomRightRadius: markerBorderRadius,
};
return ( return (
<div> <div>
{!this.state.filePath && ( {!filePath && (
<div id="drag-drop-field"> <div id="drag-drop-field">
<div style={{ fontSize: '9vw' }}>DROP VIDEO</div> <div style={{ fontSize: '9vw' }}>DROP VIDEO</div>
<div>PRESS H FOR HELP</div> <div>PRESS H FOR HELP</div>
</div> </div>
)} )}
{this.state.working && ( {working && (
<div style={{ <div style={{
color: 'white', background: 'rgba(0, 0, 0, 0.3)', borderRadius: '.5em', margin: '1em', padding: '.2em .5em', position: 'absolute', zIndex: 1, top: 0, left: 0, color: 'white', background: 'rgba(0, 0, 0, 0.3)', borderRadius: '.5em', margin: '1em', padding: '.2em .5em', position: 'absolute', zIndex: 1, top: 0, left: 0,
}} }}
> >
<i className="fa fa-cog fa-spin fa-3x fa-fw" style={{ verticalAlign: 'middle', width: '1em', height: '1em' }} /> <i className="fa fa-cog fa-spin fa-3x fa-fw" style={{ verticalAlign: 'middle', width: '1em', height: '1em' }} />
{this.state.cutProgress != null && ( {cutProgress != null && (
<span style={{ color: 'rgba(255, 255, 255, 0.7)', paddingLeft: '.4em' }}> <span style={{ color: 'rgba(255, 255, 255, 0.7)', paddingLeft: '.4em' }}>
{`${Math.floor(this.state.cutProgress * 100)} %`} {`${Math.floor(cutProgress * 100)} %`}
</span> </span>
)} )}
</div> </div>
@ -528,18 +569,20 @@ class App extends React.Component {
options={{ recognizers: {} }} options={{ recognizers: {} }}
> >
<div className="timeline-wrapper"> <div className="timeline-wrapper">
<div className="current-time" style={{ left: `${((this.state.currentTime || 0) / (this.state.duration || 1)) * 100}%` }} /> {currentTimePos !== undefined && <div className="current-time" style={{ left: currentTimePos }} />}
{this.isCutRangeValid() && ( {cutStartTime !== undefined && <div style={startMarkerStyle} className="cut-time-marker" />}
<div {isCutRangeValid && (cutStartTime !== undefined || cutEndTime !== undefined) && (
className="cut-start-time" <div
style={{ className="cut-section"
left: `${((this.state.cutStartTime) / (this.state.duration || 1)) * 100}%`, style={{
width: `${(((this.getApparentCutEndTime()) - this.state.cutStartTime) / (this.state.duration || 1)) * 100}%`, marginLeft: markerWidth,
}} left: startTimePos,
/> width: cutSectionWidth,
) }}
} />
)}
{cutEndTime !== undefined && <div style={endMarkerStyle} className="cut-time-marker" />}
<div id="current-time-display">{formatDuration(this.getOffsetCurrentTime())}</div> <div id="current-time-display">{formatDuration(this.getOffsetCurrentTime())}</div>
</div> </div>
@ -571,7 +614,7 @@ class App extends React.Component {
/> />
<i <i
className={classnames({ className={classnames({
button: true, fa: true, 'fa-pause': this.state.playing, 'fa-play': !this.state.playing, button: true, fa: true, 'fa-pause': playing, 'fa-play': !playing,
})} })}
aria-hidden="true" aria-hidden="true"
onClick={this.playCommand} onClick={this.playCommand}
@ -597,7 +640,7 @@ class App extends React.Component {
className="button fa fa-step-forward" className="button fa fa-step-forward"
aria-hidden="true" aria-hidden="true"
title="Jump to end of video" title="Jump to end of video"
onClick={() => seekAbs(this.state.duration)} onClick={() => seekAbs(duration)}
/> />
</div> </div>
@ -631,37 +674,37 @@ class App extends React.Component {
<div className="left-menu"> <div className="left-menu">
<span style={infoSpanStyle} title="Format of current file"> <span style={infoSpanStyle} title="Format of current file">
{this.state.fileFormat || 'FMT'} {fileFormat || 'FMT'}
</span> </span>
<span style={infoSpanStyle} title="Playback rate"> <span style={infoSpanStyle} title="Playback rate">
{round(this.state.playbackRate, 1) || 1} {round(playbackRate, 1) || 1}
</span> </span>
</div> </div>
<div className="right-menu"> <div className="right-menu">
<button <button
type="button" type="button"
title={`Cut mode ${this.state.keyframeCut ? 'nearest keyframe cut' : 'normal cut'}`} title={`Cut mode ${keyframeCut ? 'nearest keyframe cut' : 'normal cut'}`}
onClick={withBlur(this.toggleKeyframeCut)} onClick={withBlur(this.toggleKeyframeCut)}
> >
{this.state.keyframeCut ? 'kc' : 'nc'} {keyframeCut ? 'kc' : 'nc'}
</button> </button>
<button <button
type="button" type="button"
title={`Set output streams. Current: ${this.state.includeAllStreams ? 'include (and cut) all streams' : 'include only primary streams'}`} title={`Set output streams. Current: ${includeAllStreams ? 'include (and cut) all streams' : 'include only primary streams'}`}
onClick={withBlur(this.toggleIncludeAllStreams)} onClick={withBlur(this.toggleIncludeAllStreams)}
> >
{this.state.includeAllStreams ? 'all' : 'ps'} {includeAllStreams ? 'all' : 'ps'}
</button> </button>
<button <button
type="button" type="button"
title={`Delete audio? Current: ${this.state.stripAudio ? 'delete audio tracks' : 'keep audio tracks'}`} title={`Delete audio? Current: ${stripAudio ? 'delete audio tracks' : 'keep audio tracks'}`}
onClick={withBlur(this.toggleStripAudio)} onClick={withBlur(this.toggleStripAudio)}
> >
{this.state.stripAudio ? 'da' : 'ka'} {stripAudio ? 'da' : 'ka'}
</button> </button>
<button <button
@ -693,11 +736,11 @@ class App extends React.Component {
title="Capture frame format" title="Capture frame format"
onClick={withBlur(this.toggleCaptureFormat)} onClick={withBlur(this.toggleCaptureFormat)}
> >
{this.state.captureFormat} {captureFormat}
</button> </button>
</div> </div>
<HelpSheet visible={!!this.state.helpVisible} /> <HelpSheet visible={!!helpVisible} />
</div> </div>
); );
} }

Loading…
Cancel
Save