UNPKG

vue

Version:

Reactive, component-oriented view layer for modern web interfaces.

151 lines (136 loc) 4.11 kB
/* @flow */ import { inBrowser, isIE9 } from 'core/util/index' import { remove } from 'shared/util' import { addClass, removeClass } from './class-util' export const hasTransition = inBrowser && !isIE9 const TRANSITION = 'transition' const ANIMATION = 'animation' // Transition property/event sniffing export let transitionProp = 'transition' export let transitionEndEvent = 'transitionend' export let animationProp = 'animation' export let animationEndEvent = 'animationend' if (hasTransition) { /* istanbul ignore if */ if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) { transitionProp = 'WebkitTransition' transitionEndEvent = 'webkitTransitionEnd' } if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) { animationProp = 'WebkitAnimation' animationEndEvent = 'webkitAnimationEnd' } } // binding to window is necessary to make hot reload work in IE in strict mode const raf = inBrowser && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : setTimeout export function nextFrame (fn: Function) { raf(() => { raf(fn) }) } export function addTransitionClass (el: any, cls: string) { (el._transitionClasses || (el._transitionClasses = [])).push(cls) addClass(el, cls) } export function removeTransitionClass (el: any, cls: string) { if (el._transitionClasses) { remove(el._transitionClasses, cls) } removeClass(el, cls) } export function whenTransitionEnds ( el: Element, expectedType: ?string, cb: Function ) { const { type, timeout, propCount } = getTransitionInfo(el, expectedType) if (!type) return cb() const event = type === TRANSITION ? transitionEndEvent : animationEndEvent let ended = 0 const end = () => { el.removeEventListener(event, onEnd) cb() } const onEnd = e => { if (e.target === el) { if (++ended >= propCount) { end() } } } setTimeout(() => { if (ended < propCount) { end() } }, timeout + 1) el.addEventListener(event, onEnd) } const transformRE = /\b(transform|all)(,|$)/ export function getTransitionInfo (el: Element, expectedType?: ?string): { type: ?string; propCount: number; timeout: number; hasTransform: boolean; } { const styles = window.getComputedStyle(el) const transitioneDelays = styles[transitionProp + 'Delay'].split(', ') const transitionDurations = styles[transitionProp + 'Duration'].split(', ') const transitionTimeout = getTimeout(transitioneDelays, transitionDurations) const animationDelays = styles[animationProp + 'Delay'].split(', ') const animationDurations = styles[animationProp + 'Duration'].split(', ') const animationTimeout = getTimeout(animationDelays, animationDurations) let type let timeout = 0 let propCount = 0 /* istanbul ignore if */ if (expectedType === TRANSITION) { if (transitionTimeout > 0) { type = TRANSITION timeout = transitionTimeout propCount = transitionDurations.length } } else if (expectedType === ANIMATION) { if (animationTimeout > 0) { type = ANIMATION timeout = animationTimeout propCount = animationDurations.length } } else { timeout = Math.max(transitionTimeout, animationTimeout) type = timeout > 0 ? transitionTimeout > animationTimeout ? TRANSITION : ANIMATION : null propCount = type ? type === TRANSITION ? transitionDurations.length : animationDurations.length : 0 } const hasTransform = type === TRANSITION && transformRE.test(styles[transitionProp + 'Property']) return { type, timeout, propCount, hasTransform } } function getTimeout (delays: Array<string>, durations: Array<string>): number { /* istanbul ignore next */ while (delays.length < durations.length) { delays = delays.concat(delays) } return Math.max.apply(null, durations.map((d, i) => { return toMs(d) + toMs(delays[i]) })) } function toMs (s: string): number { return Number(s.slice(0, -1)) * 1000 }