import Event from './Event' import classes from 'component-classes' import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout' const isCssAnimationSupported = Event.endEvents.length !== 0 const capitalPrefixes = ['Webkit', 'Moz', 'O', // ms is special .... ! 'ms'] const prefixes = ['-webkit-', '-moz-', '-o-', 'ms-', ''] function getStyleProperty (node, name) { // old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle const style = window.getComputedStyle(node, null) let ret = '' for (let i = 0; i < prefixes.length; i++) { ret = style.getPropertyValue(prefixes[i] + name) if (ret) { break } } return (ret) } function fixBrowserByTimeout (node) { if (isCssAnimationSupported) { const transitionDelay = parseFloat(getStyleProperty(node, 'transition-delay')) || 0 const transitionDuration = parseFloat(getStyleProperty(node, 'transition-duration')) || 0 const animationDelay = parseFloat(getStyleProperty(node, 'animation-delay')) || 0 const animationDuration = parseFloat(getStyleProperty(node, 'animation-duration')) || 0 const time = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay) // sometimes, browser bug node.rcEndAnimTimeout = setTimeout(() => { node.rcEndAnimTimeout = null if (node.rcEndListener) { node.rcEndListener() } }, (time) * 1000 + 200) } } function clearBrowserBugTimeout (node) { if (node.rcEndAnimTimeout) { clearTimeout(node.rcEndAnimTimeout) node.rcEndAnimTimeout = null } } const cssAnimation = (node, transitionName, endCallback) => { const nameIsObj = typeof transitionName === 'object' const className = nameIsObj ? transitionName.name : transitionName const activeClassName = nameIsObj ? transitionName.active : `${transitionName}-active` let end = endCallback let start let active const nodeClasses = classes(node) if (endCallback && Object.prototype.toString.call(endCallback) === '[object Object]') { end = endCallback.end start = endCallback.start active = endCallback.active } if (node.rcEndListener) { node.rcEndListener() } node.rcEndListener = (e) => { if (e && e.target !== node) { return } if (node.rcAnimTimeout) { cancelAnimationTimeout(node.rcAnimTimeout) node.rcAnimTimeout = null } clearBrowserBugTimeout(node) nodeClasses.remove(className) nodeClasses.remove(activeClassName) Event.removeEndEventListener(node, node.rcEndListener) node.rcEndListener = null // Usually this optional end is used for informing an owner of // a leave animation and telling it to remove the child. if (end) { end() } } Event.addEndEventListener(node, node.rcEndListener) if (start) { start() } nodeClasses.add(className) node.rcAnimTimeout = requestAnimationTimeout(() => { node.rcAnimTimeout = null nodeClasses.add(activeClassName) if (active) { requestAnimationTimeout(active, 0) } fixBrowserByTimeout(node) // 30ms for firefox }, 30) return { stop () { if (node.rcEndListener) { node.rcEndListener() } }, } } cssAnimation.style = (node, style, callback) => { if (node.rcEndListener) { node.rcEndListener() } node.rcEndListener = (e) => { if (e && e.target !== node) { return } if (node.rcAnimTimeout) { cancelAnimationTimeout(node.rcAnimTimeout) node.rcAnimTimeout = null } clearBrowserBugTimeout(node) Event.removeEndEventListener(node, node.rcEndListener) node.rcEndListener = null // Usually this optional callback is used for informing an owner of // a leave animation and telling it to remove the child. if (callback) { callback() } } Event.addEndEventListener(node, node.rcEndListener) node.rcAnimTimeout = requestAnimationTimeout(() => { for (const s in style) { if (style.hasOwnProperty(s)) { node.style[s] = style[s] } } node.rcAnimTimeout = null fixBrowserByTimeout(node) }, 0) } cssAnimation.setTransition = (node, p, value) => { let property = p let v = value if (value === undefined) { v = property property = '' } property = property || '' capitalPrefixes.forEach((prefix) => { node.style[`${prefix}Transition${property}`] = v }) } cssAnimation.isCssAnimationSupported = isCssAnimationSupported export { isCssAnimationSupported, } export default cssAnimation