Skip to content

Commit

Permalink
fix(Overlay): stabilize animate id
Browse files Browse the repository at this point in the history
  • Loading branch information
HytonightYX committed Mar 17, 2022
1 parent acf9add commit 283eee6
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import React from 'react';
import { Observable } from 'rxjs';
import { Keyframes } from 'styled-components';
import * as React from 'react';

export interface Disposable {
dispose(): void;
Expand All @@ -9,9 +7,9 @@ export interface Disposable {
// animate.css CSS 类名前缀
// 详见 https://animate.style/
export const ANIMATE_PREFIX = 'animate__';
export const ANIMATE_ANIMATED = `${ANIMATE_PREFIX}animated`;
export const ANIMATE_ANIMATED = `animate__animated`;

function startSimpleAnimate(element: Element, onEnd: (event: AnimationEvent) => void) {
function startNullAnimate(element: Element, onEnd: (event: AnimationEvent) => void) {
const handle = requestAnimationFrame(() => onEnd(null));

return {
Expand All @@ -24,48 +22,33 @@ function startSimpleAnimate(element: Element, onEnd: (event: AnimationEvent) =>
// 利用 className 为元素添加指定名称的 CSS 动画(animation),并在动画结束后自动移除动画的 className
export function startAnimate(
element: Element,
animation: null | string | Keyframes,
animation: null | string,
onEnd: (event: AnimationEvent) => void,
): Disposable {
if (animation == null) {
// 用户禁用了动画,为了保持 onEnd 的调用时机一致
// 这里将调用 startSimpleAnimate,该函数内部会等待一个动画帧来表示「动画的过程」
return startSimpleAnimate(element, onEnd);
// 这里将调用 startNullAnimate,该函数内部会等待一个动画帧来表示「动画的过程」
return startNullAnimate(element, onEnd);
}

if (typeof animation === 'object') {
animation = animation.getName();
}
element.classList.add(ANIMATE_ANIMATED, `${ANIMATE_PREFIX}${animation}`);
const animateClassName = `${ANIMATE_PREFIX}${animation}`;

element.classList.add(ANIMATE_ANIMATED, animateClassName);
element.addEventListener('animationend', handleAnimationEnd);

function dispose() {
element.classList.remove(ANIMATE_ANIMATED, `${ANIMATE_PREFIX}${animation}`);
element.classList.remove(ANIMATE_ANIMATED, animateClassName);
element.removeEventListener('animationend', handleAnimationEnd);
}

function handleAnimationEnd(_e: Event) {
const event = _e as AnimationEvent;
if (event.target === element && event.animationName === animation) {

if (event.target === element && Array.from(element.classList).includes(animateClassName)) {
dispose();
onEnd(event);
}
}

return { dispose };
}

export const animationFrame$ = new Observable<number>((subscriber) => {
let handle: number;

const callback = (arg: number) => {
subscriber.next(arg);
handle = requestAnimationFrame(callback);
};

callback(performance.now());

return () => {
cancelAnimationFrame(handle);
};
});
12 changes: 8 additions & 4 deletions packages/core/src/components/overlays/overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,14 @@ export class Overlay extends React.Component<OverlayProps, OverlayState> {
}
}

private resolveEnterAnimation(instruction: IOverlayAnimationProps): null | string | Keyframes {
private resolveEnterAnimation(instruction: IOverlayAnimationProps): null | string {
const { animation: animationProp } = this.props;
if (animationProp === false || instruction?.animation === false) {
return null;
}

return instruction?.animation?.in ?? animationProp?.in ?? null;
const animation = instruction?.animation?.in ?? animationProp?.in ?? null;
return typeof animation === 'string' ? animation : animation.getName();
}

// 处理打开浮层的动画
Expand All @@ -275,6 +276,8 @@ export class Overlay extends React.Component<OverlayProps, OverlayState> {

const overlayOpenInstruction = await beforeOpen?.();

console.log('overlayOpenInstruction', overlayOpenInstruction);

this.overlayAnimateInst?.dispose();
this.overlayAnimateInst = startAnimate(inner, this.resolveEnterAnimation(overlayOpenInstruction), () => {
afterOpen?.();
Expand All @@ -284,13 +287,14 @@ export class Overlay extends React.Component<OverlayProps, OverlayState> {
onOpen?.();
}

private resolveExitAnimation(instruction: IOverlayAnimationProps): null | string | Keyframes {
private resolveExitAnimation(instruction: IOverlayAnimationProps) {
const { animation: animationProp } = this.props;
if (animationProp === false || instruction?.animation === false) {
return null;
}

return instruction?.animation?.out ?? animationProp?.out ?? null;
const animation = instruction?.animation?.out ?? animationProp?.out ?? null;
return typeof animation === 'string' ? animation : animation.getName();
}

private async doCloseOverlay(force = false) {
Expand Down

0 comments on commit 283eee6

Please sign in to comment.