117 lines
2.9 KiB
Vue
117 lines
2.9 KiB
Vue
<template>
|
|
<div class="aplayer-volume-wrap">
|
|
<icon-button
|
|
:class="`aplayer-icon-${volumeIcon}`"
|
|
:icon="volumeIcon"
|
|
@click="emit('togglemute')"
|
|
/>
|
|
<div
|
|
class="aplayer-volume-bar-wrap"
|
|
@mousedown="onBarMouseDown"
|
|
>
|
|
<div class="aplayer-volume-bar" ref="bar">
|
|
<div
|
|
class="aplayer-volume"
|
|
:style="{
|
|
height: muted ? 0 : `${Math.trunc(volume * 100)}%`,
|
|
background: theme
|
|
}"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { computed, ref } from 'vue'
|
|
import IconButton from './aplayer-iconbutton.vue'
|
|
import {getElementViewTop} from '../utils'
|
|
|
|
const props = defineProps<{
|
|
volume: number
|
|
muted: boolean
|
|
theme: string
|
|
}>()
|
|
const emit = defineEmits(['setvolume', 'togglemute'])
|
|
const barHeight = 40
|
|
|
|
const volumeIcon = computed(() => {
|
|
if (props.muted || props.volume <= 0) return 'volume_off'
|
|
if (props.volume >= 1) return 'volume_up'
|
|
return 'volume_down'
|
|
})
|
|
|
|
const bar = ref<unknown>(null)
|
|
|
|
const onBarMouseDown = () => {
|
|
document.addEventListener('mousemove', onDocumentMouseMove)
|
|
document.addEventListener('mouseup', onDocumentMouseUp)
|
|
}
|
|
const onDocumentMouseMove = (e: MouseEvent) => {
|
|
let percentage = (barHeight - e.clientY + getElementViewTop(<HTMLElement>bar.value)) / barHeight
|
|
percentage = percentage > 0 ? percentage : 0
|
|
percentage = percentage < 1 ? percentage : 1
|
|
emit('setvolume', percentage)
|
|
}
|
|
const onDocumentMouseUp = (e: MouseEvent) => {
|
|
document.removeEventListener('mouseup', onDocumentMouseUp)
|
|
document.removeEventListener('mousemove', onDocumentMouseMove)
|
|
|
|
let percentage = (barHeight - e.clientY + getElementViewTop(<HTMLElement>bar.value)) / barHeight
|
|
percentage = percentage > 0 ? percentage : 0
|
|
percentage = percentage < 1 ? percentage : 1
|
|
emit('setvolume', percentage)
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.aplayer-volume-wrap {
|
|
position: relative;
|
|
cursor: pointer;
|
|
z-index: 0;
|
|
&:hover .aplayer-volume-bar-wrap {
|
|
display: block;
|
|
}
|
|
.aplayer-volume-bar-wrap {
|
|
display: none;
|
|
position: absolute;
|
|
bottom: 15px;
|
|
left: -4px;
|
|
right: -4px;
|
|
height: 40px;
|
|
z-index: -1;
|
|
transition: all .2s ease;
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -16px;
|
|
left: 0;
|
|
right: 0;
|
|
height: 62px;
|
|
background-color: #fff;
|
|
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.07), 0 0 5px 0 rgba(0, 0, 0, 0.1);
|
|
}
|
|
.aplayer-volume-bar {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 11px;
|
|
width: 5px;
|
|
height: 40px;
|
|
background: #aaa;
|
|
border-radius: 2.5px;
|
|
overflow: hidden;
|
|
z-index: 1;
|
|
|
|
.aplayer-volume {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
transition: height 0.1s ease, background-color .3s;
|
|
will-change: height;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style> |