您的位置:首页技术文章
文章详情页

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

【字号: 日期:2022-06-11 15:46:43浏览:57作者:猪猪

抽离Vant weapp滑动单元格代码改造而成

带有拉动弹性回弹效果

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

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

<template><divclass='cell_container'@touchstartv-click-outside='handleClickOutside'@click='getClickHandler(’cell’)'><div:style='{’transform’:’translateX(’+(offset+(isElastic?elasticX:0))+’px)’,’transition-duration’:dragging?’0s’:’0.6s’}'><!-- <div ref='cellLeft' @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 : 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 : :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 insteadonClose: 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-apiopen(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 versiondetail: this.name,});},// @exposed-apiclose(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{//上下滑动后取消closethis.dragging = true;this.lockClick = true;}},onTouchEnd() {if (this.disabled) {return;}//回弹this.elasticX = 0if (this.dragging) {this.toggle(this.offset > 0 ? ’left’ : ’right’);this.dragging = false;// compatible with desktop scenariosetTimeout(() => {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滑动单元格内容请搜索好吧啦网以前的文章或继续浏览下面的相关文章希望大家以后多多支持好吧啦网!

标签: 微信
相关文章: