index.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import React, { CSSProperties, ReactNode } from 'react';
  2. import { isEqual, noop } from "lodash";
  3. interface AnimationEventsNeedBind {
  4. onAnimationStart?: (e: React.AnimationEvent) => void;
  5. onAnimationEnd?: (e: React.AnimationEvent) => void;
  6. [key: string]: (e: any) => void
  7. }
  8. interface AnimationProps {
  9. startClassName?: string;
  10. endClassName?: string;
  11. children: ({ }: {
  12. animationClassName: string;
  13. animationStyle: CSSProperties;
  14. animationEventsNeedBind: AnimationEventsNeedBind;
  15. isAnimating: boolean
  16. }) => ReactNode;
  17. animationState: "enter" | "leave";
  18. onAnimationEnd?: (stoppedByAnother: boolean) => void;
  19. onAnimationStart?: () => void;
  20. motion?: boolean;
  21. replayKey?: string;
  22. fillMode?: "backwards" | "both" | "forwards" | "none"
  23. }
  24. interface AnimationState {
  25. currentClassName: string;
  26. extraStyle: CSSProperties;
  27. isAnimating: boolean
  28. }
  29. class CSSAnimation extends React.Component<AnimationProps, AnimationState> {
  30. static defaultProps = {
  31. motion: true,
  32. replayKey: "",
  33. }
  34. constructor(props) {
  35. super(props);
  36. this.state = {
  37. currentClassName: this.props.startClassName,
  38. extraStyle: {
  39. animationFillMode: this.props.fillMode,
  40. },
  41. isAnimating: true
  42. };
  43. }
  44. componentDidMount() {
  45. // The purpose is to shield the impact of the presence or absence of animation on the other semi component life cycle.
  46. // In order to make the component side do not need to manually call the next life cycle function when there is no animation,
  47. // so when there is no animation , it is logically (and only logically) regarded as an animation with a duration of 0.
  48. this.props.onAnimationStart?.();
  49. if (!this.props.motion) {
  50. this.props.onAnimationEnd?.(false);
  51. this.setState({ isAnimating: false });
  52. }
  53. }
  54. componentDidUpdate(prevProps: Readonly<AnimationProps>, prevState: Readonly<AnimationState>, snapshot?: any) {
  55. const changedKeys = Object.keys(this.props).filter(key => !isEqual(this.props[key], prevProps[key]));
  56. if (changedKeys.includes("animationState")) {
  57. }
  58. if (changedKeys.includes("startClassName") || changedKeys.includes('replayKey') || changedKeys.includes("motion")) {
  59. this.setState({
  60. currentClassName: this.props.startClassName,
  61. extraStyle: {
  62. animationFillMode: this.props.fillMode,
  63. },
  64. isAnimating: true
  65. }, () => {
  66. this.props.onAnimationStart?.();
  67. if (!this.props.motion) {
  68. this.props.onAnimationEnd?.(this.state.isAnimating);
  69. this.setState({ isAnimating: false });
  70. }
  71. });
  72. }
  73. }
  74. handleAnimationStart = () => {
  75. this.props.onAnimationStart?.();
  76. }
  77. handleAnimationEnd = () => {
  78. this.setState({
  79. currentClassName: this.props.endClassName,
  80. extraStyle: {
  81. animationFillMode: this.props.fillMode,
  82. },
  83. isAnimating: false
  84. }, () => {
  85. this.props.onAnimationEnd?.(false);
  86. });
  87. }
  88. render() {
  89. if (this.props.motion) {
  90. return this.props.children({
  91. animationClassName: this.state.currentClassName ?? "",
  92. animationStyle: this.state.extraStyle,
  93. animationEventsNeedBind: {
  94. onAnimationStart: this.handleAnimationStart,
  95. onAnimationEnd: this.handleAnimationEnd
  96. },
  97. isAnimating: this.state.isAnimating
  98. });
  99. } else {
  100. return this.props.children({
  101. animationClassName: "",
  102. animationStyle: {},
  103. animationEventsNeedBind: {},
  104. isAnimating: this.state.isAnimating
  105. });
  106. }
  107. }
  108. }
  109. // const mergeAnimationFunction = (eventHandleFunctions: AnimationEventsNeedBind[]) => {
  110. // //merge function in objects
  111. // const mergedFunction = {};
  112. // eventHandleFunctions.forEach(eventHandleFunction => {
  113. // Object.keys(eventHandleFunction).forEach(key => {
  114. // if (mergedFunction[key]) {
  115. // const oldFunction = mergedFunction[key];
  116. // mergedFunction[key] = (e) => {
  117. // eventHandleFunction[key](e);
  118. // oldFunction(e);
  119. // };
  120. // } else {
  121. // mergedFunction[key] = eventHandleFunction[key];
  122. // }
  123. // });
  124. // });
  125. // return mergedFunction;
  126. // };
  127. // export { mergeAnimationFunction };
  128. export default CSSAnimation;