mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-29 22:57:16 +02:00
Update:Audio player drag cursor to seek #457
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
<p class="top-4 absolute left-0 right-0 mx-auto text-center uppercase tracking-widest text-opacity-75" style="font-size: 10px" :class="{ 'text-success': isLocalPlayMethod, 'text-accent': !isLocalPlayMethod }">{{ isDirectPlayMethod ? 'Direct' : isLocalPlayMethod ? 'Local' : 'Transcode' }}</p>
|
<p class="top-4 absolute left-0 right-0 mx-auto text-center uppercase tracking-widest text-opacity-75" style="font-size: 10px" :class="{ 'text-success': isLocalPlayMethod, 'text-accent': !isLocalPlayMethod }">{{ isDirectPlayMethod ? 'Direct' : isLocalPlayMethod ? 'Local' : 'Transcode' }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="useChapterTrack && useTotalTrack && showFullscreen" class="absolute total-track w-full z-30" :class="showFullscreen ? 'px-6' : 'px-3'">
|
<div v-if="useChapterTrack && useTotalTrack && showFullscreen" class="absolute total-track w-full z-30 px-6">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem">{{ currentTimePretty }}</p>
|
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem">{{ currentTimePretty }}</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
@@ -80,18 +80,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="playerTrack" class="absolute left-0 w-full" :class="showFullscreen ? 'px-6' : 'px-3'">
|
<div id="playerTrack" class="absolute left-0 w-full px-6">
|
||||||
<div class="flex pb-0.5">
|
<div class="flex">
|
||||||
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem" ref="currentTimestamp">0:00</p>
|
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem" ref="currentTimestamp">0:00</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem">{{ timeRemainingPretty }}</p>
|
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem">{{ timeRemainingPretty }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div ref="track" class="h-1.5 w-full bg-gray-500 bg-opacity-50 relative rounded-full" :class="{ 'animate-pulse': isLoading }" @touchstart="touchstartTrack" @click="clickTrack">
|
<div ref="track" class="h-1.5 w-full bg-gray-500 bg-opacity-50 relative rounded-full" :class="{ 'animate-pulse': isLoading }" @click.stop>
|
||||||
<div ref="readyTrack" class="h-full bg-gray-600 absolute top-0 left-0 pointer-events-none rounded-full" />
|
<div ref="readyTrack" class="h-full bg-gray-600 absolute top-0 left-0 rounded-full pointer-events-none" />
|
||||||
<div ref="bufferedTrack" class="h-full bg-gray-500 absolute top-0 left-0 pointer-events-none rounded-full" />
|
<div ref="bufferedTrack" class="h-full bg-gray-500 absolute top-0 left-0 rounded-full pointer-events-none" />
|
||||||
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 pointer-events-none rounded-full" />
|
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 rounded-full pointer-events-none" />
|
||||||
<div ref="draggingTrack" class="h-full bg-warning bg-opacity-25 absolute top-0 left-0 pointer-events-none rounded-full" />
|
<div ref="trackCursor" class="rounded-full bg-gray-200 absolute pointer-events-auto" :class="{ 'opacity-0': lockUi || !showFullscreen, 'h-3.5 w-3.5 -top-1': isDraggingCursor, 'h-3 w-3 -top-0.75': !isDraggingCursor }" @touchstart="touchstartCursor" />
|
||||||
<div ref="trackCursor" class="h-3.5 w-3.5 rounded-full bg-gray-200 absolute -top-1 pointer-events-none" :class="{ 'opacity-0': lockUi || !showFullscreen }" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -149,8 +148,10 @@ export default {
|
|||||||
useTotalTrack: true,
|
useTotalTrack: true,
|
||||||
lockUi: false,
|
lockUi: false,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
touchTrackStart: false,
|
isDraggingCursor: false,
|
||||||
dragPercent: 0,
|
draggingTouchStartX: 0,
|
||||||
|
draggingTouchStartTime: 0,
|
||||||
|
draggingCurrentTime: 0,
|
||||||
syncStatus: 0,
|
syncStatus: 0,
|
||||||
showMoreMenuDialog: false,
|
showMoreMenuDialog: false,
|
||||||
coverRgb: 'rgb(55, 56, 56)',
|
coverRgb: 'rgb(55, 56, 56)',
|
||||||
@@ -317,17 +318,20 @@ export default {
|
|||||||
return this.$secondsToTimestamp(this.totalDuration)
|
return this.$secondsToTimestamp(this.totalDuration)
|
||||||
},
|
},
|
||||||
currentTimePretty() {
|
currentTimePretty() {
|
||||||
return this.$secondsToTimestamp(this.currentTime / this.currentPlaybackRate)
|
let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
|
||||||
|
return this.$secondsToTimestamp(currentTimeToUse / this.currentPlaybackRate)
|
||||||
},
|
},
|
||||||
timeRemaining() {
|
timeRemaining() {
|
||||||
|
let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
|
||||||
if (this.useChapterTrack && this.currentChapter) {
|
if (this.useChapterTrack && this.currentChapter) {
|
||||||
var currChapTime = this.currentTime - this.currentChapter.start
|
var currChapTime = currentTimeToUse - this.currentChapter.start
|
||||||
return (this.currentChapterDuration - currChapTime) / this.currentPlaybackRate
|
return (this.currentChapterDuration - currChapTime) / this.currentPlaybackRate
|
||||||
}
|
}
|
||||||
return this.totalTimeRemaining
|
return this.totalTimeRemaining
|
||||||
},
|
},
|
||||||
totalTimeRemaining() {
|
totalTimeRemaining() {
|
||||||
return (this.totalDuration - this.currentTime) / this.currentPlaybackRate
|
let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
|
||||||
|
return (this.totalDuration - currentTimeToUse) / this.currentPlaybackRate
|
||||||
},
|
},
|
||||||
totalTimeRemainingPretty() {
|
totalTimeRemainingPretty() {
|
||||||
if (this.totalTimeRemaining < 0) {
|
if (this.totalTimeRemaining < 0) {
|
||||||
@@ -341,10 +345,6 @@ export default {
|
|||||||
}
|
}
|
||||||
return '-' + this.$secondsToTimestamp(this.timeRemaining)
|
return '-' + this.$secondsToTimestamp(this.timeRemaining)
|
||||||
},
|
},
|
||||||
timeLeftInChapter() {
|
|
||||||
if (!this.currentChapter) return 0
|
|
||||||
return this.currentChapter.end - this.currentTime
|
|
||||||
},
|
|
||||||
sleepTimeRemainingPretty() {
|
sleepTimeRemainingPretty() {
|
||||||
if (!this.sleepTimeRemaining) return '0s'
|
if (!this.sleepTimeRemaining) return '0s'
|
||||||
var secondsRemaining = Math.round(this.sleepTimeRemaining)
|
var secondsRemaining = Math.round(this.sleepTimeRemaining)
|
||||||
@@ -391,11 +391,6 @@ export default {
|
|||||||
this.showFullscreen = false
|
this.showFullscreen = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async touchstartTrack(e) {
|
|
||||||
await this.$hapticsImpact()
|
|
||||||
if (!e || !e.touches || !this.$refs.track || !this.showFullscreen || this.lockUi) return
|
|
||||||
this.touchTrackStart = true
|
|
||||||
},
|
|
||||||
async selectChapter(chapter) {
|
async selectChapter(chapter) {
|
||||||
await this.$hapticsImpact()
|
await this.$hapticsImpact()
|
||||||
this.seek(chapter.start)
|
this.seek(chapter.start)
|
||||||
@@ -507,12 +502,13 @@ export default {
|
|||||||
console.error('No timestamp el')
|
console.error('No timestamp el')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let currentTime = this.currentTime / this.currentPlaybackRate
|
|
||||||
|
let currentTime = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
|
||||||
if (this.useChapterTrack && this.currentChapter) {
|
if (this.useChapterTrack && this.currentChapter) {
|
||||||
const currChapTime = Math.max(0, this.currentTime - this.currentChapter.start)
|
currentTime = Math.max(0, currentTime - this.currentChapter.start)
|
||||||
currentTime = currChapTime / this.currentPlaybackRate
|
|
||||||
}
|
}
|
||||||
ts.innerText = this.$secondsToTimestamp(currentTime)
|
|
||||||
|
ts.innerText = this.$secondsToTimestamp(currentTime / this.currentPlaybackRate)
|
||||||
},
|
},
|
||||||
timeupdate() {
|
timeupdate() {
|
||||||
if (!this.$refs.playedTrack) {
|
if (!this.$refs.playedTrack) {
|
||||||
@@ -534,22 +530,25 @@ export default {
|
|||||||
},
|
},
|
||||||
updateTrack() {
|
updateTrack() {
|
||||||
// Update progress track UI
|
// Update progress track UI
|
||||||
let percentDone = this.currentTime / this.totalDuration
|
let currentTimeToUse = this.isDraggingCursor ? this.draggingCurrentTime : this.currentTime
|
||||||
|
let percentDone = currentTimeToUse / this.totalDuration
|
||||||
const totalPercentDone = percentDone
|
const totalPercentDone = percentDone
|
||||||
let bufferedPercent = this.bufferedTime / this.totalDuration
|
let bufferedPercent = this.bufferedTime / this.totalDuration
|
||||||
const totalBufferedPercent = bufferedPercent
|
const totalBufferedPercent = bufferedPercent
|
||||||
|
|
||||||
if (this.useChapterTrack && this.currentChapter) {
|
if (this.useChapterTrack && this.currentChapter) {
|
||||||
const currChapTime = this.currentTime - this.currentChapter.start
|
const currChapTime = currentTimeToUse - this.currentChapter.start
|
||||||
percentDone = currChapTime / this.currentChapterDuration
|
percentDone = currChapTime / this.currentChapterDuration
|
||||||
bufferedPercent = Math.max(0, Math.min(1, (this.bufferedTime - this.currentChapter.start) / this.currentChapterDuration))
|
bufferedPercent = Math.max(0, Math.min(1, (this.bufferedTime - this.currentChapter.start) / this.currentChapterDuration))
|
||||||
}
|
}
|
||||||
|
|
||||||
const ptWidth = Math.round(percentDone * this.trackWidth)
|
const ptWidth = Math.round(percentDone * this.trackWidth)
|
||||||
this.$refs.playedTrack.style.width = ptWidth + 'px'
|
this.$refs.playedTrack.style.width = ptWidth + 'px'
|
||||||
this.$refs.bufferedTrack.style.width = Math.round(bufferedPercent * this.trackWidth) + 'px'
|
this.$refs.bufferedTrack.style.width = Math.round(bufferedPercent * this.trackWidth) + 'px'
|
||||||
|
|
||||||
if (this.$refs.trackCursor) {
|
if (this.$refs.trackCursor) {
|
||||||
this.$refs.trackCursor.style.left = ptWidth - 8 + 'px'
|
const cursorShift = this.isDraggingCursor ? 7 : 6
|
||||||
|
this.$refs.trackCursor.style.left = ptWidth - cursorShift + 'px'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.useChapterTrack) {
|
if (this.useChapterTrack) {
|
||||||
@@ -578,27 +577,14 @@ export default {
|
|||||||
this.$refs.playedTrack.classList.add('bg-yellow-300')
|
this.$refs.playedTrack.classList.add('bg-yellow-300')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clickTrack(e) {
|
async touchstartCursor(e) {
|
||||||
if (this.isLoading || this.lockUi) return
|
if (!e || !e.touches || !this.$refs.track || !this.showFullscreen || this.lockUi) return
|
||||||
if (!this.showFullscreen) {
|
await this.$hapticsImpact()
|
||||||
// Track not clickable on mini-player
|
this.isDraggingCursor = true
|
||||||
return
|
this.draggingTouchStartX = e.touches[0].pageX
|
||||||
}
|
this.draggingTouchStartTime = this.currentTime
|
||||||
if (e) e.stopPropagation()
|
this.draggingCurrentTime = this.currentTime
|
||||||
|
this.updateTrack()
|
||||||
var offsetX = e.offsetX
|
|
||||||
var perc = offsetX / this.trackWidth
|
|
||||||
var time = 0
|
|
||||||
if (this.useChapterTrack && this.currentChapter) {
|
|
||||||
time = perc * this.currentChapterDuration + this.currentChapter.start
|
|
||||||
} else {
|
|
||||||
time = perc * this.totalDuration
|
|
||||||
}
|
|
||||||
if (isNaN(time) || time === null) {
|
|
||||||
console.error('Invalid time', perc, time)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.seek(time)
|
|
||||||
},
|
},
|
||||||
async playPauseClick() {
|
async playPauseClick() {
|
||||||
await this.$hapticsImpact()
|
await this.$hapticsImpact()
|
||||||
@@ -651,24 +637,11 @@ export default {
|
|||||||
touchend(e) {
|
touchend(e) {
|
||||||
if (!e.changedTouches) return
|
if (!e.changedTouches) return
|
||||||
|
|
||||||
if (this.touchTrackStart) {
|
if (this.isDraggingCursor) {
|
||||||
var touch = e.changedTouches[0]
|
if (this.draggingCurrentTime !== this.currentTime) {
|
||||||
const touchOnTrackPos = touch.pageX - 12
|
this.seek(this.draggingCurrentTime)
|
||||||
const dragPercent = Math.max(0, Math.min(1, touchOnTrackPos / this.trackWidth))
|
|
||||||
|
|
||||||
var seekToTime = 0
|
|
||||||
if (this.useChapterTrack && this.currentChapter) {
|
|
||||||
const currChapTime = dragPercent * this.currentChapterDuration
|
|
||||||
seekToTime = this.currentChapter.start + currChapTime
|
|
||||||
} else {
|
|
||||||
seekToTime = dragPercent * this.totalDuration
|
|
||||||
}
|
}
|
||||||
this.seek(seekToTime)
|
this.isDraggingCursor = false
|
||||||
|
|
||||||
if (this.$refs.draggingTrack) {
|
|
||||||
this.$refs.draggingTrack.style.width = '0px'
|
|
||||||
}
|
|
||||||
this.touchTrackStart = false
|
|
||||||
} else if (this.showFullscreen) {
|
} else if (this.showFullscreen) {
|
||||||
this.touchEndY = e.changedTouches[0].screenY
|
this.touchEndY = e.changedTouches[0].screenY
|
||||||
var touchDuration = Date.now() - this.touchStartTime
|
var touchDuration = Date.now() - this.touchStartTime
|
||||||
@@ -680,29 +653,24 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
touchmove(e) {
|
touchmove(e) {
|
||||||
if (!this.touchTrackStart) return
|
if (!this.isDraggingCursor || !e.touches) return
|
||||||
|
|
||||||
var touch = e.touches[0]
|
const distanceMoved = e.touches[0].pageX - this.draggingTouchStartX
|
||||||
const touchOnTrackPos = touch.pageX - 12
|
let duration = this.totalDuration
|
||||||
const dragPercent = Math.max(0, Math.min(1, touchOnTrackPos / this.trackWidth))
|
let minTime = 0
|
||||||
this.dragPercent = dragPercent
|
let maxTime = duration
|
||||||
|
if (this.useChapterTrack && this.currentChapter) {
|
||||||
if (this.$refs.draggingTrack) {
|
duration = this.currentChapterDuration
|
||||||
this.$refs.draggingTrack.style.width = this.dragPercent * this.trackWidth + 'px'
|
minTime = this.currentChapter.start
|
||||||
|
maxTime = minTime + duration
|
||||||
}
|
}
|
||||||
|
|
||||||
var ts = this.$refs.currentTimestamp
|
const timePerPixel = duration / this.trackWidth
|
||||||
if (ts) {
|
const newTime = this.draggingTouchStartTime + timePerPixel * distanceMoved
|
||||||
var currTimeStr = ''
|
this.draggingCurrentTime = Math.min(maxTime, Math.max(minTime, newTime))
|
||||||
if (this.useChapterTrack && this.currentChapter) {
|
|
||||||
const currChapTime = dragPercent * this.currentChapterDuration
|
this.updateTimestamp()
|
||||||
currTimeStr = this.$secondsToTimestamp(currChapTime)
|
this.updateTrack()
|
||||||
} else {
|
|
||||||
const dragTime = dragPercent * this.totalDuration
|
|
||||||
currTimeStr = this.$secondsToTimestamp(dragTime)
|
|
||||||
}
|
|
||||||
ts.innerText = currTimeStr
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async clickMenuAction(action) {
|
async clickMenuAction(action) {
|
||||||
await this.$hapticsImpact()
|
await this.$hapticsImpact()
|
||||||
@@ -848,7 +816,7 @@ export default {
|
|||||||
document.documentElement.style.setProperty('--cover-image-height', coverHeight + 'px')
|
document.documentElement.style.setProperty('--cover-image-height', coverHeight + 'px')
|
||||||
document.documentElement.style.setProperty('--cover-image-width-collapsed', coverImageWidthCollapsed + 'px')
|
document.documentElement.style.setProperty('--cover-image-width-collapsed', coverImageWidthCollapsed + 'px')
|
||||||
document.documentElement.style.setProperty('--cover-image-height-collapsed', 46 + 'px')
|
document.documentElement.style.setProperty('--cover-image-height-collapsed', 46 + 'px')
|
||||||
document.documentElement.style.setProperty('--title-author-left-offset-collapsed', 24 + coverImageWidthCollapsed + 'px')
|
document.documentElement.style.setProperty('--title-author-left-offset-collapsed', 30 + coverImageWidthCollapsed + 'px')
|
||||||
},
|
},
|
||||||
minimizePlayerEvt() {
|
minimizePlayerEvt() {
|
||||||
this.collapseFullscreen()
|
this.collapseFullscreen()
|
||||||
@@ -915,7 +883,7 @@ export default {
|
|||||||
--cover-image-height: 0px;
|
--cover-image-height: 0px;
|
||||||
--cover-image-width-collapsed: 46px;
|
--cover-image-width-collapsed: 46px;
|
||||||
--cover-image-height-collapsed: 46px;
|
--cover-image-height-collapsed: 46px;
|
||||||
--title-author-left-offset-collapsed: 70px;
|
--title-author-left-offset-collapsed: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playerContainer {
|
.playerContainer {
|
||||||
@@ -942,7 +910,7 @@ export default {
|
|||||||
|
|
||||||
.cover-wrapper {
|
.cover-wrapper {
|
||||||
bottom: 68px;
|
bottom: 68px;
|
||||||
left: 12px;
|
left: 24px;
|
||||||
height: var(--cover-image-height-collapsed);
|
height: var(--cover-image-height-collapsed);
|
||||||
width: var(--cover-image-width-collapsed);
|
width: var(--cover-image-width-collapsed);
|
||||||
transition: all 0.25s cubic-bezier(0.39, 0.575, 0.565, 1);
|
transition: all 0.25s cubic-bezier(0.39, 0.575, 0.565, 1);
|
||||||
@@ -999,9 +967,8 @@ export default {
|
|||||||
#playerControls {
|
#playerControls {
|
||||||
transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1);
|
transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1);
|
||||||
transition-property: width, bottom;
|
transition-property: width, bottom;
|
||||||
width: 140px;
|
width: 128px;
|
||||||
padding-left: 12px;
|
padding-right: 24px;
|
||||||
padding-right: 12px;
|
|
||||||
bottom: 70px;
|
bottom: 70px;
|
||||||
}
|
}
|
||||||
#playerControls .jump-icon {
|
#playerControls .jump-icon {
|
||||||
@@ -1019,7 +986,7 @@ export default {
|
|||||||
width: 40px;
|
width: 40px;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
margin: 0px 14px;
|
margin: 0px 7px;
|
||||||
}
|
}
|
||||||
#playerControls .play-btn .material-icons {
|
#playerControls .play-btn .material-icons {
|
||||||
transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1);
|
transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1);
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
minHeight: {
|
minHeight: {
|
||||||
'12': '3rem'
|
'12': '3rem'
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
'0.75': '0.1875rem'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user