replace bootstrap-vue progress bar with vue-slider-component
better hit area, drag support
This commit is contained in:
		
							parent
							
								
									e1dc32060b
								
							
						
					
					
						commit
						73b2b493a4
					
				| @ -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)) | ||||||
|       }, |       }, | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								src/player/ProgressBar.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/player/ProgressBar.vue
									
									
									
									
									
										Normal 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> | ||||||
| @ -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 }) { | ||||||
|  | |||||||
| @ -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"> | ||||||
|  | |||||||
| @ -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
									
								
							
							
						
						
									
										15
									
								
								src/style/_variables.scss
									
									
									
									
									
										Normal 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; | ||||||
| @ -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}; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user