在 React 中处理滚动动画

Posted

技术标签:

【中文标题】在 React 中处理滚动动画【英文标题】:Handling scroll Animation in React 【发布时间】:2017-11-06 14:09:42 【问题描述】:

在 React 中处理滚动位置的正确方法是什么?由于更好的用户体验,我真的很喜欢平滑滚动。由于在 React 中操作 DOM 是一种反模式,我遇到了问题:如何平滑滚动到某个位置/元素?我通常会更改元素的 scrollTop 值,但这是对 DOM 的操作,这是不允许的。

JSBIN

代码:

import React from 'react';
import ReactDOM from 'react-dom';


class App extends React.Component 
  handleClick = e => 
    for (let i = 1; i <= 100; i++) 
      setTimeout(() => (this.node.scrollTop = i), i * 2);
    
  ;

  render() 
    const someArrayToMap = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    return (
      <div ref=node => this.node = node style=overflow: 'auto', height: '100vh'>
        <button onClick=this.handleClick>CLICK TO SCROLL</button>
        
            [...someArrayToMap,
            ...someArrayToMap,
            ...someArrayToMap,
            ...someArrayToMap,
            ...someArrayToMap,
            ...someArrayToMap,
            ...someArrayToMap].map((e, i) => <div key=i>some text here</div>)
        
      </div>
    );
  



ReactDOM.render(<App />, document.getElementById('root'));

如何以 React 的方式实现这一点?

【问题讨论】:

请记住,操作 DOM 是一种反模式,因为 React 本质上是一个 DOM 管理器,因此它必须保持对 DOM 中发生的一切的感知;话虽如此,您始终可以使用 findDOMNodegetBoundingClientRect 之类的东西直接观察 DOM 节点。只读就好了。 【参考方案1】:

您可以只使用 refs 和 scrollIntoView 方法(使用 behavior: 'smooth' 进行平滑滚动)。它只有几行代码,不需要包。

说这是你想要滚动到的地方

<p ref=this.myRef className="scrollToHere">[1] ...</p>

还有一些按钮

<button onClick=() => this.scroll(this.myRef) className="footnote">[1]</button>

调用滚动方法

class App extends Component 
  constructor() 
    super()
    this.myRef = React.createRef();

  scroll(ref) 
    ref.current.scrollIntoView(behavior: 'smooth')
  

编辑:由于所有浏览器 (overview of browser support) 尚不支持此方法,您可能需要使用 polyfill。

【讨论】:

这是仅用于 React Native 还是? 警告:自 2018 年 11 月起,浏览器对 scrollIntoView 上平滑滚动选项的支持仅限于 Chrome 和 Firefox 是一个Javscript方法,所以不依赖React。在这里你可以看到specification and browser support。由于并非所有浏览器都支持它,因此您可能需要使用 polyfill。我使用了this polyfill,这对我来说非常有效 这似乎是最好的选择,如果你包括 polyfill。当所有浏览器都实现这一点时,您只需删除 polyfill 即可。感谢您提供此解决方案,效果很好! @sympi 你应该接受这个作为正确答案。 @bbrinx 您的 polyfill 链接指向规范页面。【参考方案2】:

window.scroll(top: 0, left: 0, behavior: 'smooth' ) 为我工作。

您还需要检查browser's compability

或使用polyfill


编辑:为了完整起见,这里是如何使用 webpack 动态填充。

if (!('scrollBehavior' in document.documentElement.style)) 
//safari does not support smooth scroll
  (async () => 
    const default: smoothScroll = await import(
      /* webpackChunkName: 'polyfill-modern' */
      'smoothscroll-polyfill'
      )
    smoothScroll.polyfill()
  )()

通过这种动态填充,除非浏览器支持平滑滚动,否则包是通过 ajax 加载的。

polyfill-modern 是一个任意的块名称,它提示 webpack 编译器将包组合在一起,以减少对服务器的请求数。

【讨论】:

如果您只想“npm install”解决问题 - npmjs.com/package/smoothscroll-polyfill【参考方案3】:

最简单的方法:-

window.scrollTo(top: 0, left: 0, behavior: 'smooth' );

这个简单的 javascript 代码适用于所有浏览器。

【讨论】:

自 2021 年 7 月起,这将不适用于 Safari。请参阅其他答案,主要与 polyfill 相关【参考方案4】:

已经有几个很好的包可以为你处理这个问题:

https://github.com/fisshy/react-scroll - Demo

https://www.npmjs.com/package/react-scroll-to-component 简单滚动到组件

希望这会有所帮助!

【讨论】:

@PaulCo fisshy.github.io/react-scroll-example/basic/index.html 的演示对我来说效果很好 是左上角的列表应该可以点击滚动还是只是基于视口位置的样式? 是的,顶部菜单点击正在 Chrome 中为我滚动到该页面。 在 FF 中我遇到了控制台错误:ReferenceError: event is not defined,没关系,我会在 Chromium 中观看这个!【参考方案5】:

在 React 中有几个用于滚动到锚点的库。选择哪一种取决于您正在寻找的功能和页面的现有设置。

React Scrollable Anchor 是一个轻量级库,专门用于滚动到映射到 URL 哈希的锚点。它还根据当前关注的部分更新 URL 哈希。 [完全披露:我是这个库的作者]

在另一个答案中提到的 React Scroll 是一个功能更齐全的库,用于滚动到锚点,而不反映 URL 中的任何位置。

如果你已经在使用 React Router,你也可以连接像 React Router Hash Link Scroll 这样的东西,它也会绑定到你的 URL 哈希中。

【讨论】:

因为你是一个这样做的库的作者,并且 op 询问了 如何以反应的方式做到这一点,没有库。 (这就是我提出问题的方式,并希望得到答案)【参考方案6】:

这是一个使用钩子的小型、无依赖解决方案

const useSmoothScrollTo = id => 
    const ref = useRef(null)
    useEffect(() => 
        const listener = e => 
            if (ref.current && location.hash === id) 
                ref.current.scrollIntoView(behavior: 'smooth')
            
        
        window.addEventListener('hashchange', listener, true)
        return () => 
            window.removeEventListener('hashchange', listener)
        
    , [])
    return 
        'data-anchor-id': id,
        ref
    

你可以这样使用它:

export const FeaturesSection = () => 
    const bind = useSmoothScrollTo('#features')
    return (
        <section ...bind className=classes.features>
        ...
        </section>
    )
      

然后你只需要在你的应用中的其他任何地方做

<a href="#features">Go to Features</a>

显然,与上述相同的警告也适用于.scrollIntoView(behavior: 'smooth')

【讨论】:

【参考方案7】:

我真的很喜欢 API 部分中的react-router website,他们似乎使用了这个scrollToDoc component,这是一个将典型的 VanillaJS 平滑滚动函数非常漂亮的翻译成依赖于react-motion 的 React!

【讨论】:

以上是关于在 React 中处理滚动动画的主要内容,如果未能解决你的问题,请参考以下文章

react-transition-group动画以及数字滚动效果实现

React-native 水平滚动视图动画到结束

[react] 如何用React实现滚动动画?

有没有办法在水平滚动视图中对特定偏移进行动画处理?

react-ace无法滚动

UITableView 滚动时动画单元格