JavaScript

当前位置:首页 > JavaScript

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

抽离Vant weapp滑动单元格代码改造而成带有拉动弹性回弹效果demo展示:https://littaotao.github.io/me/index(切换为浏览器调试的手机模式并且再次刷新一次)<template> <di...

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

相关内容

文章评论

表情

共 0 条评论,查看全部
  • 这篇文章还没有收到评论,赶紧来抢沙发吧~

评论排行榜

热门标签