mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			154 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			154 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div class="relative" v-click-outside="clickOutside" @mouseover="mouseover" @mouseleave="mouseleave">
 | |
|     <button :aria-label="$strings.LabelVolume" class="text-gray-300 hover:text-white" @mousedown.prevent @mouseup.prevent @click="clickVolumeIcon">
 | |
|       <span class="material-symbols text-2xl sm:text-3xl">{{ volumeIcon }}</span>
 | |
|     </button>
 | |
|     <transition name="menux">
 | |
|       <div v-show="isOpen" class="volumeMenu h-28 absolute bottom-2 w-6 py-2 bg-bg shadow-sm rounded-lg" style="top: -116px">
 | |
|         <div ref="volumeTrack" class="w-1 h-full bg-gray-500 mx-2.5 relative cursor-pointer rounded-full" @mousedown="mousedownTrack" @click="clickVolumeTrack">
 | |
|           <div class="bg-gray-100 w-full absolute left-0 bottom-0 pointer-events-none rounded-full" :style="{ height: volume * trackHeight + 'px' }" />
 | |
|           <div class="w-2.5 h-2.5 bg-white shadow-sm rounded-full absolute pointer-events-none" :class="isDragging ? 'transform scale-125 origin-center' : ''" :style="{ bottom: cursorBottom + 'px', left: '-3px' }" />
 | |
|         </div>
 | |
|       </div>
 | |
|     </transition>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| export default {
 | |
|   props: {
 | |
|     value: Number
 | |
|   },
 | |
|   data() {
 | |
|     return {
 | |
|       isOpen: false,
 | |
|       isDragging: false,
 | |
|       isHovering: false,
 | |
|       posY: 0,
 | |
|       lastValue: 0.5,
 | |
|       isMute: false,
 | |
|       trackHeight: 112 - 20,
 | |
|       openTimeout: null
 | |
|     }
 | |
|   },
 | |
|   computed: {
 | |
|     volume: {
 | |
|       get() {
 | |
|         return this.value
 | |
|       },
 | |
|       set(val) {
 | |
|         try {
 | |
|           localStorage.setItem('volume', val)
 | |
|         } catch (error) {
 | |
|           console.error('Failed to store volume', err)
 | |
|         }
 | |
|         this.$emit('input', val)
 | |
|       }
 | |
|     },
 | |
|     cursorBottom() {
 | |
|       var bottom = this.trackHeight * this.volume
 | |
|       return bottom - 3
 | |
|     },
 | |
|     volumeIcon() {
 | |
|       if (this.volume <= 0) return 'volume_mute'
 | |
|       else if (this.volume <= 0.5) return 'volume_down'
 | |
|       else return 'volume_up'
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     scroll(e) {
 | |
|       if (!e || !e.wheelDeltaY) return
 | |
|       if (e.wheelDeltaY > 0) {
 | |
|         this.volume = Math.min(1, this.volume + 0.1)
 | |
|       } else {
 | |
|         this.volume = Math.max(0, this.volume - 0.1)
 | |
|       }
 | |
|     },
 | |
|     mouseover() {
 | |
|       if (!this.isHovering) {
 | |
|         window.addEventListener('mousewheel', this.scroll)
 | |
|       }
 | |
|       this.isHovering = true
 | |
|       this.setOpen()
 | |
|     },
 | |
|     mouseleave() {
 | |
|       if (this.isHovering) {
 | |
|         window.removeEventListener('mousewheel', this.scroll)
 | |
|       }
 | |
|       this.isHovering = false
 | |
|     },
 | |
|     setOpen() {
 | |
|       this.isOpen = true
 | |
|       clearTimeout(this.openTimeout)
 | |
|       this.openTimeout = setTimeout(() => {
 | |
|         if (!this.isHovering && !this.isDragging) {
 | |
|           this.isOpen = false
 | |
|         } else {
 | |
|           this.setOpen()
 | |
|         }
 | |
|       }, 600)
 | |
|     },
 | |
|     mousemove(e) {
 | |
|       var diff = this.posY - e.y
 | |
|       this.posY = e.y
 | |
|       var volShift = diff / this.trackHeight
 | |
|       var newVol = this.volume + volShift
 | |
|       newVol = Math.min(Math.max(0, newVol), 1)
 | |
|       this.volume = newVol
 | |
|       e.preventDefault()
 | |
|     },
 | |
|     mouseup(e) {
 | |
|       if (this.isDragging) {
 | |
|         this.isDragging = false
 | |
|         document.body.removeEventListener('mousemove', this.mousemove)
 | |
|         document.body.removeEventListener('mouseup', this.mouseup)
 | |
|       }
 | |
|     },
 | |
|     mousedownTrack(e) {
 | |
|       this.isDragging = true
 | |
|       this.posY = e.y
 | |
|       var vol = 1 - e.offsetY / this.trackHeight
 | |
|       vol = Math.min(Math.max(vol, 0), 1)
 | |
|       this.volume = vol
 | |
|       document.body.addEventListener('mousemove', this.mousemove)
 | |
|       document.body.addEventListener('mouseup', this.mouseup)
 | |
|       e.preventDefault()
 | |
|     },
 | |
|     clickOutside() {
 | |
|       this.isOpen = false
 | |
|     },
 | |
|     clickVolumeIcon() {
 | |
|       this.isMute = !this.isMute
 | |
|       if (this.isMute) {
 | |
|         this.lastValue = this.volume
 | |
|         this.volume = 0
 | |
|       } else {
 | |
|         this.volume = this.lastValue || 0.5
 | |
|       }
 | |
|     },
 | |
|     toggleMute() {
 | |
|       this.clickVolumeIcon()
 | |
|     },
 | |
|     clickVolumeTrack(e) {
 | |
|       var vol = 1 - e.offsetY / this.trackHeight
 | |
|       vol = Math.min(Math.max(vol, 0), 1)
 | |
|       this.volume = vol
 | |
|     }
 | |
|   },
 | |
|   mounted() {
 | |
|     if (this.value === 0) {
 | |
|       this.isMute = true
 | |
|     }
 | |
|     const storageVolume = localStorage.getItem('volume')
 | |
|     if (storageVolume && !isNaN(storageVolume)) {
 | |
|       this.volume = parseFloat(storageVolume)
 | |
|     }
 | |
|   },
 | |
|   beforeDestroy() {
 | |
|     window.removeEventListener('mousewheel', this.scroll)
 | |
|     document.body.removeEventListener('mousemove', this.mousemove)
 | |
|     document.body.removeEventListener('mouseup', this.mouseup)
 | |
|   }
 | |
| }
 | |
| </script>
 |