음악, 삶, 개발

Slider.vue (초안) 본문

개발 Web/Vue.js 공부방

Slider.vue (초안)

Lee_____ 2020. 12. 5. 09:59

<!-- 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>