Browse Source

robust transition/animation detection

dev
Evan You 9 years ago
parent
commit
9d5d4671e0
  1. 1
      src/runtime/dom-backend/modules/class.js
  2. 20
      src/runtime/dom-backend/modules/style.js
  3. 167
      src/runtime/dom-backend/modules/transition.js
  4. 34
      src/runtime/util/env.js

1
src/runtime/dom-backend/modules/class.js

@ -11,6 +11,7 @@ function updateClass (oldVnode, vnode) {
transitionClass = genClass(transitionClass)
const cls = concat(concat(staticClass, dynamicClass), transitionClass)
if (cls !== oldVnode.class) {
console.log(cls)
setClass(el, cls)
}
vnode.class = cls

20
src/runtime/dom-backend/modules/style.js

@ -18,16 +18,6 @@ const normalize = cached(function (prop) {
}
})
function toObject (arr) {
const res = arr[0] || {}
for (var i = 1; i < arr.length; i++) {
if (arr[i]) {
extend(res, arr[i])
}
}
return res
}
function updateStyle (oldVnode, vnode) {
if (!oldVnode.data.style && !vnode.data.style) {
return
@ -55,6 +45,16 @@ function updateStyle (oldVnode, vnode) {
}
}
function toObject (arr) {
const res = arr[0] || {}
for (var i = 1; i < arr.length; i++) {
if (arr[i]) {
extend(res, arr[i])
}
}
return res
}
export default {
create: updateStyle,
update: updateStyle

167
src/runtime/dom-backend/modules/transition.js

@ -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
}

34
src/runtime/util/env.js

@ -18,40 +18,6 @@ export const isAndroid = UA && UA.indexOf('android') > 0
export const isIos = UA && /(iphone|ipad|ipod|ios)/i.test(UA)
export const isWechat = UA && UA.indexOf('micromessenger') > 0
let transitionProp
let transitionEndEvent
let animationProp
let animationEndEvent
// Transition property/event sniffing
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'
}
export {
transitionProp,
transitionEndEvent,
animationProp,
animationEndEvent
}
/**
* Defer a task to execute it asynchronously. Ideally this
* should be executed as a microtask, so we leverage

Loading…
Cancel
Save