transition.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. var endEvent = sniffTransitionEndEvent(),
  2. config = require('./config'),
  3. // exit codes for testing
  4. codes = {
  5. CSS_E : 1,
  6. CSS_L : 2,
  7. JS_E : 3,
  8. JS_L : 4,
  9. CSS_SKIP : -1,
  10. JS_SKIP : -2,
  11. JS_SKIP_E : -3,
  12. JS_SKIP_L : -4,
  13. INIT : -5,
  14. SKIP : -6
  15. }
  16. /**
  17. * stage:
  18. * 1 = enter
  19. * 2 = leave
  20. */
  21. var transition = module.exports = function (el, stage, cb, compiler) {
  22. var changeState = function () {
  23. cb()
  24. compiler.execHook(stage > 0 ? 'enteredView' : 'leftView')
  25. }
  26. if (compiler.init) {
  27. changeState()
  28. return codes.INIT
  29. }
  30. var transitionId = el.vue_trans
  31. if (transitionId) {
  32. return applyTransitionFunctions(
  33. el,
  34. stage,
  35. changeState,
  36. transitionId,
  37. compiler
  38. )
  39. } else if (transitionId === '') {
  40. return applyTransitionClass(
  41. el,
  42. stage,
  43. changeState
  44. )
  45. } else {
  46. changeState()
  47. return codes.SKIP
  48. }
  49. }
  50. transition.codes = codes
  51. /**
  52. * Togggle a CSS class to trigger transition
  53. */
  54. function applyTransitionClass (el, stage, changeState) {
  55. if (!endEvent) {
  56. changeState()
  57. return codes.CSS_SKIP
  58. }
  59. var classList = el.classList,
  60. lastLeaveCallback = el.vue_trans_cb
  61. if (stage > 0) { // enter
  62. // cancel unfinished leave transition
  63. if (lastLeaveCallback) {
  64. el.removeEventListener(endEvent, lastLeaveCallback)
  65. el.vue_trans_cb = null
  66. }
  67. // set to hidden state before appending
  68. classList.add(config.enterClass)
  69. // append
  70. changeState()
  71. // force a layout so transition can be triggered
  72. /* jshint unused: false */
  73. var forceLayout = el.clientHeight
  74. // trigger transition
  75. classList.remove(config.enterClass)
  76. return codes.CSS_E
  77. } else { // leave
  78. // trigger hide transition
  79. classList.add(config.leaveClass)
  80. var onEnd = function (e) {
  81. if (e.target === el) {
  82. el.removeEventListener(endEvent, onEnd)
  83. el.vue_trans_cb = null
  84. // actually remove node here
  85. changeState()
  86. classList.remove(config.leaveClass)
  87. }
  88. }
  89. // attach transition end listener
  90. el.addEventListener(endEvent, onEnd)
  91. el.vue_trans_cb = onEnd
  92. return codes.CSS_L
  93. }
  94. }
  95. function applyTransitionFunctions (el, stage, changeState, functionId, compiler) {
  96. var funcs = compiler.getOption('transitions', functionId)
  97. if (!funcs) {
  98. changeState()
  99. return codes.JS_SKIP
  100. }
  101. var enter = funcs.enter,
  102. leave = funcs.leave
  103. if (stage > 0) { // enter
  104. if (typeof enter !== 'function') {
  105. changeState()
  106. return codes.JS_SKIP_E
  107. }
  108. enter(el, changeState)
  109. return codes.JS_E
  110. } else { // leave
  111. if (typeof leave !== 'function') {
  112. changeState()
  113. return codes.JS_SKIP_L
  114. }
  115. leave(el, changeState)
  116. return codes.JS_L
  117. }
  118. }
  119. /**
  120. * Sniff proper transition end event name
  121. */
  122. function sniffTransitionEndEvent () {
  123. var el = document.createElement('vue'),
  124. defaultEvent = 'transitionend',
  125. events = {
  126. 'transition' : defaultEvent,
  127. 'mozTransition' : defaultEvent,
  128. 'webkitTransition' : 'webkitTransitionEnd'
  129. }
  130. for (var name in events) {
  131. if (el.style[name] !== undefined) {
  132. return events[name]
  133. }
  134. }
  135. }