|
|
@@ -1,45 +1,75 @@
|
|
|
import { addClass, removeClass } from '../class-util'
|
|
|
-import {
|
|
|
- isIE9,
|
|
|
- inBrowser,
|
|
|
- transitionProp,
|
|
|
- transitionEndEvent,
|
|
|
- animationProp,
|
|
|
- animationEndEvent
|
|
|
-} from '../../util/index'
|
|
|
+import { isIE9, inBrowser, cached } from '../../util/index'
|
|
|
|
|
|
-export default isIE9 ? {} : {
|
|
|
- create: function applyEnterTransition (_, vnode) {
|
|
|
- let data = vnode.data.transition
|
|
|
- const el = vnode.elm
|
|
|
- if (data != null) {
|
|
|
- if (typeof data === 'string') {
|
|
|
- // pure CSS
|
|
|
- data = cssTransition(data)
|
|
|
- }
|
|
|
- // apply enter class
|
|
|
- const enterClass = data.enterClass
|
|
|
- if (enterClass) {
|
|
|
- addTransitionClass(el, enterClass)
|
|
|
- nextFrame(() => {
|
|
|
- removeTransitionClass(el, enterClass)
|
|
|
- })
|
|
|
- }
|
|
|
- const enterActiveClass = data.enterActiveClass
|
|
|
- if (enterActiveClass) {
|
|
|
- addTransitionClass(el, enterActiveClass)
|
|
|
- el.addEventListener(transitionEndEvent, () => {
|
|
|
+const TRANSITION = 'transition'
|
|
|
+const ANIMATION = 'animation'
|
|
|
+
|
|
|
+// Transition property/event sniffing
|
|
|
+let transitionProp
|
|
|
+let transitionEndEvent
|
|
|
+let animationProp
|
|
|
+let animationEndEvent
|
|
|
+if (inBrowser && !isIE9) {
|
|
|
+ const isWebkitTrans =
|
|
|
+ window.ontransitionend === undefined &&
|
|
|
+ window.onwebkittransitionend !== undefined
|
|
|
+ const isWebkitAnim =
|
|
|
+ window.onanimationend === undefined &&
|
|
|
+ window.onwebkitanimationend !== undefined
|
|
|
+ transitionProp = isWebkitTrans ? 'WebkitTransition' : 'transition'
|
|
|
+ transitionEndEvent = isWebkitTrans ? 'webkitTransitionEnd' : 'transitionend'
|
|
|
+ animationProp = isWebkitAnim ? 'WebkitAnimation' : 'animation'
|
|
|
+ animationEndEvent = isWebkitAnim ? 'webkitAnimationEnd' : 'animationend'
|
|
|
+}
|
|
|
+
|
|
|
+const raf = (inBrowser && window.requestAnimationFrame) || setTimeout
|
|
|
+function nextFrame (fn) {
|
|
|
+ raf(() => {
|
|
|
+ raf(fn)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const autoCssTransition = cached(name => {
|
|
|
+ name = name || 'v'
|
|
|
+ return {
|
|
|
+ enterClass: `${name}-enter`,
|
|
|
+ leaveClass: `${name}-leave`,
|
|
|
+ enterActiveClass: `${name}-enter-active`,
|
|
|
+ leaveActiveClass: `${name}-leave-active`
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+function beforeEnter (_, vnode) {
|
|
|
+ const el = vnode.elm
|
|
|
+ let data = vnode.data.transition
|
|
|
+ if (data != null) {
|
|
|
+ if (typeof data === 'string') {
|
|
|
+ data = vnode.data.transition = autoCssTransition(data)
|
|
|
+ }
|
|
|
+ // apply enter class
|
|
|
+ const enterClass = data.enterClass
|
|
|
+ if (enterClass) {
|
|
|
+ addTransitionClass(el, enterClass)
|
|
|
+ nextFrame(() => {
|
|
|
+ removeTransitionClass(el, enterClass)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ const enterActiveClass = data.enterActiveClass
|
|
|
+ if (enterActiveClass) {
|
|
|
+ addTransitionClass(el, enterActiveClass)
|
|
|
+ nextFrame(() => {
|
|
|
+ whenTransitionEnds(el, () => {
|
|
|
removeTransitionClass(el, enterActiveClass)
|
|
|
})
|
|
|
- }
|
|
|
+ })
|
|
|
}
|
|
|
- },
|
|
|
-
|
|
|
- remove: function applyLeaveTransition (vnode, rm) {
|
|
|
-
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+function onLeave (vnode, rm) {
|
|
|
+ rm()
|
|
|
+}
|
|
|
+
|
|
|
function addTransitionClass (el, cls) {
|
|
|
(el._transitionClasses || (el._transitionClasses = [])).push(cls)
|
|
|
addClass(el, cls)
|
|
|
@@ -50,19 +80,66 @@ function removeTransitionClass (el, cls) {
|
|
|
removeClass(el, cls)
|
|
|
}
|
|
|
|
|
|
-const raf = (inBrowser && window.requestAnimationFrame) || setTimeout
|
|
|
-function nextFrame (fn) {
|
|
|
- raf(() => {
|
|
|
- raf(fn)
|
|
|
- })
|
|
|
+function whenTransitionEnds (el, cb) {
|
|
|
+ const { type, timeout, propCount } = getTransitionInfo(el)
|
|
|
+ if (!type) return cb()
|
|
|
+ const event = type === TRANSITION ? transitionEndEvent : animationEndEvent
|
|
|
+ let ended = 0
|
|
|
+ const end = () => {
|
|
|
+ el.removeEventListener(event, onEnd)
|
|
|
+ cb()
|
|
|
+ }
|
|
|
+ const onEnd = () => {
|
|
|
+ if (++ended >= propCount) {
|
|
|
+ end()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ setTimeout(() => {
|
|
|
+ if (ended < propCount) {
|
|
|
+ end()
|
|
|
+ }
|
|
|
+ }, timeout)
|
|
|
+ el.addEventListener(event, onEnd)
|
|
|
}
|
|
|
|
|
|
-function cssTransition (name) {
|
|
|
- name = name || 'v'
|
|
|
+function getTransitionInfo (el) {
|
|
|
+ const styles = window.getComputedStyle(el)
|
|
|
+ // 1. determine the maximum duration (timeout)
|
|
|
+ const transitioneDelays = styles[transitionProp + 'Delay'].split(', ')
|
|
|
+ const transitionDurations = styles[transitionProp + 'Duration'].split(', ')
|
|
|
+ const animationDelays = styles[animationProp + 'Delay'].split(', ')
|
|
|
+ const animationDurations = styles[animationProp + 'Duration'].split(', ')
|
|
|
+ const transitionTimeout = getTimeout(transitioneDelays, transitionDurations)
|
|
|
+ const animationTimeout = getTimeout(animationDelays, animationDurations)
|
|
|
+ const timeout = Math.max(transitionTimeout, animationTimeout)
|
|
|
+ const type = timeout > 0
|
|
|
+ ? transitionTimeout > animationTimeout
|
|
|
+ ? TRANSITION
|
|
|
+ : ANIMATION
|
|
|
+ : null
|
|
|
+ const propCount = type
|
|
|
+ ? type === TRANSITION
|
|
|
+ ? transitionDurations.length
|
|
|
+ : animationDurations.length
|
|
|
+ : 0
|
|
|
return {
|
|
|
- enterClass: `${name}-enter`,
|
|
|
- leaveClass: `${name}-leave`,
|
|
|
- enterActiveClass: `${name}-enter-active`,
|
|
|
- leaveActiveClass: `${name}-leave-active`
|
|
|
+ type,
|
|
|
+ timeout,
|
|
|
+ propCount
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+function getTimeout (delays, durations) {
|
|
|
+ return Math.max.apply(null, durations.map((d, i) => {
|
|
|
+ return toMs(d) + toMs(delays[i])
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+function toMs (s) {
|
|
|
+ return Number(s.slice(0, -1)) * 1000
|
|
|
+}
|
|
|
+
|
|
|
+export default !transitionEndEvent ? {} : {
|
|
|
+ create: beforeEnter,
|
|
|
+ remove: onLeave
|
|
|
+}
|