首页 > 解决方案 > 如何使用 React 将动画从静态定位到固定定位?

问题描述

我想做从静态到固定定位的平滑过渡。因为浏览器在动画之前需要知道初始值,所以我想分两个阶段来做:

  1. 将位置更改为固定并强制渲染,将元素从流中取出,但将其放置在同一位置。left、top、right 和 bottom 的值现在对浏览器来说是已知的。
  2. 将值更改为它们的目标位置并强制另一个渲染。浏览器现在应该能够为这些值设置动画。

但是我认为我误解了一些基本的东西,因为虽然它确实定位正确,但它根本没有动画。有人可以告诉我以下代码中缺少什么吗?或者,如果我对此采取了错误的方法?

const Component = ({
  className
}) => {
  const placeholder = React.useRef()
  const container = React.useRef()
  const [state, setState] = React.useState('contracted')

  React.useEffect(() => {
    const containerStyle = container.current.style
    const placeholderStyle = placeholder.current.style

    switch (state) {
      case 'expand':
        const {
          left,
          top,
          right,
          bottom,
          width,
          height
        } = container.current.getBoundingClientRect()

        // Take the container out of the flow, placing it
        // at the same position.
        containerStyle.position = 'fixed'
        containerStyle.left = left + 'px'
        containerStyle.top = top + 'px'
        containerStyle.right = right + 'px'
        containerStyle.bottom = bottom + 'px'

        // Replace the container with the placeholder.
        placeholderStyle.width = width + 'px'
        placeholderStyle.height = height + 'px'

        // Set animation parameters for the next render.
        containerStyle.transition = 'all 1s ease'

        // Force a render by transitioning to the next state.
        setState('expanding')
        break;
      case 'expanding':
        // Position the container relative to the viewport.
        containerStyle.left = 10 + '%'
        containerStyle.top = 10 + '%'
        containerStyle.right = 10 + '%'
        containerStyle.bottom = 10 + '%'

        // Force a render by transitioning to the next state.
        setState('expanded')
        break;
      case 'contract':
        // Place the container back into the flow.
        containerStyle.transition = null
        containerStyle.position = null
        containerStyle.left = null
        containerStyle.top = null
        containerStyle.right = null
        containerStyle.bottom = null

        // Remove the placeholder.
        placeholderStyle.width = null
        placeholderStyle.height = null

        // Force a render by transitioning to the next state.
        setState('contracted')
        break;
    }
  }, [state])

  const toggle = () => {
    if (state === 'contracted') {
      setState('expand')
    } else {
      setState('contract')
    }
  }

  return (
    <div ref={placeholder} className={className} >
      <div
        ref={container}
        className='container'
        onClick={toggle}>
        <span>This should animate.</span>
      </div>
    </div>
  )
}


ReactDOM.render(
  <React.StrictMode >
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Egestas tellus rutrum tellus pellentesque eu tincidunt tortor aliquam nulla.
    <Component className = 'component' />
    Gravida dictum fusce ut placerat orci nulla pellentesque dignissim.Netus et malesuada fames ac turpis egestas.Accumsan lacus vel facilisis volutpat est velit egestas dui id.
  </React.StrictMode>,
  document.getElementById('root')
)
.component {
  float: left;
  margin: 0.5rem;
}

.container {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  padding: 0.5rem;
  background-color: lightgrey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js" integrity="sha512-qlzIeUtTg7eBpmEaS12NZgxz52YYZVF5myj89mjJEesBd/oE9UPsYOX2QAXzvOAZYEvQohKdcY8zKE02ifXDmA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js" integrity="sha512-9jGNr5Piwe8nzLLYTk8QrEMPfjGU0px80GYzKZUxi7lmCfrBjtyCc1V5kkS5vxVwwIB7Qpzc7UxLiQxfAN30dw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="root"></div>

标签: javascriptcssreactjs

解决方案


推荐阅读