replace bootstrap-vue progress bar with vue-slider-component

better hit area, drag support
This commit is contained in:
Thomas Amland 2021-07-17 13:25:05 +02:00
parent e1dc32060b
commit 73b2b493a4
7 changed files with 118 additions and 46 deletions

View File

@ -2,10 +2,11 @@
<div :class="{'visible': visible}" class="player elevated d-flex"> <div :class="{'visible': visible}" class="player elevated d-flex">
<div class="flex-fill"> <div class="flex-fill">
<!-- Progress ---> <!-- Progress --->
<div class="progress2" @click="seek"> <ProgressBar
<b-progress :value="progress" :max="100" height="4px" /> style="margin-bottom: -5px; margin-top: -9px"
</div> :value="progress" @input="seek"
<div class="row align-items-center m-0"> />
<div class="row align-items-center m-0" style="padding-top: -10px">
<!-- Track info ---> <!-- Track info --->
<div class="col p-0 d-flex flex-nowrap align-items-center justify-content-start" style="width: 0; min-width: 0"> <div class="col p-0 d-flex flex-nowrap align-items-center justify-content-start" style="width: 0; min-width: 0">
<template v-if="track"> <template v-if="track">
@ -96,9 +97,6 @@
</div> </div>
</template> </template>
<style scoped> <style scoped>
.progress2 {
cursor: pointer;
}
.player { .player {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
@ -120,8 +118,12 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue' import Vue from 'vue'
import { mapState, mapGetters, mapActions } from 'vuex' import { mapState, mapGetters, mapActions } from 'vuex'
import ProgressBar from '@/player/ProgressBar.vue'
export default Vue.extend({ export default Vue.extend({
components: {
ProgressBar,
},
computed: { computed: {
...mapState('player', { ...mapState('player', {
isPlaying: (state: any) => state.isPlaying, isPlaying: (state: any) => state.isPlaying,
@ -154,14 +156,8 @@
'toggleRepeat', 'toggleRepeat',
'toggleShuffle', 'toggleShuffle',
'toggleMute', 'toggleMute',
'seek',
]), ]),
seek(event: any) {
if (event.target) {
const width = event.currentTarget.clientWidth
const value = event.offsetX / width
return this.$store.dispatch('player/seek', value)
}
},
setVolume(volume: any) { setVolume(volume: any) {
return this.$store.dispatch('player/setVolume', parseFloat(volume)) return this.$store.dispatch('player/setVolume', parseFloat(volume))
}, },

View File

@ -0,0 +1,62 @@
<template>
<VueSlider
v-bind="$attrs"
:value="value"
:min="0"
:max="100"
:interval="0.001"
:lazy="true"
:contained="true"
:dot-options="{tooltip: 'none'}"
@change="onInput"
/>
</template>
<style lang="scss" scoped>
@import '/src/style/variables';
@import '~vue-slider-component/theme/material.css';
.vue-slider {
height: 4px !important;
padding: 5px 0 !important;
cursor: pointer;
}
::v-deep .vue-slider-rail {
background-color: $secondary;
border-radius: 0;
}
::v-deep .vue-slider-process {
background-color: $primary;
border-radius: 0;
}
::v-deep .vue-slider-dot-handle {
background-color: $primary;
}
::v-deep .vue-slider-dot-handle::after {
background-color: rgba($primary, 0.32);
transform: translate(-50%, -50%) scale(1);
}
.vue-slider:not(:hover) ::v-deep .vue-slider-dot-handle {
display: none;
}
.vue-slider:hover ::v-deep .vue-slider-dot-handle {
display: block;
}
</style>
<script lang="ts">
import Vue from 'vue'
import VueSlider from 'vue-slider-component'
export default Vue.extend({
components: {
VueSlider,
},
props: {
value: { type: Number, required: true },
},
methods: {
onInput(value: number) {
this.$emit('input', value)
},
}
})
</script>

View File

@ -187,7 +187,7 @@ export const playerModule: Module<State, any> = {
}, },
seek({ state }, value) { seek({ state }, value) {
if (isFinite(state.duration)) { if (isFinite(state.duration)) {
audio.seek(state.duration * value) audio.seek(state.duration * (value / 100.0))
} }
}, },
async resetQueue({ commit, getters }) { async resetQueue({ commit, getters }) {

View File

@ -9,20 +9,38 @@
@change="onInput" @change="onInput"
/> />
</template> </template>
<style> <style lang="scss" scoped>
@import '~vue-slider-component/theme/default.css'; @import '/src/style/_variables';
.vue-slider-rail { @import '~vue-slider-component/theme/material.css';
background-color: var(--secondary) !important;
.vue-slider {
cursor: pointer;
} }
.vue-slider-process { ::v-deep .vue-slider-rail {
background-color: var(--primary) !important; background-color: $secondary;
border-radius: 0;
} }
.vue-slider-dot-tooltip-inner { ::v-deep .vue-slider-process {
background-color: var(--primary); background-color: $primary;
border-color: var(--primary); border-radius: 0;
} }
.vue-slider-dot-handle { ::v-deep .vue-slider-dot-handle {
background-color: var(--text-body); background-color: $primary;
}
::v-deep .vue-slider-dot-handle::after {
background-color: rgba($primary, 0.32);
transform: translate(-50%, -50%) scale(1);
}
::v-deep .vue-slider-dot-handle:hover .vue-slider-dot-tooltip {
visibility: visible;
}
::v-deep .vue-slider-dot-tooltip-inner {
background-color: $primary;
border-color: $primary;
}
::v-deep .vue-slider-dot-tooltip-text {
width: 40px;
height: 40px;
} }
</style> </style>
<script lang="ts"> <script lang="ts">

View File

@ -18,7 +18,6 @@ import {
BModal, BModal,
BOverlay, BOverlay,
BPopover, BPopover,
BProgress,
BSidebar, BSidebar,
DropdownPlugin, DropdownPlugin,
} from 'bootstrap-vue' } from 'bootstrap-vue'
@ -33,7 +32,6 @@ Vue.component('BFormCheckbox', BFormCheckbox)
Vue.component('BFormTextarea', BFormTextarea) Vue.component('BFormTextarea', BFormTextarea)
Vue.component('BButton', BButton) Vue.component('BButton', BButton)
Vue.component('BPopover', BPopover) Vue.component('BPopover', BPopover)
Vue.component('BProgress', BProgress)
Vue.component('BOverlay', BOverlay) Vue.component('BOverlay', BOverlay)
Vue.use(DropdownPlugin) Vue.use(DropdownPlugin)

15
src/style/_variables.scss Normal file
View File

@ -0,0 +1,15 @@
$theme-elevation-0: hsl(0, 0%, 0%);
$theme-elevation-1: hsl(0, 0%, 10%);
$theme-elevation-2: hsl(0, 0%, 20%);
$theme-text: #ccc;
$theme-text-muted: #999;
$primary: #09f;
$danger: #ff4141;
$secondary: $theme-elevation-2;
$body-bg: $theme-elevation-0;
$body-color: $theme-text;
$link-color: $theme-text;
$text-muted: $theme-text-muted;
$border-color: $theme-elevation-2;

View File

@ -1,18 +1,4 @@
$theme-elevation-0: hsl(0, 0%, 0%); @import "./variables";
$theme-elevation-1: hsl(0, 0%, 10%);
$theme-elevation-2: hsl(0, 0%, 20%);
$theme-text: #ccc;
$theme-text-muted: #999;
$primary: #09f;
$danger: #ff4141;
$secondary: $theme-elevation-2;
$body-bg: $theme-elevation-0;
$body-color: $theme-text;
$link-color: $theme-text;
$text-muted: $theme-text-muted;
$border-color: $theme-elevation-2;
// Card // Card
$card-bg: $theme-elevation-1; $card-bg: $theme-elevation-1;
@ -42,9 +28,6 @@ $custom-range-track-height: 0.1rem;
$custom-range-thumb-bg: $theme-text; $custom-range-thumb-bg: $theme-text;
$custom-range-track-bg: $theme-text-muted; $custom-range-track-bg: $theme-text-muted;
// Other
$progress-bg: rgb(35, 35, 35);
:root { :root {
--text-body: #{$theme-text}; --text-body: #{$theme-text};
--text-muted: #{$theme-text-muted}; --text-muted: #{$theme-text-muted};