新增播放速率调整功能
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div :class="[$style.sliderContent, className]">
|
||||
<div :class="[$style.slider ]">
|
||||
<div ref="dom_sliderBar" :class="$style.sliderBar" :style="{ transform: `scaleX(${(value - min) / (max - min) || 0})` }" />
|
||||
</div>
|
||||
<div :class="$style.sliderMask" @mousedown="handleSliderMsDown" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onBeforeUnmount } from '@common/utils/vueTools'
|
||||
// import { player as eventPlayerNames } from '@renderer/event/names'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['change'],
|
||||
setup(props, { emit }) {
|
||||
const sliderEvent = {
|
||||
isMsDown: false,
|
||||
msDownX: 0,
|
||||
msDownValue: 0,
|
||||
}
|
||||
const dom_sliderBar = ref(null)
|
||||
|
||||
const handleSliderMsDown = event => {
|
||||
sliderEvent.isMsDown = true
|
||||
sliderEvent.msDownX = event.clientX
|
||||
|
||||
sliderEvent.msDownValue = event.offsetX / dom_sliderBar.value.clientWidth
|
||||
let val = sliderEvent.msDownValue * (props.max - props.min) + props.min
|
||||
if (val < props.min) val = props.min
|
||||
if (val > props.max) val = props.max
|
||||
emit('change', val)
|
||||
|
||||
// if (isMute.value) window.app_event.setSliderIsMute(false)
|
||||
}
|
||||
const handleSliderMsUp = () => {
|
||||
sliderEvent.isMsDown = false
|
||||
}
|
||||
const handleSliderMsMove = event => {
|
||||
if (!sliderEvent.isMsDown) return
|
||||
let value = (sliderEvent.msDownValue + (event.clientX - sliderEvent.msDownX) / dom_sliderBar.value.clientWidth) * (props.max - props.min) + props.min
|
||||
if (value > props.max) value = props.max
|
||||
else if (value < props.min) value = props.min
|
||||
emit('change', value)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', handleSliderMsMove)
|
||||
document.addEventListener('mouseup', handleSliderMsUp)
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('mousemove', handleSliderMsMove)
|
||||
document.removeEventListener('mouseup', handleSliderMsUp)
|
||||
})
|
||||
|
||||
return {
|
||||
handleSliderMsDown,
|
||||
dom_sliderBar,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.slider-content {
|
||||
flex: none;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
padding: 5px 0;
|
||||
// margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: .5;
|
||||
transition: opacity @transition-normal;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.slider {
|
||||
// cursor: pointer;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
transition: @transition-normal;
|
||||
transition-property: background-color, opacity;
|
||||
background-color: var(--color-primary-alpha-700);
|
||||
// background-color: #f5f5f5;
|
||||
position: relative;
|
||||
// border-radius: @radius-progress-border;
|
||||
}
|
||||
|
||||
// .muted {
|
||||
// opacity: .5;
|
||||
// }
|
||||
|
||||
.slider-bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: scaleX(0);
|
||||
transform-origin: 0;
|
||||
transition-property: transform;
|
||||
transition-timing-function: ease;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// border-radius: @radius-progress-border;
|
||||
transition-duration: 0.2s;
|
||||
background-color: var(--color-button-font);
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.slider-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<material-popup-btn :class="$style.btnContent">
|
||||
<button :class="[$style.btn, { [$style.active]: playbackRate != 1 }]" :aria-label="`${$t('player__playback_rate')}x${playbackRate}`">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" width="100%" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-plex" />
|
||||
</svg>
|
||||
</button>
|
||||
<template #content>
|
||||
<div :class="$style.setting">
|
||||
<div :class="$style.info">
|
||||
<span>x{{ playbackRate }}</span>
|
||||
<base-btn min @click="handleUpdatePlaybackRate(1)">{{ $t('player__playback_rate_reset_btn') }}</base-btn>
|
||||
</div>
|
||||
<base-slider-bar :value="playbackRate" :min="0.5" :max="2" @change="handleUpdatePlaybackRate" />
|
||||
</div>
|
||||
</template>
|
||||
</material-popup-btn>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import { computed } from '@common/utils/vueTools'
|
||||
import { playbackRate } from '@renderer/store/player/playbackRate'
|
||||
|
||||
const handleUpdatePlaybackRate = (val) => {
|
||||
window.app_event.setPlaybackRate(Math.round(val * 10) / 10)
|
||||
}
|
||||
|
||||
// const icon = computed(() => {
|
||||
// return playbackRate.value == 0
|
||||
// ? '#icon-volume-off-outline'
|
||||
// : playbackRate.value < 0.3
|
||||
// ? '#icon-volume-low-outline'
|
||||
// : playbackRate.value < 0.7
|
||||
// ? '#icon-volume-medium-outline'
|
||||
// : '#icon-volume-high-outline'
|
||||
// })
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
.btnContent {
|
||||
flex: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.btn {
|
||||
position: relative;
|
||||
// color: var(--color-button-font);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: color @transition-normal;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
width: 24px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
transition: opacity @transition-fast;
|
||||
opacity: .5;
|
||||
// filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
&:hover {
|
||||
svg {
|
||||
opacity: .9;
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
svg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
svg {
|
||||
color: var(--color-primary);
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.setting {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
padding: 2px 3px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
span {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
@@ -1,133 +0,0 @@
|
||||
<template>
|
||||
<div :class="[$style.volumeContent, className]">
|
||||
<div :class="[$style.volume ]">
|
||||
<div ref="dom_volumeBar" :class="$style.volumeBar" :style="{ transform: `scaleX(${volume || 0})` }" />
|
||||
</div>
|
||||
<div :class="$style.volumeMask" @mousedown="handleVolumeMsDown" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onBeforeUnmount } from '@common/utils/vueTools'
|
||||
// import { player as eventPlayerNames } from '@renderer/event/names'
|
||||
|
||||
import { volume, isMute } from '@renderer/store/player/volume'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
className: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const volumeEvent = {
|
||||
isMsDown: false,
|
||||
msDownX: 0,
|
||||
msDownVolume: 0,
|
||||
}
|
||||
const dom_volumeBar = ref(null)
|
||||
|
||||
const handleVolumeMsDown = event => {
|
||||
volumeEvent.isMsDown = true
|
||||
volumeEvent.msDownX = event.clientX
|
||||
|
||||
let val = event.offsetX / dom_volumeBar.value.clientWidth
|
||||
if (val < 0) val = 0
|
||||
if (val > 1) val = 1
|
||||
|
||||
window.app_event.setVolume(volumeEvent.msDownVolume = val)
|
||||
|
||||
// if (isMute.value) window.app_event.setVolumeIsMute(false)
|
||||
}
|
||||
const handleVolumeMsUp = () => {
|
||||
volumeEvent.isMsDown = false
|
||||
}
|
||||
const handleVolumeMsMove = event => {
|
||||
if (!volumeEvent.isMsDown) return
|
||||
let volume = volumeEvent.msDownVolume + (event.clientX - volumeEvent.msDownX) / dom_volumeBar.value.clientWidth
|
||||
if (volume > 1) volume = 1
|
||||
else if (volume < 0) volume = 0
|
||||
window.app_event.setVolume(volume)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', handleVolumeMsMove)
|
||||
document.addEventListener('mouseup', handleVolumeMsUp)
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('mousemove', handleVolumeMsMove)
|
||||
document.removeEventListener('mouseup', handleVolumeMsUp)
|
||||
})
|
||||
|
||||
return {
|
||||
handleVolumeMsDown,
|
||||
dom_volumeBar,
|
||||
volume,
|
||||
isMute,
|
||||
appSetting,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.volume-content {
|
||||
flex: none;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
padding: 5px 0;
|
||||
// margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: .5;
|
||||
transition: opacity @transition-normal;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.volume {
|
||||
// cursor: pointer;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
transition: @transition-normal;
|
||||
transition-property: background-color, opacity;
|
||||
background-color: var(--color-primary-alpha-700);
|
||||
// background-color: #f5f5f5;
|
||||
position: relative;
|
||||
// border-radius: @radius-progress-border;
|
||||
}
|
||||
|
||||
// .muted {
|
||||
// opacity: .5;
|
||||
// }
|
||||
|
||||
.volume-bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: scaleX(0);
|
||||
transform-origin: 0;
|
||||
transition-property: transform;
|
||||
transition-timing-function: ease;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// border-radius: @radius-progress-border;
|
||||
transition-duration: 0.2s;
|
||||
background-color: var(--color-button-font);
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.volume-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -16,7 +16,7 @@
|
||||
@update:model-value="saveVolumeIsMute($event)"
|
||||
/>
|
||||
</div>
|
||||
<common-volume-bar />
|
||||
<base-slider-bar :value="volume" :min="0" :max="1" @change="handleUpdateVolume" />
|
||||
</div>
|
||||
</template>
|
||||
</material-popup-btn>
|
||||
@@ -30,6 +30,10 @@ import { computed } from '@common/utils/vueTools'
|
||||
import { saveVolumeIsMute } from '@renderer/store/setting'
|
||||
import { volume, isMute } from '@renderer/store/player/volume'
|
||||
|
||||
const handleUpdateVolume = (val) => {
|
||||
window.app_event.setVolume(val)
|
||||
}
|
||||
|
||||
const icon = computed(() => {
|
||||
return isMute.value
|
||||
? '#icon-volume-mute-outline'
|
||||
|
||||
@@ -14,6 +14,7 @@ div(:class="$style.footerLeftControlBtns")
|
||||
button(:class="[$style.footerLeftControlBtn, {[$style.active]: isShowPlayComment}]" @click="toggleVisibleComment" :aria-label="$t('comment__show')")
|
||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='95%' viewBox='0 0 24 24' space='preserve')
|
||||
use(xlink:href='#icon-comment')
|
||||
common-playback-rate-btn
|
||||
common-volume-btn
|
||||
common-toggle-play-mode-btn
|
||||
button(:class="$style.footerLeftControlBtn" @click="isShowAddMusicTo = true" :aria-label="$t('player__add_music_to')")
|
||||
|
||||
Reference in New Issue
Block a user