1
0
mirror of https://github.com/mifi/lossless-cut.git synced 2024-11-25 03:33:14 +01:00

improve cut section rendering 💪

This commit is contained in:
Mikael Finstad 2019-01-28 14:31:30 +01:00
parent d401ec888b
commit 89cc588714
2 changed files with 92 additions and 46 deletions

View File

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

View File

@ -68,7 +68,7 @@ const localState = {
playing: false,
currentTime: undefined,
duration: undefined,
cutStartTime: 0,
cutStartTime: undefined,
cutStartTimeManual: undefined,
cutEndTime: undefined,
cutEndTimeManual: undefined,
@ -274,6 +274,11 @@ class App extends React.Component {
return `${this.getRotation()}°`;
}
getApparentCutStartTime() {
if (this.state.cutStartTime !== undefined) return this.state.cutStartTime;
return 0;
}
getApparentCutEndTime() {
if (this.state.cutEndTime !== undefined) return this.state.cutEndTime;
if (this.state.duration !== undefined) return this.state.duration;
@ -302,7 +307,7 @@ class App extends React.Component {
toggleKeyframeCut = () => this.setState(({ keyframeCut }) => ({ keyframeCut: !keyframeCut }));
jumpCutStart = () => {
seekAbs(this.state.cutStartTime);
seekAbs(this.getApparentCutStartTime());
}
jumpCutEnd = () => {
@ -314,7 +319,7 @@ class App extends React.Component {
const $target = $('.timeline-wrapper');
const parentOffset = $target.offset();
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);
/* eslint-enable react/sort-comp */
@ -351,13 +356,13 @@ class App extends React.Component {
}
const {
cutStartTime, cutEndTime, filePath, customOutDir, fileFormat, duration, includeAllStreams,
cutEndTime, cutStartTime, filePath, customOutDir, fileFormat, duration, includeAllStreams,
stripAudio, keyframeCut,
} = this.state;
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');
return;
}
@ -368,7 +373,7 @@ class App extends React.Component {
customOutDir,
filePath,
format: fileFormat,
cutFrom: cutStartTime,
cutFrom: this.getApparentCutStartTime(),
cutTo: cutEndTime,
cutToApparent: this.getApparentCutEndTime(),
videoDuration: duration,
@ -430,7 +435,7 @@ class App extends React.Component {
}
isCutRangeValid() {
return this.state.cutStartTime < this.getApparentCutEndTime();
return this.getApparentCutStartTime() < this.getApparentCutEndTime();
}
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 (
<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',
};
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 (
<div>
{!this.state.filePath && (
{!filePath && (
<div id="drag-drop-field">
<div style={{ fontSize: '9vw' }}>DROP VIDEO</div>
<div>PRESS H FOR HELP</div>
</div>
)}
{this.state.working && (
{working && (
<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,
}}
>
<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' }}>
{`${Math.floor(this.state.cutProgress * 100)} %`}
{`${Math.floor(cutProgress * 100)} %`}
</span>
)}
</div>
@ -528,18 +569,20 @@ class App extends React.Component {
options={{ recognizers: {} }}
>
<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() && (
<div
className="cut-start-time"
style={{
left: `${((this.state.cutStartTime) / (this.state.duration || 1)) * 100}%`,
width: `${(((this.getApparentCutEndTime()) - this.state.cutStartTime) / (this.state.duration || 1)) * 100}%`,
}}
/>
)
}
{cutStartTime !== undefined && <div style={startMarkerStyle} className="cut-time-marker" />}
{isCutRangeValid && (cutStartTime !== undefined || cutEndTime !== undefined) && (
<div
className="cut-section"
style={{
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>
@ -571,7 +614,7 @@ class App extends React.Component {
/>
<i
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"
onClick={this.playCommand}
@ -597,7 +640,7 @@ class App extends React.Component {
className="button fa fa-step-forward"
aria-hidden="true"
title="Jump to end of video"
onClick={() => seekAbs(this.state.duration)}
onClick={() => seekAbs(duration)}
/>
</div>
@ -631,37 +674,37 @@ class App extends React.Component {
<div className="left-menu">
<span style={infoSpanStyle} title="Format of current file">
{this.state.fileFormat || 'FMT'}
{fileFormat || 'FMT'}
</span>
<span style={infoSpanStyle} title="Playback rate">
{round(this.state.playbackRate, 1) || 1}
{round(playbackRate, 1) || 1}
</span>
</div>
<div className="right-menu">
<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)}
>
{this.state.keyframeCut ? 'kc' : 'nc'}
{keyframeCut ? 'kc' : 'nc'}
</button>
<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)}
>
{this.state.includeAllStreams ? 'all' : 'ps'}
{includeAllStreams ? 'all' : 'ps'}
</button>
<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)}
>
{this.state.stripAudio ? 'da' : 'ka'}
{stripAudio ? 'da' : 'ka'}
</button>
<button
@ -693,11 +736,11 @@ class App extends React.Component {
title="Capture frame format"
onClick={withBlur(this.toggleCaptureFormat)}
>
{this.state.captureFormat}
{captureFormat}
</button>
</div>
<HelpSheet visible={!!this.state.helpVisible} />
<HelpSheet visible={!!helpVisible} />
</div>
);
}