[web] Improve user interaction with sliders for output volume and music progress bar #1620

The sliders for output volume and the music slider have been optimised and simplified (one library less) to ensure easier user interaction.
This commit is contained in:
Alain Nussbaumer
2023-06-21 15:14:17 +02:00
parent b7a52d1761
commit eeb4d328c8
15 changed files with 249 additions and 241 deletions

View File

@@ -1,6 +1,6 @@
<template>
<div>
<section class="section">
<section class="section fd-page">
<div class="container">
<div class="columns is-centered">
<div class="column is-four-fifths has-text-centered-mobile">

View File

@@ -1,69 +1,54 @@
<template>
<section>
<div v-if="now_playing.id > 0" class="fd-is-fullheight">
<cover-artwork
:artwork_url="now_playing.artwork_url"
:artist="now_playing.artist"
:album="now_playing.album"
class="is-clickable fd-has-shadow fd-is-expanded fd-cover fd-cover-big-image"
@click="open_dialog(now_playing)"
/>
<div class="fd-has-padding-left-right">
<div class="container has-text-centered">
<p class="control has-text-centered fd-progress-now-playing">
<slider
ref="slider"
v-model="item_progress_ms"
:min="0"
:max="state.item_length_ms"
:step="1000"
:tooltips="false"
:disabled="state.state === 'stop'"
:classes="{ target: 'seek-slider' }"
@change="seek"
@start="start_dragging"
@end="end_dragging"
/>
</p>
<p class="content">
<span
v-text="
[
$filters.durationInHours(item_progress_ms),
$filters.durationInHours(now_playing.length_ms)
].join(' / ')
"
/>
</p>
</div>
</div>
<div class="fd-has-padding-left-right">
<div class="container has-text-centered fd-has-margin-top">
<h1 class="title is-5" v-text="now_playing.title" />
<h2 class="title is-6" v-text="now_playing.artist" />
<h2
v-if="composer"
class="subtitle is-6 has-text-grey has-text-weight-bold"
v-text="composer"
<div class="hero is-fullheight">
<div v-if="track.id > 0" class="hero-body">
<div class="container has-text-centered" style="max-width: 500px">
<cover-artwork
:artwork_url="track.artwork_url"
:artist="track.artist"
:album="track.album"
class="is-clickable fd-has-shadow fd-cover-big-image"
@click="open_dialog(track)"
/>
<input
v-model.number="item_progress_ms"
:step="1000"
:max="is_live ? 1000 : track.length_ms"
type="range"
class="slider mt-5"
:style="{ '--ratio': progress }"
@change="seek"
@touchstart="start_dragging"
@touchend="end_dragging"
/>
<div class="is-flex is-justify-content-space-between">
<p
class="subtitle is-7"
v-text="$filters.durationInHours(item_progress_ms)"
/>
<h3 class="subtitle is-6" v-text="now_playing.album" />
<h3
v-if="filepath"
class="subtitle is-6 has-text-grey"
v-text="filepath"
<p
class="subtitle is-7"
v-text="$filters.durationInHours(track.length_ms)"
/>
</div>
<h1 class="title is-5" v-text="track.title" />
<h2 class="title is-6" v-text="track.artist" />
<h2
v-if="composer"
class="subtitle is-6 has-text-grey has-text-weight-bold"
v-text="composer"
/>
<h3 class="subtitle is-6" v-text="track.album" />
<h3
v-if="filepath"
class="subtitle is-6 has-text-grey"
v-text="filepath"
/>
</div>
</div>
<div v-else class="fd-is-fullheight">
<div
class="fd-is-expanded fd-has-padding-left-right"
style="flex-direction: column"
>
<div class="content has-text-centered">
<h1 class="title is-5" v-text="$t('page.now-playing.title')" />
<p v-text="$t('page.now-playing.info')" />
</div>
<div v-else class="hero-body">
<div class="container has-text-centered">
<p class="title is-5" v-text="$t('page.now-playing.title')" />
<p class="subtitle" v-text="$t('page.now-playing.info')" />
</div>
</div>
<modal-dialog-queue-item
@@ -71,12 +56,11 @@
:item="selected_item"
@close="show_details_modal = false"
/>
</section>
</div>
</template>
<script>
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem.vue'
import Slider from '@vueform/slider'
import CoverArtwork from '@/components/CoverArtwork.vue'
import webapi from '@/webapi'
import * as types from '@/store/mutation_types'
@@ -85,7 +69,6 @@ export default {
name: 'PageNowPlaying',
components: {
ModalDialogQueueItem,
Slider,
CoverArtwork
},
@@ -101,11 +84,19 @@ export default {
},
computed: {
state() {
progress() {
return this.is_live ? 2 : this.item_progress_ms / this.track.length_ms
},
is_live() {
return this.track.length_ms == 0
},
player() {
return this.$store.state.player
},
now_playing() {
track() {
return this.$store.getters.now_playing
},
@@ -121,16 +112,16 @@ export default {
if (this.settings_option_show_composer_now_playing) {
if (
!this.settings_option_show_composer_for_genre ||
(this.now_playing.genre &&
(this.track.genre &&
this.settings_option_show_composer_for_genre
.toLowerCase()
.split(',')
.findIndex(
(elem) =>
this.now_playing.genre.toLowerCase().indexOf(elem.trim()) >= 0
this.track.genre.toLowerCase().indexOf(elem.trim()) >= 0
) >= 0)
) {
return this.now_playing.composer
return this.track.composer
}
}
return null
@@ -142,32 +133,30 @@ export default {
filepath() {
if (this.settings_option_show_filepath_now_playing) {
return this.now_playing.path
return this.track.path
}
return null
}
},
watch: {
state() {
player() {
if (this.interval_id > 0) {
window.clearTimeout(this.interval_id)
this.interval_id = 0
}
this.item_progress_ms = this.state.item_progress_ms
if (this.state.state === 'play') {
this.item_progress_ms = this.player.item_progress_ms
if (this.player.state === 'play') {
this.interval_id = window.setInterval(this.tick, 1000)
}
}
},
mounted() {},
created() {
this.item_progress_ms = this.state.item_progress_ms
this.item_progress_ms = this.player.item_progress_ms
webapi.player_status().then(({ data }) => {
this.$store.commit(types.UPDATE_PLAYER_STATUS, data)
if (this.state.state === 'play') {
if (this.player.state === 'play') {
this.interval_id = window.setInterval(this.tick, 1000)
}
})
@@ -183,7 +172,13 @@ export default {
methods: {
tick() {
if (!this.is_dragged) {
this.item_progress_ms += 1000
if (this.is_live) {
this.item_progress_ms += 1000
} else if (this.item_progress_ms + 1000 > this.track.length_ms) {
this.item_progress_ms = this.track.length_ms
} else {
this.item_progress_ms += 1000
}
}
},
@@ -195,10 +190,12 @@ export default {
this.is_dragged = false
},
seek(newPosition) {
webapi.player_seek_to_pos(newPosition).catch(() => {
this.item_progress_ms = this.state.item_progress_ms
})
seek() {
if (!this.is_live) {
webapi.player_seek_to_pos(this.item_progress_ms).catch(() => {
this.item_progress_ms = this.player.item_progress_ms
})
}
},
open_dialog(item) {

View File

@@ -1,7 +1,7 @@
<template>
<div>
<!-- Search field + recent searches -->
<section class="section pb-0">
<section class="section fd-page pb-0">
<div class="container">
<div class="columns is-centered">
<div class="column is-four-fifths">

View File

@@ -1,7 +1,7 @@
<template>
<div>
<!-- Search field + recent searches -->
<section class="section pb-0">
<section class="section pb-0 fd-page">
<div class="container">
<div class="columns is-centered">
<div class="column is-four-fifths">