음악, 삶, 개발
Slider.vue (초안) 본문
<!-- App.vue -->
<template>
<div id="container">
<slider
v-for="i in 8"
:key="i"
/>
</div>
</template>
<script>
import Slider from './Slider.vue'
export default {
components : { Slider }
}
</script>
<style>
#container {
width : 100%;
height : 100%;
background-color : rgb(25, 25, 25);
display : grid;
grid-auto-flow : column;
box-sizing : border-box;
padding : 4px;
}
</style>
<!-- Slider.vue -->
<template>
<div class="slider">
<!-- label -->
<div
class =" slider-label
slider-text-element
slider-first-child "
v-text ="state.labelText"
:style ="state.style.textElement"
></div>
<!-- random button -->
<button
class =" slider-random-button
slider-text-element
slider-first-child "
v-text =" state.randomButtonText"
:style =" state.style.textElement"
@mousedown.prevent.stop =" mouseDownRandomButton"
></button>
<!-- meter -->
<div
class =" slider-meter
slider-text-element
slider-first-child "
>
<div
class =" slider-meter-spacer"
@wheel.prevent.stop =" mouseWheelMeter"
@dblclick.prevent.stop =" mouseDoubleClickMeter"
@mousedown.prevent.stop =" mouseDownMeter"
@contextmenu.prevent.stop
>
<div
class ="slider-meter-display"
:style ="state.style.meterDisplay"
></div>
</div>
</div>
<!-- number box -->
<div
class =" slider-number-box
slider-text-element
slider-first-child "
v-text =" state.value.toFixed(2)"
:style =" state.style.textElement"
></div>
</div>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
setup() {
const state = reactive({
labelText : 'Slider',
randomButtonText : 'Random',
value : 0.5,
valueDefault : 0.5,
incrementWheel : 0.075,
color : getRandomColor(),
transitionTime : 0.2,
style : {
textElement : {
color : computed(() => state.color)
},
meterDisplay : {
backgroundColor : computed(() => state.color),
height : computed(() => getMeterHeight(state.value)),
transition : 'height 0s' // will be changed by random button.
},
},
mouseDownMeterChaser : {
// will be updated by mouseDownMeter
targetHeight : 0,
screenY : 0,
valueLast : 0,
}
})
function getMeterHeight(valueOfState) {
return (valueOfState * 100) + '%'
}
function getRandomColor() {
const hue = Math.random() * 360
const saturation = 45 // max is 100
const lightness = 50 // max is 100
return `hsl(${hue}, ${saturation}%, ${lightness}%)`
}
// function getRandomGradientColor() {
// return `linear-gradient(0deg, ${getRandomColor()} 0%, ${getRandomColor()} 100%)`
// }
function mouseDownRandomButton(e) {
// left button only
if (e.button === 0) {
// change meter transition time.
state.style.meterDisplay.transition = `height ${state.transitionTime}s`
// randomize value.
state.value = Math.random()
// back again for other event which shouldn't meter to be transitioned.
setTimeout(() => { state.style.meterDisplay.transition = 'height 0s' }, state.transitionTime * 1000);
}
}
function mouseWheelMeter(e) {
// wheel up.
if (e.deltaY < 0) {
if (state.value + state.incrementWheel <= 1) state.value += state.incrementWheel
else if (state.value !== 1) state.value = 1
}
// wheel down
else {
if (state.value - state.incrementWheel >= 0) state.value -= state.incrementWheel
else if (state.value !== 0) state.value = 0
}
}
function mouseDoubleClickMeter(e) {
// left button only
if (e.button === 0) state.value = state.valueDefault
}
function mouseDownMeter(e) {
// left button
if (e.button === 0) {
// update value
const height = e.currentTarget.offsetHeight
const mouseY = e.pageY - e.currentTarget.getBoundingClientRect().top
state.value = 1 - (mouseY / height)
// store what i've doen in this function for mouseMoveMeter
state.mouseDownMeterChaser.targetHeight = height
state.mouseDownMeterChaser.screenY = e.screenY
state.mouseDownMeterChaser.valueLast = state.value
// turn on eventListener
window.addEventListener('mousemove', mouseMoveMeter)
window.addEventListener('mouseup', mouseUpMeter)
}
// right button
else if (e.button === 2) {
state.value = state.valueDefault
}
}
function mouseMoveMeter(e) {
const height = state.mouseDownMeterChaser.targetHeight
const offsetY = state.mouseDownMeterChaser.screenY - e.screenY
const relativeY = offsetY / height
const valueToUpdate = state.mouseDownMeterChaser.valueLast + relativeY
if (valueToUpdate > 1) state.value = 1
else if (valueToUpdate < 0) state.value = 0
else state.value = valueToUpdate
}
function mouseUpMeter() {
// remove listener
window.removeEventListener('mousemove', mouseMoveMeter)
window.removeEventListener('mouseup', mouseUpMeter)
}
return {
state,
getMeterHeight,
mouseDownRandomButton,
mouseWheelMeter,
mouseDoubleClickMeter,
mouseDownMeter
}
}
}
</script>
<style scoped>
.slider {
margin : 4px;
/* about child */
display : grid;
grid-template-rows : 1fr 1.5fr 25fr 1fr;
gap : 8px;
}
.slider-first-child {
background-color : rgb(20, 20, 20);
box-sizing : border-box;
border : 2px solid rgb(40, 40, 40);
border-radius : 4px;
}
.slider-text-element {
font-family : Helvetica, sans-serif;
font-size : 14px;
/* make text center */
display : flex;
justify-content : center;
align-items : center;
overflow : hidden;
text-overflow : ellipsis;
}
.slider-random-button:active {
background : rgb(50, 50, 50);
box-sizing : border-box;
border : 2px solid white;
}
.slider-meter {
box-sizing : border-box;
padding : 50px;
}
.slider-meter-spacer {
position : relative;
width : 100%;
height : 100%;
background-color : rgb(5, 5, 5);
border-radius : 20px;
}
.slider-meter-display {
position : absolute;
width : 100%;
bottom : 0;
border-radius : 20px;
}
</style>