请选择 进入手机版 | 继续访问电脑版

vue swipeCell滑动单元格(仿微信)的实现示例

[复制链接]
查看527 | 回复0 | 2020-10-6 17:51:06 | 显示全部楼层 |阅读模式
抽离Vant weapp滑动单元格代码改造而成

带有拉动弹性回弹效果

vue swipeCell滑动单元格(仿微信)的实现示例-1.png


demo展示:https://littaotao.github.io/me/index(切换为浏览器调试的手机模式并且再次刷新一次)

<template>
        <div
                class="cell_container"
                @touchstart
                v-click-outside="handleClickOutside"
                @click="getClickHandler('cell')">
                <div
                        :style="{'transform':
                        'translateX('+(offset+(isElastic?elasticX:0))+'px)','transition-duration':dragging?'0s':'0.6s'}">
                        <!-- <div ref="cellLeft" class="cell_left" @click="getClickHandler('left', true)">
                                <div>收藏</div>
                                <div>添加</div>
                        </div> -->
                        <div
                                @touchend="onClick()"
                                :class="offset?'cell_content':'cell_content_active'">SwipeCell</div>
                        <div ref="cellRight"
                                class="cell_right"
                                @click="getClickHandler('right', true)">
                                <div
                                        :class="type?'divPostion':''"
                                        ref="remove"
                                        :style="{'background':'#ccc','padding-left':'10px','padding-right':10+(isElastic?Math.abs(elasticX/3):0)+'px','transition-duration':dragging?'0s':'0.6s'}">标记</div>
                                <div
                                        :class="type?'divPostion':''"
                                        ref="tag"
                                        :style="{'transform': type?'translateX('+(-offset*removeWidth/cellRightWidth-(isElastic?elasticX/3:0))+'px)':'','padding-left':'10px','padding-right':10+(isElastic?Math.abs(elasticX/3):0)+'px','transition-duration':dragging?'0s':'0.6s','background':'#000'}">不再关注</div>
                                <div
                                        :class="type?'divPostion':''"
                                        :style="{'transform': type?'translateX('+(-offset*(removeWidth+tagWidth)/cellRightWidth-(isElastic?elasticX/3*2:0))+'px)':'','padding-left':'10px','padding-right':10+(isElastic?Math.abs(elasticX/3):0)+'px','transition-duration':dragging?'0s':'0.6s'}">删除</div>
                        </div>
                </div>
        </div>
</template>
<script>
import ClickOutside from 'vue-click-outside';
import { TouchMixin } from '@/components/mixins/touch';
export default{
        name:"SwipeCell",
        props: {
                // @deprecated
                // should be removed in next major version, use beforeClose instead
                onClose: Function,
                disabled: Boolean,
                leftWidth: [Number, String],
                rightWidth: [Number, String],
                beforeClose: Function,
                stopPropagation: Boolean,
                name: {
                        type: [Number, String],
                        default: '',
                },
                //
                type:{
                        type:[Number,String],
                        default:1 //0 常规 1 定位
                },
                isElastic:{ //弹性
                        type:Boolean,
                        default:true
                }
        },
        data(){
                return {
                        offset: 0,
                        dragging: true,
                        //-位移
                        elasticX:0,
                        removeWidth:0,
                        tagWidth:0,
                        cellRightWidth:0,
                        cellLeftWidth:0
                }
        },
        computed: {
                computedLeftWidth() {
                        return +this.leftWidth || this.getWidthByRef('cellLeft');
                },

                computedRightWidth() {
                        return +this.rightWidth || this.getWidthByRef('cellRight');
                },
        },
        mounted() {
                //防止弹性效果影响宽度
                this.cellRightWidth = this.getWidthByRef('cellRight');
                this.cellLeftWidth = this.getWidthByRef('cellLeft');
                this.removeWidth = this.getWidthByRef('remove');
                this.tagWidth = this.getWidthByRef('tag');
                this.bindTouchEvent(this.$el);
        },
        mixins: [
                TouchMixin
        ],
        directives: {
                ClickOutside
        },
        methods: {
                getWidthByRef(ref) {
                        if (this.$refs[ref]) {
                                const rect = this.$refs[ref].getBoundingClientRect();
                                //type=1定位时获取宽度为0,为此采用获取子元素宽度之和
                                if(!rect.width){
                                        let childWidth = 0;
                                        for(const item of this.$refs[ref].children){
                                                childWidth += item.getBoundingClientRect().width
                                        }
                                        return childWidth;
                                }
                                return rect.width;
                        }
                        return 0;
                },

                handleClickOutside(e){
                        if(this.opened) this.close()
                },

                // @exposed-api
                open(position) {
                        const offset =
                        position === 'left' ? this.computedLeftWidth : -this.computedRightWidth;

                        this.opened = true;
                        this.offset = offset;

                        this.$emit('open', {
                                position,
                                name: this.name,
                                // @deprecated
                                // should be removed in next major version
                                detail: this.name,
                        });
                },

                // @exposed-api
                close(position) {
                        this.offset = 0;

                        if (this.opened) {
                                this.opened = false;
                                this.$emit('close', {
                                        position,
                                        name: this.name,
                                });
                        }
                },

                onTouchStart(event) {
                        if (this.disabled) {
                                return;
                        }
                        this.startOffset = this.offset;
                        this.touchStart(event);
                },

                range(num, min, max) {
                        return Math.min(Math.max(num, min), max);
                },

                preventDefault(event, isStopPropagation) {
                        /* istanbul ignore else */
                        if (typeof event.cancelable !== 'boolean' || event.cancelable) {
                                event.preventDefault();
                        }

                        if (this.isStopPropagations) {
                                stopPropagation(event);
                        }
                },

                stopPropagations(event) {
                        event.stopPropagation();
                },

                onTouchMove(event) {
                        if (this.disabled) {
                                return;
                        }
                        this.touchMove(event);
                        if (this.direction === 'horizontal') {
                                this.dragging = true;
                                this.lockClick = true;
                                const isPrevent = !this.opened || this.deltaX * this.startOffset < 0;
                                if (isPrevent) {
                                        this.preventDefault(event, this.stopPropagation);
                                }

                                this.offset = this.range(
                                        this.deltaX + this.startOffset,
                                        -this.computedRightWidth,
                                        this.computedLeftWidth
                                );
                                //增加弹性
                                if(this.computedRightWidth && this.offset === -this.computedRightWidth || this.computedLeftWidth && this.offset === this.computedLeftWidth){
                                        //
                                        this.preventDefault(event, this.stopPropagation);
                                        //弹性系数
                                        this.elasticX = (this.deltaX + this.startOffset - this.offset)/4;
                                }
                        }else{
                                //上下滑动后取消close
                                this.dragging = true;
                                this.lockClick = true;
                        }
                },

                onTouchEnd() {
                        if (this.disabled) {
                                return;
                        }
                        //回弹
                        this.elasticX = 0
                        if (this.dragging) {
                                this.toggle(this.offset > 0 ? 'left' : 'right');
                                this.dragging = false;
                                // compatible with desktop scenario
                                setTimeout(() => {
                                        this.lockClick = false;
                                }, 0);
                        }
                },

                toggle(direction) {
                        const offset = Math.abs(this.offset);
                        const THRESHOLD = 0.15;
                        const threshold = this.opened ? 1 - THRESHOLD : THRESHOLD;
                        const { computedLeftWidth, computedRightWidth } = this;

                        if (
                        computedRightWidth &&
                        direction === 'right' &&
                        offset > computedRightWidth * threshold
                        ) {
                                this.open('right');
                        } else if (
                        computedLeftWidth &&
                        direction === 'left' &&
                        offset > computedLeftWidth * threshold
                        ) {
                                this.open('left');
                        } else {
                                this.close();
                        }
                },

                onClick(position = 'outside') {
                        this.$emit('click', position);

                        if (this.opened && !this.lockClick) {
                                if (this.beforeClose) {
                                        this.beforeClose({
                                                position,
                                                name: this.name,
                                                instance: this,
                                        });
                                } else if (this.onClose) {
                                        this.onClose(position, this, { name: this.name });
                                } else {
                                        this.close(position);
                                }
                        }
                },

                getClickHandler(position, stop) {
                        return (event) => {
                                if (stop) {
                                        event.stopPropagation();
                                }
                                this.onClick(position);
                        };
                },
        }
}
</script>
<style lang="stylus" scoped>
.cell_container{
        position: relative;
        overflow: hidden;
        line-height: 68px;
        height:68px;
        div{
                height: 100%;
                .cell_content{
                        height: 100%;
                        width: 100%;
                        text-align: center;
                }
                .cell_content_active{
                        height: 100%;
                        width: 100%;
                        text-align: center;
                        &:active{
                                background: #e8e8e8;
                        }
                }
                .cell_left,.cell_right{
                        position: absolute;
                        top: 0;
                        height: 100%;
                        display: flex;
                        color: #fff;
                        .divPostion{
                                position: absolute;
                        }
                        div{
                                white-space:nowrap;
                                display: flex;
                                align-items: center;
                                background: #ccc;
                        }
                }
                .cell_left{
                        left: 0;
                        transform:translateX(-100%);
                }
                .cell_right{
                        right: 0;
                        transform:translateX(100%);
                }
        }
}
</style>
touch.js

import Vue from 'vue';
export const isServer=false;
const MIN_DISTANCE = 10;
const TouchMixinData = {
startX: Number,
startY: Number,
deltaX: Number,
deltaY: Number,
offsetX: Number,
offsetY: Number,
direction: String
};

function getDirection(x,y) {
if (x > y && x > MIN_DISTANCE) {
return 'horizontal';
}

if (y > x && y > MIN_DISTANCE) {
return 'vertical';
}

return '';
}

export let supportsPassive = false;

export function on(
target,
event,
handler,
passive = false
) {
if (!isServer) {
target.addEventListener(
  event,
  handler,
  supportsPassive ? { capture: false, passive } : false
);
}
}

export const TouchMixin = Vue.extend({
data() {TouchMixinData
return { direction: '' } ;
},

methods: {
touchStart() {
  this.resetTouchStatus();
  this.startX = event.touches[0].clientX;
  this.startY = event.touches[0].clientY;
},

touchMove() {
  const touch = event.touches[0];
  this.deltaX = touch.clientX - this.startX;
  this.deltaY = touch.clientY - this.startY;
  this.offsetX = Math.abs(this.deltaX);
  this.offsetY = Math.abs(this.deltaY);
  this.direction =
  this.direction || getDirection(this.offsetX, this.offsetY);
},

resetTouchStatus() {
  this.direction = '';
  this.deltaX = 0;
  this.deltaY = 0;
  this.offsetX = 0;
  this.offsetY = 0;
},

// avoid Vue 2.6 event bubble issues by manually binding events
// https://github.com/youzan/vant/issues/3015
bindTouchEvent( el ) {
  const { onTouchStart, onTouchMove, onTouchEnd } = this;

  on(el, 'touchstart', onTouchStart);
  on(el, 'touchmove', onTouchMove);

  if (onTouchEnd) {
  on(el, 'touchend', onTouchEnd);
  on(el, 'touchcancel', onTouchEnd);
  }
},
},
});引入即可!!!

到此这篇关于vue swipeCell滑动单元格(仿微信)的实现示例的文章就介绍到这了,更多相关vue swipeCell滑动单元格内容请搜索目录社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持目录社区!

买目录提供泛目录、二级目录、租目录、出租网站建设资源、编程学习类,提供asp、php、asp.net、javascript、jquery、vbscript、dos批处理、网页制作、网络编程、网站建设等编程资料。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则