|
| 1 | +(function (global, factory) { |
| 2 | + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : |
| 3 | + typeof define === 'function' && define.amd ? define(factory) : |
| 4 | + (global.VueAnimatedList = factory()); |
| 5 | +}(this, function () { 'use strict'; |
| 6 | + function install (Vue) { |
| 7 | + var _ = Vue.util |
| 8 | + var transitionEndEvent = _.transitionEndEvent |
| 9 | + var addClass = _.addClass |
| 10 | + var removeClass = _.removeClass |
| 11 | + var on = _.on |
| 12 | + var off = _.off |
| 13 | + |
| 14 | + // patch v-for |
| 15 | + var vFor = Vue.directive('for') |
| 16 | + var diff = vFor.diff |
| 17 | + vFor.diff = function () { |
| 18 | + var needMoveTransition = prepareMoveTransition(this.frags) |
| 19 | + diff.apply(this, arguments) |
| 20 | + if (needMoveTransition) { |
| 21 | + applyMoveTransition(this.frags) |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + /** |
| 26 | + * Check if move transitions are needed, and if so, |
| 27 | + * record the bounding client rects for each item. |
| 28 | + * |
| 29 | + * @param {Array<Fragment>|undefined} frags |
| 30 | + * @return {Boolean|undefined} |
| 31 | + */ |
| 32 | + |
| 33 | + function prepareMoveTransition (frags) { |
| 34 | + var transition = |
| 35 | + transitionEndEvent && // css transition supported? |
| 36 | + frags && frags.length && // has frags to be moved? |
| 37 | + frags[0].node.__v_trans // has transitions? |
| 38 | + if (transition) { |
| 39 | + var node = frags[0].node |
| 40 | + var moveClass = transition.id + '-move' |
| 41 | + var moving = node._pendingMoveCb |
| 42 | + var type |
| 43 | + if (!moving) { |
| 44 | + // sniff whether element has a transition duration for transform |
| 45 | + // with the move class applied |
| 46 | + addClass(node, moveClass) |
| 47 | + type = transition.getCssTransitionType(moveClass, true) |
| 48 | + removeClass(node, moveClass) |
| 49 | + } |
| 50 | + if (moving || type === 'transition') { |
| 51 | + frags.forEach(frag => { |
| 52 | + frag._oldPos = frag.node.getBoundingClientRect() |
| 53 | + }) |
| 54 | + return true |
| 55 | + } |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + /** |
| 60 | + * Apply move transitions. |
| 61 | + * Calculate new target positions after the move, then apply the |
| 62 | + * FLIP technique to trigger CSS transforms. |
| 63 | + * |
| 64 | + * @param {Array<Fragment>} frags |
| 65 | + */ |
| 66 | + |
| 67 | + function applyMoveTransition (frags) { |
| 68 | + frags.forEach(function (frag) { |
| 69 | + var node = frag.node |
| 70 | + var oldPos = frag._oldPos |
| 71 | + if (!oldPos) return |
| 72 | + if (!frag.moved) { |
| 73 | + // transition busting to ensure correct bounding rect: |
| 74 | + // if an element has an ongoing transition and not "reinserted", |
| 75 | + // the bounding rect will not be calculated at its target position, |
| 76 | + // but rather an in-transition position. |
| 77 | + var p = node.parentNode |
| 78 | + var next = node.nextSibling |
| 79 | + p.removeChild(node) |
| 80 | + p.insertBefore(node, next) |
| 81 | + } |
| 82 | + var newPos = node.getBoundingClientRect() |
| 83 | + var dx = oldPos.left - newPos.left |
| 84 | + var dy = oldPos.top - newPos.top |
| 85 | + if (dx !== 0 || dy !== 0) { |
| 86 | + frag.moved = true |
| 87 | + node.style.transform = `translate(${dx}px, ${dy}px)` |
| 88 | + node.style.transitionDuration = '0s' |
| 89 | + } else { |
| 90 | + frag.moved = false |
| 91 | + } |
| 92 | + }) |
| 93 | + Vue.nextTick(function () { |
| 94 | + var f = document.documentElement.offsetHeight |
| 95 | + frags.forEach(function (frag) { |
| 96 | + var node = frag.node |
| 97 | + var moveClass = node.__v_trans.id + '-move' |
| 98 | + if (frag.moved) { |
| 99 | + addClass(node, moveClass) |
| 100 | + node.style.transform = '' |
| 101 | + node.style.transitionDuration = '' |
| 102 | + if (node._pendingMoveCb) { |
| 103 | + off(node, transitionEndEvent, node._pendingMoveCb) |
| 104 | + } |
| 105 | + node._pendingMoveCb = function cb () { |
| 106 | + off(node, transitionEndEvent, cb) |
| 107 | + node._pendingMoveCb = null |
| 108 | + removeClass(node, moveClass) |
| 109 | + } |
| 110 | + on(node, transitionEndEvent, node._pendingMoveCb) |
| 111 | + } |
| 112 | + }) |
| 113 | + }) |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + if (typeof Vue !== 'undefined') { |
| 118 | + Vue.use(install) |
| 119 | + } |
| 120 | + return install |
| 121 | +})); |
0 commit comments