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

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

带有拉动弹性回弹效果

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滑动单元格内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

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

郑重声明:本网站发布的内容(图片、视频和文字)以及用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服