Vue之可拖动容器大小(ResizableContainer)

Vue之ResizableContainer

效果

Vue之可拖动容器大小(ResizableContainer)_第1张图片

代码

<template>
    <div class="container">
        <div class="resizable-container" v-panel-resize:vertical="handleResize">
            <div class="item item-1 wrapper" weight="0.3">1div>
            <div class="item item-2 wrapper" weight="0.35">2div>
            <div class="item item-3 wrapper" weight="0.35">3div>
        div>
    div>
template>
<script>
import PanelResize from '@/directive/resizer'

export default {
    directives: {
        PanelResize
    },
    data: () => ({
    	
    }),
    mounted() {
    	
    },
    methods: {
        handleResize(weights) {
            console.log(weights)
        }
    }
};
</script>
<style lang="scss">
.resizable-container {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: stretch;
    min-width: 0;
    min-height: 0;
    height: 100%;

    &.horizontal {
        flex-direction: row;
    }

    .wrapper {
        display: flex;
        flex: 1;
        flex-direction: column;
        align-items: stretch;
        overflow: hidden;
        min-width: 20px;
        min-height: 20px;

        &.horizontal {
            flex-direction: row;
        }
    }
}

.ide-divider {
    position: absolute;
    z-index: 97;

    &:after {
        position: absolute;
        background-color: #505050;
        content: "";
    }

    &.horizontal {
        right: 0;
        top: 0;
        width: 7px;
        height: 100%;
        margin: 0 -3px;
        cursor: ew-resize;

        &:after {
            top: 0;
            bottom: 0;
            left: 3px;
            width: 1px;
        }
    }

    &.vertical {
        left: 0;
        bottom: 0;
        width: 100%;
        height: 7px;
        margin: -3px 0;
        cursor: ns-resize;

        &:after {
            left: 0;
            right: 0;
            top: 3px;
            height: 1px;
        }
    }
}


.container {
    width: 400px;
    height: 450px;
    margin: 50px auto;
    border: 2px solid rgb(18, 78, 243);
}
.item {
}
.item-1 {
    background-color: pink;
}
.item-2 {
    background-color: palegoldenrod;
}
.item-3 {
    background-color: peachpuff;
}
style>

指令

import { hasClass } from "../../utils";

const resizer = {
    bind(el, binding) {
        binding.arg = binding.arg || 'vertical'

        const handleResizer = typeof binding.value === 'function' ? binding.value : () => {}

        const getChildren = (el) => {
            let childs = el.childNodes || []
            let children = []
            childs.forEach(child => {
                if (child.nodeType === 1 && !hasClass(child, 'ide-divider') && child.hasAttribute('weight')) {
                    children.push(child)
                }
            })
            return children
        }

        const createDivider = (el, binding) => {
            el.style.position = 'relative'
            let divider = document.createElement('div')
            divider.setAttribute('class', 'ide-divider ' + binding.arg)
            el.appendChild(divider)
            return divider
        }

        const handleResize = (targetElement, clientX, clientY, index, nextIndex, weights) => {
            const horizontal = binding.arg === 'horizontal' ? true : false;
            const { left, top } = targetElement.getBoundingClientRect();
            const { offsetWidth, offsetHeight } = targetElement;
            const position = horizontal ? clientX - left : clientY - top;
            const containerSize = horizontal ? offsetWidth : offsetHeight;

            /**
             * 1.计算总共的比重
             * 2.计算resize前,当前容器它之前的容器比重之和
             */
            let totalWeight = 0;
            let subtotalWeight = 0;

            weights.forEach((weight, i) => {
                totalWeight += weight;

                if (i < nextIndex) subtotalWeight += weight;
            });

            // console.log('totalWeight', totalWeight, subtotalWeight, index)

            /**
             * 计算resize后的新比重
             *
             *   newWeight        position
             * ———————————— = ————————————————
             *  totalWeight     containerSize
             */

            const newWeight = (position / containerSize) * totalWeight;
            let deltaWeight = newWeight - subtotalWeight;

            /**
             * 能调整的最大最小的比重
             */
            deltaWeight = Math.max(deltaWeight, -weights[index]);
            deltaWeight = Math.min(deltaWeight, weights[nextIndex]);

            /**
             * 前一个容器  +=
             * 当前容器    -=
             */
            weights[index] += deltaWeight;
            weights[nextIndex] -= deltaWeight;

            return weights
        }

        let target
        
        const handleMouseDown = (e) => {
            target = e.target
            e.preventDefault()
            document.addEventListener('mousemove', handleMouseMove, false)
            document.addEventListener('mouseup', handleMouseUp, false)
        }

        const handleMouseMove = (e) => {
            let dx = e.clientX
            let dy = e.clientY
            let children = getChildren(el)
            let weights = []
            let index = 0, nextIndex

            children.forEach((child, i) => {
                let weight = +(child.getAttribute('weight') || (1 / children.length))
                weights.push(weight)
                if (target && target.parentElement === child) {
                    index = i
                    nextIndex = i + 1
                }
            })

            let newWeights = handleResize(el, dx, dy, index, nextIndex, weights)

            children.forEach((child, i) => {
                let weight = newWeights[i]
                child.style.flexGrow = weight
                child.setAttribute('weight', weight)
            })

            handleResizer(weights)
        }

        const handleMouseUp = () => {
            target = null
            document.removeEventListener('mousemove', handleMouseMove)
            document.removeEventListener('mouseup', handleMouseUp)
        }

        const init = () => {
            if (!el) {
                return
            }
            const children = getChildren(el)

            // console.log('children', children)

            children.forEach((child, index) => {
                let weight = +(child.getAttribute('weight') || (1 / children.length))
                child.style.flexGrow = weight

                if (index < children.length - 1) {
                    let divider = createDivider(child, binding)
                    divider.addEventListener('mousedown', handleMouseDown, false)
                }
            })
        }

        init()
    }
}

resizer.install = Vue => Vue.directive('panel-resize', resizer)

export default resizer

你可能感兴趣的:(vue,vue,js,前端,html5)