Vue Touch HammerJs Example and Demo are developed using CSS, HTML and HammerJs.

Demo Download

Author Lisi
Created AUGUST 10, 2018
License Open
Compatible browsers Chrome, Firefox, Safari

HTML Snippet

<main class="" id="app" v-pan="onPan"> 	<div class="emoji" ref="emoji">{{ selectedContent }}</div> 	<section class="slider"> 		<ul class="slider__list" ref="list"> 			<li v-for="(animal, index) in animals" :key="animal" 					class="slider__item"  					v-tap="(e) => onTap(e, animal)" 					:style="{backgroundColor: colors[index]}"> 				{{ animal }} 			</li> 		</ul> 	</section> </main>

SCSS Code

.slider { 	width: 100%; 	height: 120px; 	overflow: visible;   position: relative;   white-space: nowrap;  	&__list { 		display: flex; 		width: 100%; 		height: 100%; 		 		font-size: 2rem; 		backface-visibility: hidden; 		transform: translateX(calc(var(--x, 0) * 1%)); 	} 	 	&__item { 		position: relative; 		flex: 0 0 140px; 		 		display: flex; 		justify-content: center; 		align-items: center; 		height: 100%; 		margin-right: 12px; 		padding: 6px; 		box-sizing: border-box; 		 		border-radius: 8px; 		text-align: center;   	transition: opacity 0.15s ease; 		color: #fff;  		&:focus { 			opacity: 0.8; 		} 	} }  .emoji  { 	padding: 40px; 	font-size: 6rem; 	min-height: 6rem; 	backface-visibility: hidden; }   /* layout */ html { 	height: 100%; 	display: flex; 	background: #155e63; }  body {	 	position: relative;  	width: 100%; 	height: 100%; 	max-width:  360px; 	max-height: 640px; 	margin: auto;  	background: #efefef; 	font-family:'Do Hyeon', sans-serif; 	font-size: 16px; }  #app { 	height: 100%; 	width: 100%; 	display: flex; 	flex-direction: column; 	justify-content: center; 	align-items: center; 	padding: 20px; 	box-sizing: border-box; 	 	overflow: hidden; }

JavaScript Snippet

Vue.directive("pan", { 	bind: function(el, binding) { 		if (typeof binding.value === "function") { 			const mc = new Hammer(el); 			mc.get("pan").set({ direction: Hammer.DIRECTION_ALL }); 			mc.on("pan", binding.value); 		} 	} });  Vue.directive("tap", { 	bind: function(el, binding) { 		if (typeof binding.value === "function") { 			const mc = new Hammer(el); 			mc.on("tap", binding.value); 		} 	} });  const app = new Vue({ 	el: "#app", 	data: { 		animals: [ 			"cat", 			"dog", 			"panda", 			"lion", 			"frog", 			"bear", 			"mouse", 			"tiger", 			"monkey" 		], 		emojis: ["ðߐ±", "ðߐ¶", "ðߐ¼", "ð�¦", "🐸", "🐻", "🐹", "🐯", "🐵"], 		colors: [ 			"#F7CC45", 			"#AC6909", 			"#272625", 			"#FFAD01", 			"#81DC58", 			"#C68E71", 			"#F2B2BD", 			"#FFCB00", 			"#BE9763" 		], 		currentOffset: 0, 		selected: "cat" 	}, 	computed: { 		overflowRatio() { 			return this.$refs.list.scrollWidth / this.$refs.list.offsetWidth; 		}, 		itemWidth() { 			return this.$refs.list.scrollWidth / this.animals.length; 		}, 		selectedContent() { 			if (this.selected) { 				return this.emojis[this.animals.indexOf(this.selected)]; 			} 			return ""; 		}, 		count() { 			return this.animals.length 		} 	}, 	watch: { 		selected(newValue) { 			TweenMax.fromTo( 				this.$refs.emoji, 				0.6, 				{ scale: 0 }, 				{ scale: 1, ease: Elastic.easeOut.config(1, 0.8) } 			); 		} 	}, 	methods: { 		onPan(e) { 			const dragOffset = 100 / this.itemWidth * e.deltaX / this.count * this.overflowRatio;  			const transform = this.currentOffset + dragOffset;  			this.$refs.list.style.setProperty("--x", transform);  			if (e.isFinal) { 				this.currentOffset = transform; 				const maxScroll = 100 - this.overflowRatio * 100; 				let finalOffset = this.currentOffset;  				// scrolled to last item 				if (this.currentOffset <= maxScroll) { 					finalOffset = maxScroll; 				} else if (this.currentOffset >= 0) { 					// scroll to first item 					finalOffset = 0; 				} else { 					// animate to next item according to pan direction 					const index = this.currentOffset / this.overflowRatio / 100 * this.count; 					const nextIndex = e.deltaX <= 0 ? Math.floor(index) : Math.ceil(index); 					finalOffset = 100 * this.overflowRatio / this.count * nextIndex; 				}  				// bounce back animation 				TweenMax.fromTo( 					this.$refs.list, 					0.4, 					{ '--x': this.currentOffset }, 					{ 						'--x': finalOffset, 						ease: Elastic.easeOut.config(1, 0.8), 						onComplete: () => { 							this.currentOffset = finalOffset; 						} 					} 				); 			} 		}, 		onTap(e, value) { 			if (value) { 				TweenMax.to(e.target, 0.12, { scale: 1.1, yoyo: true, repeat: 1, ease: Sine.easeOut}) 				this.selected = value; 			} 		} 	} });

Preview

Vue Touch HammerJs Example and Demo preview