| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- var endEvents = sniffEndEvents(),
- config = require('./config'),
- // batch enter animations so we only force the layout once
- Batcher = require('./batcher'),
- batcher = new Batcher(),
- // cache timer functions
- setTO = window.setTimeout,
- clearTO = window.clearTimeout,
- // exit codes for testing
- codes = {
- CSS_E : 1,
- CSS_L : 2,
- JS_E : 3,
- JS_L : 4,
- CSS_SKIP : -1,
- JS_SKIP : -2,
- JS_SKIP_E : -3,
- JS_SKIP_L : -4,
- INIT : -5,
- SKIP : -6
- }
- // force layout before triggering transitions/animations
- batcher._preFlush = function () {
- /* jshint unused: false */
- var f = document.body.offsetHeight
- }
- /**
- * stage:
- * 1 = enter
- * 2 = leave
- */
- var transition = module.exports = function (el, stage, cb, compiler) {
- var changeState = function () {
- cb()
- compiler.execHook(stage > 0 ? 'attached' : 'detached')
- }
- if (compiler.init) {
- changeState()
- return codes.INIT
- }
- var hasTransition = el.vue_trans === '',
- hasAnimation = el.vue_anim === '',
- effectId = el.vue_effect
- if (effectId) {
- return applyTransitionFunctions(
- el,
- stage,
- changeState,
- effectId,
- compiler
- )
- } else if (hasTransition || hasAnimation) {
- return applyTransitionClass(
- el,
- stage,
- changeState,
- hasAnimation
- )
- } else {
- changeState()
- return codes.SKIP
- }
- }
- transition.codes = codes
- /**
- * Togggle a CSS class to trigger transition
- */
- function applyTransitionClass (el, stage, changeState, hasAnimation) {
- if (!endEvents.trans) {
- changeState()
- return codes.CSS_SKIP
- }
- // if the browser supports transition,
- // it must have classList...
- var onEnd,
- classList = el.classList,
- existingCallback = el.vue_trans_cb,
- enterClass = config.enterClass,
- leaveClass = config.leaveClass,
- endEvent = hasAnimation ? endEvents.anim : endEvents.trans
- // cancel unfinished callbacks and jobs
- if (existingCallback) {
- el.removeEventListener(endEvent, existingCallback)
- classList.remove(enterClass)
- classList.remove(leaveClass)
- el.vue_trans_cb = null
- }
- if (stage > 0) { // enter
- // set to enter state before appending
- classList.add(enterClass)
- // append
- changeState()
- // trigger transition
- if (!hasAnimation) {
- batcher.push({
- execute: function () {
- classList.remove(enterClass)
- }
- })
- } else {
- onEnd = function (e) {
- if (e.target === el) {
- el.removeEventListener(endEvent, onEnd)
- el.vue_trans_cb = null
- classList.remove(enterClass)
- }
- }
- el.addEventListener(endEvent, onEnd)
- el.vue_trans_cb = onEnd
- }
- return codes.CSS_E
- } else { // leave
- if (el.offsetWidth || el.offsetHeight) {
- // trigger hide transition
- classList.add(leaveClass)
- onEnd = function (e) {
- if (e.target === el) {
- el.removeEventListener(endEvent, onEnd)
- el.vue_trans_cb = null
- // actually remove node here
- changeState()
- classList.remove(leaveClass)
- }
- }
- // attach transition end listener
- el.addEventListener(endEvent, onEnd)
- el.vue_trans_cb = onEnd
- } else {
- // directly remove invisible elements
- changeState()
- }
- return codes.CSS_L
-
- }
- }
- function applyTransitionFunctions (el, stage, changeState, effectId, compiler) {
- var funcs = compiler.getOption('effects', effectId)
- if (!funcs) {
- changeState()
- return codes.JS_SKIP
- }
- var enter = funcs.enter,
- leave = funcs.leave,
- timeouts = el.vue_timeouts
- // clear previous timeouts
- if (timeouts) {
- var i = timeouts.length
- while (i--) {
- clearTO(timeouts[i])
- }
- }
- timeouts = el.vue_timeouts = []
- function timeout (cb, delay) {
- var id = setTO(function () {
- cb()
- timeouts.splice(timeouts.indexOf(id), 1)
- if (!timeouts.length) {
- el.vue_timeouts = null
- }
- }, delay)
- timeouts.push(id)
- }
- if (stage > 0) { // enter
- if (typeof enter !== 'function') {
- changeState()
- return codes.JS_SKIP_E
- }
- enter(el, changeState, timeout)
- return codes.JS_E
- } else { // leave
- if (typeof leave !== 'function') {
- changeState()
- return codes.JS_SKIP_L
- }
- leave(el, changeState, timeout)
- return codes.JS_L
- }
- }
- /**
- * Sniff proper transition end event name
- */
- function sniffEndEvents () {
- var el = document.createElement('vue'),
- defaultEvent = 'transitionend',
- events = {
- 'webkitTransition' : 'webkitTransitionEnd',
- 'transition' : defaultEvent,
- 'mozTransition' : defaultEvent
- },
- ret = {}
- for (var name in events) {
- if (el.style[name] !== undefined) {
- ret.trans = events[name]
- break
- }
- }
- ret.anim = el.style.animation === ''
- ? 'animationend'
- : 'webkitAnimationEnd'
- return ret
- }
|