Skip to content

Instantly share code, notes, and snippets.

@sseltzer
Created February 16, 2017 03:39
Show Gist options
  • Save sseltzer/25c5dc1c923511bd39555ba684bd956d to your computer and use it in GitHub Desktop.
Save sseltzer/25c5dc1c923511bd39555ba684bd956d to your computer and use it in GitHub Desktop.
Vue CSS Anim Test
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="slider" class="slider">
<div v-for="(item, index) in items" class="item" v-bind:style="item" v-bind:index="index"/>
</div>
let slider = new Vue({
el: '#slider',
data: {
items: [
{background: '#1A237E'},
{background: '#283593'},
{background: '#303F9F'},
{background: '#3949AB'},
{background: '#3F51B5'},
{background: '#5C6BC0'},
{background: '#7986CB'}
]
},
methods: {
setElementIndex: function() {
this.itemElements = document.querySelectorAll('.item');
},
update: function() {
console.log('update')
if (this.animate) requestAnimationFrame(this.update);
if (!this.target) return;
if (this.dragging) this.updateItem(this.currentX - this.startX);
else if (this.remove) this.slideAway();
else this.resetItem();
},
updateItem: function(translationX) {
this.target.style.transform = `translateX(${translationX}px)`;
this.target.style.opacity = 1 - Math.abs(this.originX - this.target.getBoundingClientRect().left) / this.target.getBoundingClientRect().width;
},
onDown: function(event) {
this.$el.style.willChange = 'transform';
this.originX = this.$el.getBoundingClientRect().left;
this.startX = event.pageX || event.touches[0].pageX;
this.dragging = true;
this.itemElements.forEach((item, i) => { if (item === event.target) this.index = i; });
this.target = this.itemElements[this.index];
if (this.target) {
this.animate = true;
requestAnimationFrame(this.update);
}
},
onMove: function(event) {
this.currentX = event.pageX || event.touches[0].pageX || 0;
},
onUp: function(event) {
if (this.itemElements.length < 1 || !this.target) return;
this.dragging = false;
this.releaseX = this.target.getBoundingClientRect().left;
this.slideOffset = this.target.getBoundingClientRect().left - this.originX;
this.remove = (Math.abs(this.currentX - this.startX) > (this.$el.getBoundingClientRect().width * .55));
},
resetItem: function() {
let easing = (this.originX - this.target.getBoundingClientRect().left) * .8;
let translationX = this.target.getBoundingClientRect().left + (this.currentX - this.startX < 0) ? -easing : easing;
// Snap the easing algorithm & check for corner case 0 to ensure no mem leaks.
if (Math.abs(this.originX - this.target.getBoundingClientRect().left) < 2 || translationX == 0 || easing == 0) {
translationX = 0;
this.updateItem(translationX);
this.target.style.willChange = 'initial';
this.target.style.transform = 'none';
this.animate = false;
this.index = -1;
this.target = null;
return;
}
this.updateItem(translationX);
},
slideAway: function() {
let direction = (this.releaseX < this.originX) ? -1 : 1;
let easing = (this.originX + direction * this.target.getBoundingClientRect().width - this.releaseX) / 10;
this.slideOffset += easing;
this.updateItem(this.slideOffset);
if (this.target.getBoundingClientRect().left < this.originX - this.target.getBoundingClientRect().width ||
this.target.getBoundingClientRect().left > this.originX + this.target.getBoundingClientRect().width)
this.deleteItem();
},
deleteItem: function() {
let onAnimationComplete = (event) => {
event.target.removeEventListener( 'transitionend', onAnimationComplete );
event.target.style.transition = '';
event.target.style.transform = '';
this.animate = false;
};
for (let i = this.index + 1; i < this.itemElements.length; ++i) {
this.itemElements[i].style.transform = `translateY( ${ this.itemElements[i - 1].getBoundingClientRect().height }px )`;
this.itemElements[i].addEventListener( 'transitionend', onAnimationComplete );
}
this.target.parentNode.removeChild(this.target);
requestAnimationFrame( () => {
for (let i = this.index + 1; i < this.itemElements.length; ++i) {
this.itemElements[i].style.transition = `transform 150ms cubic-bezier( 0,0,0.31,1 ) ${ i * 50 }ms`;
this.itemElements[i].style.transform = '';
}
});
this.setElementIndex();
this.index = -1;
this.target = null;
}
},
mounted: function() {
this.setElementIndex();
document.addEventListener( 'mousedown', this.onDown );
document.addEventListener( 'mousemove', this.onMove );
document.addEventListener( 'mouseup', this.onUp );
document.addEventListener( 'touchstart', this.onDown );
document.addEventListener( 'touchmove', this.onMove );
document.addEventListener( 'touchend', this.onUp );
}
});
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
background: #fafafa;
font-family: arial;
font-size: 30px;
}
.slider {
width: 100%;
max-width: 450px;
margin: 0 auto;
}
.item {
height: 100px;
cursor: pointer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment