transition.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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 ? 'attached' : 'detached')
  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. // if the browser supports transition,
  60. // it must have classList...
  61. var classList = el.classList,
  62. lastLeaveCallback = el.vue_trans_cb
  63. if (stage > 0) { // enter
  64. // cancel unfinished leave transition
  65. if (lastLeaveCallback) {
  66. el.removeEventListener(endEvent, lastLeaveCallback)
  67. classList.remove(config.leaveClass)
  68. el.vue_trans_cb = null
  69. }
  70. // set to hidden state before appending
  71. classList.add(config.enterClass)
  72. // append
  73. changeState()
  74. // force a layout so transition can be triggered
  75. /* jshint unused: false */
  76. var forceLayout = el.clientHeight
  77. // trigger transition
  78. classList.remove(config.enterClass)
  79. return codes.CSS_E
  80. } else { // leave
  81. if (el.offsetWidth || el.offsetHeight) {
  82. // trigger hide transition
  83. classList.add(config.leaveClass)
  84. var onEnd = function (e) {
  85. if (e.target === el) {
  86. el.removeEventListener(endEvent, onEnd)
  87. el.vue_trans_cb = null
  88. // actually remove node here
  89. changeState()
  90. classList.remove(config.leaveClass)
  91. }
  92. }
  93. // attach transition end listener
  94. el.addEventListener(endEvent, onEnd)
  95. el.vue_trans_cb = onEnd
  96. } else {
  97. // directly remove invisible elements
  98. changeState()
  99. }
  100. return codes.CSS_L
  101. }
  102. }
  103. function applyTransitionFunctions (el, stage, changeState, functionId, compiler) {
  104. var funcs = compiler.getOption('transitions', functionId)
  105. if (!funcs) {
  106. changeState()
  107. return codes.JS_SKIP
  108. }
  109. var enter = funcs.enter,
  110. leave = funcs.leave
  111. if (stage > 0) { // enter
  112. if (typeof enter !== 'function') {
  113. changeState()
  114. return codes.JS_SKIP_E
  115. }
  116. enter(el, changeState)
  117. return codes.JS_E
  118. } else { // leave
  119. if (typeof leave !== 'function') {
  120. changeState()
  121. return codes.JS_SKIP_L
  122. }
  123. leave(el, changeState)
  124. return codes.JS_L
  125. }
  126. }
  127. /**
  128. * Sniff proper transition end event name
  129. */
  130. function sniffTransitionEndEvent () {
  131. var el = document.createElement('vue'),
  132. defaultEvent = 'transitionend',
  133. events = {
  134. 'transition' : defaultEvent,
  135. 'mozTransition' : defaultEvent,
  136. 'webkitTransition' : 'webkitTransitionEnd'
  137. }
  138. for (var name in events) {
  139. if (el.style[name] !== undefined) {
  140. return events[name]
  141. }
  142. }
  143. }