split.vue 6.84 KB
<template>
    <div ref="outerWrapper" :class="wrapperClasses">
        <div v-if="isHorizontal" :class="`${prefix}-horizontal`">
            <div :style="{right: `${anotherOffset}%`}" class="left-pane" :class="paneClasses">
                <slot name="left"/>
            </div>
            <div :class="`${prefix}-trigger-con`" :style="{left: `${offset}%`}" @mousedown="handleMousedown">
                <slot name="trigger">
                    <trigger mode="vertical"/>
                </slot>
            </div>
            <div :style="{left: `${offset}%`}" class="right-pane" :class="paneClasses">
                <slot name="right"/>
            </div>
        </div>
        <div v-else :class="`${prefix}-vertical`">
            <div :style="{bottom: `${anotherOffset}%`}" class="top-pane" :class="paneClasses">
                <slot name="top"/>
            </div>
            <div :class="`${prefix}-trigger-con`" :style="{top: `${offset}%`}" @mousedown="handleMousedown">
                <slot name="trigger">
                    <trigger mode="horizontal"/>
                </slot>
            </div>
            <div :style="{top: `${offset}%`}" class="bottom-pane" :class="paneClasses">
                <slot name="bottom"/>
            </div>
        </div>
    </div>
</template>

<script>
    import {oneOf} from '../../utils/assist';
    import {on, off} from '../../utils/dom';
    import Trigger from './trigger.vue';
    export default {
        name: 'Split',
        components: {
            Trigger
        },
        props: {
            value: {
                type: [Number, String],
                default: 0.5
            },
            mode: {
                validator (value) {
                    return oneOf(value, ['horizontal', 'vertical']);
                },
                default: 'horizontal'
            },
            min: {
                type: [Number, String],
                default: '40px'
            },
            max: {
                type: [Number, String],
                default: '40px'
            }
        },
        /**
         * Events
         * @on-move-start
         * @on-moving 返回值:事件对象,但是在事件对象中加入了两个参数:atMin(当前是否在最小值处), atMax(当前是否在最大值处)
         * @on-move-end
         */
        data () {
            return {
                prefix: 'ivu-split',
                offset: 0,
                oldOffset: 0,
                isMoving: false
            };
        },
        computed: {
            wrapperClasses () {
                return [
                    `${this.prefix}-wrapper`,
                    this.isMoving ? 'no-select' : ''
                ];
            },
            paneClasses () {
                return [
                    `${this.prefix}-pane`,
                    {
                        [`${this.prefix}-pane-moving`]: this.isMoving
                    }
                ];
            },
            isHorizontal () {
                return this.mode === 'horizontal';
            },
            anotherOffset () {
                return 100 - this.offset;
            },
            valueIsPx () {
                return typeof this.value === 'string';
            },
            offsetSize () {
                return this.isHorizontal ? 'offsetWidth' : 'offsetHeight';
            },
            computedMin () {
                return this.getComputedThresholdValue('min');
            },
            computedMax () {
                return this.getComputedThresholdValue('max');
            }
        },
        methods: {
            px2percent (numerator, denominator) {
                return parseFloat(numerator) / parseFloat(denominator);
            },
            getComputedThresholdValue (type) {
                let size = this.$refs.outerWrapper[this.offsetSize];
                if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type];
                else return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type];
            },
            getMin (value1, value2) {
                if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`;
                else return Math.min(value1, value2);
            },
            getMax (value1, value2) {
                if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`;
                else return Math.max(value1, value2);
            },
            getAnotherOffset (value) {
                let res = 0;
                if (this.valueIsPx) res = `${this.$refs.outerWrapper[this.offsetSize] - parseFloat(value)}px`;
                else res = 1 - value;
                return res;
            },
            handleMove (e) {
                let pageOffset = this.isHorizontal ? e.pageX : e.pageY;
                let offset = pageOffset - this.initOffset;
                let outerWidth = this.$refs.outerWrapper[this.offsetSize];
                let value = this.valueIsPx ? `${parseFloat(this.oldOffset) + offset}px` : (this.px2percent(outerWidth * this.oldOffset + offset, outerWidth));
                let anotherValue = this.getAnotherOffset(value);
                if (parseFloat(value) <= parseFloat(this.computedMin)) value = this.getMax(value, this.computedMin);
                if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax));
                e.atMin = this.value === this.computedMin;
                e.atMax = this.valueIsPx ? this.getAnotherOffset(this.value) === this.computedMax : this.getAnotherOffset(this.value).toFixed(5) === this.computedMax.toFixed(5);
                this.$emit('input', value);
                this.$emit('on-moving', e);
            },
            handleUp () {
                this.isMoving = false;
                off(document, 'mousemove', this.handleMove);
                off(document, 'mouseup', this.handleUp);
                this.$emit('on-move-end');
            },
            handleMousedown (e) {
                this.initOffset = this.isHorizontal ? e.pageX : e.pageY;
                this.oldOffset = this.value;
                this.isMoving = true;
                on(document, 'mousemove', this.handleMove);
                on(document, 'mouseup', this.handleUp);
                this.$emit('on-move-start');
            },
            computeOffset(){
                this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100;
            }
        },
        watch: {
            value () {
                this.computeOffset();
            }
        },
        mounted () {
            this.$nextTick(() => {
                this.computeOffset();
            });

            window.addEventListener('resize', ()=>{
                this.computeOffset();
            });
        }
    };
</script>