185 lines
4.6 KiB
JavaScript
185 lines
4.6 KiB
JavaScript
// https://github.com/yiminghe/css-animation 1.5.0
|
|
|
|
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(className);
|
|
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;
|