Explorar o código

proper transition start detection (fix #2452)

Evan You %!s(int64=10) %!d(string=hai) anos
pai
achega
ddbe53ba3c
Modificáronse 2 ficheiros con 28 adicións e 14 borrados
  1. 27 13
      src/transition/transition.js
  2. 1 1
      test/unit/specs/transition/transition_spec.js

+ 27 - 13
src/transition/transition.js

@@ -10,7 +10,8 @@ import {
   animationEndEvent,
   animationEndEvent,
   transitionProp,
   transitionProp,
   animationProp,
   animationProp,
-  warn
+  warn,
+  inBrowser
 } from '../util/index'
 } from '../util/index'
 
 
 const TYPE_TRANSITION = 'transition'
 const TYPE_TRANSITION = 'transition'
@@ -18,6 +19,27 @@ const TYPE_ANIMATION = 'animation'
 const transDurationProp = transitionProp + 'Duration'
 const transDurationProp = transitionProp + 'Duration'
 const animDurationProp = animationProp + 'Duration'
 const animDurationProp = animationProp + 'Duration'
 
 
+/**
+ * If a just-entered element is applied the
+ * leave class while its enter transition hasn't started yet,
+ * and the transitioned property has the same value for both
+ * enter/leave, then the leave transition will be skipped and
+ * the transitionend event never fires. This function ensures
+ * its callback to be called after a transition has started
+ * by waiting for double raf.
+ *
+ * It falls back to setTimeout on devices that support CSS
+ * transitions but not raf (e.g. Android 4.2 browser) - since
+ * these environments are usually slow, we are giving it a
+ * relatively large timeout.
+ */
+
+const raf = inBrowser && window.requestAnimationFrame
+const waitForTransitionStart = raf
+  /* istanbul ignore next */
+  ? function (fn) { raf(function () { raf(fn) }) }
+  : function (fn) { setTimeout(fn, 50) }
+
 /**
 /**
  * A Transition object that encapsulates the state and logic
  * A Transition object that encapsulates the state and logic
  * of the transition.
  * of the transition.
@@ -117,19 +139,11 @@ p.enter = function (op, cb) {
  */
  */
 
 
 p.enterNextTick = function () {
 p.enterNextTick = function () {
-  // Important hack:
-  // in Chrome, if a just-entered element is applied the
-  // leave class while its interpolated property still has
-  // a very small value (within one frame), Chrome will
-  // skip the leave transition entirely and not firing the
-  // transtionend event. Therefore we need to protected
-  // against such cases using a one-frame timeout.
+  // prevent transition skipping
   this.justEntered = true
   this.justEntered = true
-  var self = this
-  setTimeout(function () {
-    self.justEntered = false
-  }, 17)
-
+  waitForTransitionStart(() => {
+    this.justEntered = false
+  })
   var enterDone = this.enterDone
   var enterDone = this.enterDone
   var type = this.getCssTransitionType(this.enterClass)
   var type = this.getCssTransitionType(this.enterClass)
   if (!this.pendingJsCb) {
   if (!this.pendingJsCb) {

+ 1 - 1
test/unit/specs/transition/transition_spec.js

@@ -194,7 +194,7 @@ if (!_.isIE9) {
               expect(el.classList.contains('test-no-trans-leave')).toBe(false)
               expect(el.classList.contains('test-no-trans-leave')).toBe(false)
               done()
               done()
             })
             })
-          }, 17)
+          }, 50)
         })
         })
       })
       })