Merge pull request #1812 from dsernst/scrub-preview
Adds Youtube-style Scrubbing preview
This commit is contained in:
@@ -158,6 +158,20 @@ module.exports = class PlaybackController {
|
|||||||
else this.state.playing.jumpToTime = time
|
else this.state.playing.jumpToTime = time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show video preview
|
||||||
|
preview (x) {
|
||||||
|
if (!Number.isFinite(x)) {
|
||||||
|
console.error('Tried to preview a non-finite position ' + x)
|
||||||
|
return console.trace()
|
||||||
|
}
|
||||||
|
this.state.playing.previewXCoord = x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide video preview
|
||||||
|
clearPreview () {
|
||||||
|
this.state.playing.previewXCoord = null
|
||||||
|
}
|
||||||
|
|
||||||
// Change playback speed. 1 = faster, -1 = slower
|
// Change playback speed. 1 = faster, -1 = slower
|
||||||
// Playback speed ranges from 16 (fast forward) to 1 (normal playback)
|
// Playback speed ranges from 16 (fast forward) to 1 (normal playback)
|
||||||
// to 0.25 (quarter-speed playback), then goes to -0.25, -0.5, -1, -2, etc
|
// to 0.25 (quarter-speed playback), then goes to -0.25, -0.5, -1, -2, etc
|
||||||
|
|||||||
@@ -274,6 +274,8 @@ const dispatchHandlers = {
|
|||||||
previousTrack: () => controllers.playback().previousTrack(),
|
previousTrack: () => controllers.playback().previousTrack(),
|
||||||
skip: (time) => controllers.playback().skip(time),
|
skip: (time) => controllers.playback().skip(time),
|
||||||
skipTo: (time) => controllers.playback().skipTo(time),
|
skipTo: (time) => controllers.playback().skipTo(time),
|
||||||
|
preview: (x) => controllers.playback().preview(x),
|
||||||
|
clearPreview: () => controllers.playback().clearPreview(),
|
||||||
changePlaybackRate: (dir) => controllers.playback().changePlaybackRate(dir),
|
changePlaybackRate: (dir) => controllers.playback().changePlaybackRate(dir),
|
||||||
changeVolume: (delta) => controllers.playback().changeVolume(delta),
|
changeVolume: (delta) => controllers.playback().changeVolume(delta),
|
||||||
setVolume: (vol) => controllers.playback().setVolume(vol),
|
setVolume: (vol) => controllers.playback().setVolume(vol),
|
||||||
|
|||||||
@@ -536,6 +536,8 @@ function renderPlayerControls (state) {
|
|||||||
const nextClass = Playlist.hasNext(state) ? '' : 'disabled'
|
const nextClass = Playlist.hasNext(state) ? '' : 'disabled'
|
||||||
|
|
||||||
const elements = [
|
const elements = [
|
||||||
|
renderPreview(state),
|
||||||
|
|
||||||
<div key='playback-bar' className='playback-bar'>
|
<div key='playback-bar' className='playback-bar'>
|
||||||
{renderLoadingBar(state)}
|
{renderLoadingBar(state)}
|
||||||
<div
|
<div
|
||||||
@@ -547,6 +549,8 @@ function renderPlayerControls (state) {
|
|||||||
key='scrub-bar'
|
key='scrub-bar'
|
||||||
className='scrub-bar'
|
className='scrub-bar'
|
||||||
draggable
|
draggable
|
||||||
|
onMouseMove={handleScrubPreview}
|
||||||
|
onMouseOut={clearPreview}
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
onClick={handleScrub}
|
onClick={handleScrub}
|
||||||
onDrag={handleScrub}
|
onDrag={handleScrub}
|
||||||
@@ -722,6 +726,19 @@ function renderPlayerControls (state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handles a scrub hover (preview another position in the video)
|
||||||
|
function handleScrubPreview (e) {
|
||||||
|
// Only show for videos
|
||||||
|
if (!e.clientX || state.playing.type !== 'video') return
|
||||||
|
dispatch('mediaMouseMoved')
|
||||||
|
dispatch('preview', e.clientX)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearPreview (e) {
|
||||||
|
if (state.playing.type !== 'video') return
|
||||||
|
dispatch('clearPreview')
|
||||||
|
}
|
||||||
|
|
||||||
// Handles a click or drag to scrub (jump to another position in the video)
|
// Handles a click or drag to scrub (jump to another position in the video)
|
||||||
function handleScrub (e) {
|
function handleScrub (e) {
|
||||||
if (!e.clientX) return
|
if (!e.clientX) return
|
||||||
@@ -760,6 +777,56 @@ function renderPlayerControls (state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderPreview (state) {
|
||||||
|
const { previewXCoord = null } = state.playing
|
||||||
|
|
||||||
|
// Calculate time from x-coord as fraction of track width
|
||||||
|
const windowWidth = document.querySelector('body').clientWidth
|
||||||
|
const fraction = previewXCoord / windowWidth
|
||||||
|
const time = fraction * state.playing.duration /* seconds */
|
||||||
|
|
||||||
|
const height = 70
|
||||||
|
let width = 0
|
||||||
|
|
||||||
|
const previewEl = document.querySelector('video#preview')
|
||||||
|
if (previewEl !== null && previewXCoord !== null) {
|
||||||
|
previewEl.currentTime = time
|
||||||
|
|
||||||
|
// Auto adjust width to maintain video aspect ratio
|
||||||
|
width = Math.floor((previewEl.videoWidth / previewEl.videoHeight) * height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center preview window on mouse cursor,
|
||||||
|
// while avoiding falling off the left or right edges
|
||||||
|
const xPos = Math.min(Math.max(previewXCoord - (width / 2), 5), windowWidth - width - 5)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key='preview' style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 50,
|
||||||
|
left: xPos,
|
||||||
|
display: previewXCoord == null && 'none' // Hide preview when XCoord unset
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ width, height, backgroundColor: 'black' }}>
|
||||||
|
<video
|
||||||
|
src={Playlist.getCurrentLocalURL(state)}
|
||||||
|
id='preview'
|
||||||
|
style={{ border: '1px solid lightgrey', borderRadius: 2 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
textAlign: 'center', margin: 5, textShadow: '0 0 2px rgba(0,0,0,.5)', color: '#eee'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{formatTime(time, state.playing.duration)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Renders the loading bar. Shows which parts of the torrent are loaded, which
|
// Renders the loading bar. Shows which parts of the torrent are loaded, which
|
||||||
// can be 'spongey' / non-contiguous
|
// can be 'spongey' / non-contiguous
|
||||||
function renderLoadingBar (state) {
|
function renderLoadingBar (state) {
|
||||||
|
|||||||
Reference in New Issue
Block a user