Basic swap animation plugin

This commit is contained in:
Max Hoffmann 2017-10-13 10:27:38 -04:00
parent 55dd819083
commit de7a441e47
No known key found for this signature in database
GPG Key ID: 1DFA4D13DD27A676
4 changed files with 127 additions and 0 deletions

View File

@ -0,0 +1,50 @@
## SwapAnimation
The swap animation plugin currently only works with `Sortable`. It adds a swap animation on `sortable:sorted`,
and animates both `source` and `over` via `translate3d`. It is currently possible to change the duration and
the easing function of the animation.
This plugin is not included by default, so make sure to import it before using.
### Import
```js
import {SwapAnimation} from '@shopify/draggable/plugins/swap-animation';
```
```html
<script src="https://cdn.jsdelivr.net/npm/@shopify/draggable@1.0.0-beta.3/lib/draggable/plugins/swap-animation"></script>
```
### API
**`new SwapAnimation(draggable: Draggable): SwapAnimation`**
To create a swap animation plugin instance.
### Options
**`duration {Integer}`**
The duration option allows you to specify the animation during for a single swap. Default: `150`
**`easingFunction {String}`**
The easing option allows you to specify an animation easing function. Default: `'ease-in-out'`
### Examples
```js
import {Sortable, Plugins} from '@shopify/draggable';
const sortable = new Sortable(document.querySelectorAll('ul'), {
draggable: 'li',
swapAnimation: {
duration: 200,
easingFunction: 'ease-in-out',
},
plugins: [Plugins.SwapAnimation],
});
```
### Known issues
- Only works with vertical lists
- Animations are not staggering yet

View File

@ -0,0 +1,69 @@
export const defaultOptions = {
duration: 150,
easingFunction: 'ease-in-out',
};
export default class SwapAnimation {
constructor(draggable) {
this.draggable = draggable;
this.options = {
...defaultOptions,
...this.getOptions(),
};
this.onSortableSorted = this.onSortableSorted.bind(this);
}
attach() {
this.draggable.on('sortable:sorted', this.onSortableSorted);
}
detach() {
this.draggable.off('sortable:sorted', this.onSortableSorted);
}
onSortableSorted({oldIndex, newIndex, dragEvent}) {
const {source, over} = dragEvent;
cancelAnimationFrame(this.lastAnimationFrame);
// Can be done in a separate frame
this.lastAnimationFrame = requestAnimationFrame(() => {
if (oldIndex >= newIndex) {
animate(source, over, this.options);
} else {
animate(over, source, this.options);
}
});
}
getOptions() {
return this.draggable.options.swapAnimation || {};
}
}
function animate(top, bottom, {duration, easingFunction}) {
const height = top.offsetHeight;
for (const element of [top, bottom]) {
element.style.pointerEvents = 'none';
}
top.style.transform = `translate3d(0, ${height}px, 0)`;
bottom.style.transform = `translate3d(0, -${height}px, 0)`;
requestAnimationFrame(() => {
for (const element of [top, bottom]) {
element.addEventListener('transitionend', resetElementOnTransitionEnd);
element.style.transition = `transform ${duration}ms ${easingFunction}`;
element.style.transform = '';
}
});
}
function resetElementOnTransitionEnd(event) {
event.target.style.transition = '';
event.target.style.pointerEvents = '';
event.target.removeEventListener('transitionend', resetElementOnTransitionEnd);
}

View File

@ -0,0 +1,6 @@
import SwapAnimation, {defaultOptions} from './SwapAnimation';
export default SwapAnimation;
export {
defaultOptions as defaultSwapAnimationOptions,
};

View File

@ -1,7 +1,9 @@
import Collidable from './Collidable';
import Snappable from './Snappable';
import SwapAnimation from './SwapAnimation';
export {
Collidable,
Snappable,
SwapAnimation,
};