Rotate.tsx 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import React, { useEffect, useState, Children, cloneElement, isValidElement } from 'react';
  2. import { Transition } from '@douyinfe/semi-animation-react';
  3. import PropTypes from 'prop-types';
  4. const formatStyle = function formatStyle({ rotate = 0 }) {
  5. return ({
  6. transform: `rotate(${rotate}deg)`,
  7. });
  8. };
  9. export interface TransitionStyle {
  10. rotate?: number;
  11. }
  12. export interface OpenIconTransitionProps {
  13. isOpen?: boolean;
  14. children?: React.ReactNode;
  15. enterDeg?: number;
  16. fromDeg?: number;
  17. duration?: number;
  18. }
  19. function OpenIconTransition(props: OpenIconTransitionProps = {}): React.ReactElement {
  20. const { children, isOpen, enterDeg = 180, fromDeg = 0, duration = 150 } = props;
  21. const [immediate, setImmediate] = useState(true);
  22. useEffect(() => {
  23. // eslint-disable-next-line @typescript-eslint/no-implied-eval
  24. setImmediate(false);
  25. }, [isOpen]);
  26. return (
  27. <Transition
  28. immediate={immediate}
  29. state={isOpen ? 'enter' : 'leave'}
  30. from={{ rotate: fromDeg }}
  31. enter={{ rotate: { val: enterDeg, duration, easing: 'cubic-bezier(.62, .05, .36, .95)' } }}
  32. leave={{ rotate: { val: fromDeg, duration, easing: 'cubic-bezier(.62, .05, .36, .95)' } }}
  33. >
  34. {(transitionStyle: TransitionStyle) =>
  35. Children.map(children, child => (isValidElement(child) ?
  36. cloneElement(child, {
  37. ...child.props,
  38. style: {
  39. ...child.props.style,
  40. ...formatStyle(transitionStyle),
  41. },
  42. }) :
  43. child))
  44. }
  45. </Transition>
  46. );
  47. }
  48. OpenIconTransition.propTypes = {
  49. isOpen: PropTypes.bool.isRequired,
  50. children: PropTypes.any.isRequired,
  51. enterDeg: PropTypes.number,
  52. fromDeg: PropTypes.number,
  53. duration: PropTypes.number,
  54. };
  55. export default OpenIconTransition;