200 lines
5.1 KiB
TypeScript
200 lines
5.1 KiB
TypeScript
// 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-', ''];
|
|
|
|
interface CustomHTMLElement extends HTMLElement {
|
|
rcEndAnimTimeout?: any;
|
|
rcAnimTimeout?: any;
|
|
rcEndListener?: Function | null;
|
|
}
|
|
|
|
function getStyleProperty(node: HTMLElement, name: string) {
|
|
// 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: CustomHTMLElement) {
|
|
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: CustomHTMLElement) {
|
|
if (node.rcEndAnimTimeout) {
|
|
clearTimeout(node.rcEndAnimTimeout);
|
|
node.rcEndAnimTimeout = null;
|
|
}
|
|
}
|
|
interface TransitionName {
|
|
name?: string;
|
|
active?: string;
|
|
}
|
|
const cssAnimation = (
|
|
node: CustomHTMLElement,
|
|
transitionName: string | TransitionName,
|
|
endCallback?: any,
|
|
) => {
|
|
const nameIsObj = typeof transitionName === 'object';
|
|
const className = nameIsObj ? (transitionName as TransitionName).name : transitionName;
|
|
const activeClassName = nameIsObj
|
|
? (transitionName as TransitionName).active
|
|
: `${transitionName}-active`;
|
|
let end = endCallback;
|
|
let start;
|
|
let active: any;
|
|
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: Event) => {
|
|
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: CustomHTMLElement, style: Record<string, any>, callback?: Function) => {
|
|
if (node.rcEndListener) {
|
|
node.rcEndListener();
|
|
}
|
|
|
|
node.rcEndListener = (e: Event) => {
|
|
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 as any] = style[s];
|
|
}
|
|
}
|
|
node.rcAnimTimeout = null;
|
|
fixBrowserByTimeout(node);
|
|
}, 0);
|
|
};
|
|
|
|
cssAnimation.setTransition = (node: HTMLElement, p?: any, value?: any) => {
|
|
let property = p;
|
|
let v = value;
|
|
if (value === undefined) {
|
|
v = property;
|
|
property = '';
|
|
}
|
|
property = property || '';
|
|
capitalPrefixes.forEach(prefix => {
|
|
node.style[`${prefix}Transition${property}` as any] = v;
|
|
});
|
|
};
|
|
|
|
cssAnimation.isCssAnimationSupported = isCssAnimationSupported;
|
|
|
|
export { isCssAnimationSupported };
|
|
|
|
export default cssAnimation;
|