ホームページ  >  記事  >  ウェブフロントエンド  >  React アニメーションの練習_html/css_WEB-ITnose

React アニメーションの練習_html/css_WEB-ITnose

WBOY
WBOYオリジナル
2016-06-24 11:28:451217ブラウズ

1. アニメーションの重要性

世界で最も難しい知識は、人間を研究することです。アニメーションがユーザーのデバイスの速度を低下させるほどリソースを大量に消費しない限り、アニメーションはユーザー インターフェイス エクスペリエンスを大幅に向上させることができます。

ページ アニメーションは次のタイプに簡単に分けることができます:

1. ユーザー操作によって引き起こされるページ要素アニメーション

2. ユーザーの視覚的な待ち時間を短縮します。装飾的なアニメーション: ユーザーの注意をそらすことになるため、避けるようにしてください。

4. 広告アニメーション: 広告のコンバージョン率を向上させます。

5. プロット アニメーション: 主に SPA で使用されます。アニメーションの読み込みを例に挙げます。ユーザー エクスペリエンスを向上させ、ユーザーの定着率を高めるために、開発の観点から最初に思い浮かぶのは、フロントツーバックのパフォーマンスの最適化であり、それによって待ち時間を短縮します。ユーザーがページを開くためには、帯域幅を増やす、ページの http リクエストを減らす、データ キャッシュを使用する、データベースを最適化する、ロード バランシングを使用するなどを検討できます。ただし、ビジネスの制限とユーザーの複雑なエクスペリエンス環境により、常にいくつかのボトルネックに遭遇します。このとき、私たちがやるべきことは、ユーザーの視覚的な待ち時間をいかに減らすかです。たとえそれが回転菊であっても、盲目的に待っているとビジネスが失われます。率直に言って、セクシーな菊はデータベースを最適化するのと同じくらい役立つ場合があります。

2. アニメーション実装の原則

アニメーションを実装するとき、私は常に次の原則に従っています:

1. パフォーマンス、パフォーマンス、パフォーマンス: この点での提案は、選択肢がある場合は CSS を使用する必要があるということです。 -based アニメーションの場合は、代替として JS を使用します。これは、ハードウェアのアクセラレーションとパフォーマンスを考慮すると、ほとんどの場合、ネイティブ JS によって実装されたアニメーションよりも CSS の方が優れているためです

2。 3. 大きくてゴージャスなアニメーションは目的を持ったものである必要があります。

4. アニメーションの長さは短くする必要があります。

6.アニメーションは突然停止すべきではありません

これが当てはまるかどうか考えてみてください。

3. React アニメーション

(1) 実装方法

React でアニメーションを実装するには 2 つの方法があります:

1. CSS グラデーション グループ

2. : 簡略化 CSS アニメーションをグラデーションに適用し、適切なレンダリングおよび再描画時に要素のクラスを戦略的に追加および削除するプロセス。

インターバル アニメーション: パフォーマンスを犠牲にして、拡張性と制御性を向上させます。より多くのレンダリングが必要ですが、CSS の外部のコンテンツ (スクロール バーの位置やキャンバスの描画など) のアニメーションも可能になります。

(2) CSS グラデーション グループ

ReactCSStransitionGroup は、基本的な CSS アニメーションとトランジションを単純に実装するために、プラグイン クラス ReactTransitionGroup の基礎となる API に基づいてさらにカプセル化された高レベルの API です。

1. クイックスタート

簡単な画像カルーセルを例に挙げます:

var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; var Carousel = React.createClass({        propTypes: {    transitionName: React.PropTypes.string.isRequired,            imageSrc: React.PropTypes.string.isRequired        },        render: function() {            return (                <divclassName='carousel'>                    <ReactCSSTransitionGrouptransitionName={this.props.transitionName} >                        <imgsrc={this.props.imageSrc} key={this.props.imageSrc} />                    </ReactCSSTransitionGroup>                </div>            );        }});

残りは、親コンポーネントに適切なtransitionName と imageSrc を渡すことです。効果は次のとおりです:

賢明な方なら、もうおわかりでしょう: このコンポーネントでは、新しいリスト項目が ReactCSSTransitionGroup に追加されると、transitionName-enter に対応する CSS クラスが追加されます。次の瞬間に、transitionName が追加されます。-enter-active に対応する CSS クラス。ReactCSSTransitionGroup からリスト項目が削除される場合、transitionName-leave に対応する CSS クラスも追加されます。次の瞬間に、transitionName-leave-active に追加されます。ここで注意すべき点は、項目を削除しようとすると、ReactCSSTransitionGroup はアニメーションが終了するまでその項目を DOM に保持するということです。この例では、transitionName 属性に対応する CSS アニメーションを変更するだけで済みます。 クラス:

透明度切り替えエフェクト:

.carousel1-enter {    opacity: 0;}.carousel1-enter-active {    opacity: 1;    transition: opacity 300ms ease-in;}.carousel1-leave {    opacity: 1;}.carousel1-leave-active {    opacity: 0;    transition: opacity 300ms ease-in;}

ディスプレイスメント切り替えエフェクト:

.carousel2-enter {    left: 100%;}.carousel2-enter-active {    left: 0;    transition: left 300ms ease-in;}.carousel2-leave {    left: 0;}.carousel2-leave-active {    left: -100%;    transition: left 300ms ease-in;}

私がtransitionName属性について言及していることに誰もが気づいています。実際、ReactCSStransitionGroup では 1 つの属性が完全に不足しているため、そのプロパティを以下に詳しく紹介します。

2. プロパティ

(1)、transitionName

    {oneOfType([React.PropTypes.string,React.PropTypes.object]).isRequired}
  • 関数: 関連する CSS クラス:
  • のための例:

  • transitionName-appear;transitionName-appear-active;transitionName-enter;transitionName-enter-active;transitionName-leave;transitionName-leave-active;
  • CSS クラスの指定:

  •   transitionName={ {         enter: ‘enter’,        enterActive: ‘enterActive’,        leave: ‘leave’,         leaveActive: ‘leaveActive’,         appear: ‘appear’,         appearActive: ‘appearActive’   } }

(2)、transitionAppear

{React.PropTypes.bool} {false}

機能: マウントアニメーションを初期化します。コンポーネントの初期マウント中に追加の移行フェーズを追加します。 通常、transitionAppear のデフォルト値は false であるため、マウントの初期化時に移行フェーズはありません。

例:

render: function() {       return (           <ReactCSSTransitionGrouptransitionName="example" transitionAppear={true} >  <h1>FadingatInitialMount</h1>           </ReactCSSTransitionGroup>       ); }

(3)、transitionEnter

{React.PropTypes.bool} {true}

関数: Enter アニメーションを無効にするために使用されます

(4)、transitionLeave

{React.PropTypes.bool} {true}

作用:用来禁止leave动画 ReactCSSTransitionGroup 会在移除你的DOM节点之前等待一个动画完成。你可以添加transitionLeave={false} 到ReactCSSTransitionGroup 来禁用这些动画。

(5)、component

{React.PropTypes.any} {‘span’}

作用:默认情况下 ReactTransitionGroup 渲染为一个 span。你可以通过提供一个 component prop 来改变这种行为. 组件不需要是一个DOM组件,它可以是任何你想要的React组件,甚至是你自己写的。

(6)、className

{ React.PropTypes.string }

作用:给当前的component设置样式类

例如:

<ReactTransitionGroupcomponent=“ul” className="example" >  ... </ReactTransitionGroup>

3、 生命周期

当子级被声明式的从其中添加或移除(就像上面的例子)时,特殊的生命周期挂钩会在它们上面被调用。

componentWillAppear(callback)

对于被初始化挂载到 CSSTransitionGroup 的组件,它和 componentDidMount() 在相同时间被调用 。它将会阻塞其它动画发生,直到callback被调用。它只会在CSS TransitionGroup 初始化渲染时被调用。

componentDidAppear()

在 传给componentWillAppear 的 回调 函数被调用后调用。

componentWillEnter(callback)

对于被添加到已存在的 CSSTransitionGroup 的组件,它和 componentDidUpdate() 在相同时间被调用 。它将会阻塞其它动画发生,直到callback被调用。它不会在 CSSTransitionGroup 初始化渲染时被调用。

componentDidEnter()

在传给 componentWillEnter 的回调函数被调用之后调用。

componentWillLeave(callback)

在子级从 ReactCSSTransitionGroup 中移除时调用。虽然子级被移除了,ReactTransitionGroup 将会保持它在DOM中,直到callback被调用。

componentDidLeave()

在willLeave callback 被调用的时候调用(与 componentWillUnmount 同一时间)。

  1. 4、原理简述

以componentWillEnter为例,伪代码如下:

componentWillEnter (callback) {    letel = ReactDOM.findDOMNode(this);    el.classList.add(styles.enter);    requestAnimationFrame(() => {           el.classList.add(styles.active);    });    el.addEventListener('transitionend', () => {        callback && callback();        el.classList.remove(styles.enter);        el.classList.remove(styles.active);    });}

在 componentWillEnter 里给 Animation 组件添加了 styles.enter 样式类,然后在浏览器下一个 tick 加入 styles.active 样式类 – 这里使用了 requestAnimationFrame,也可以使用 setTimeout,另外还监听 ‘transitionend’ 事件,transitionend 事件发生时执行回调 callback 并移除 styles.enter 与 styles.active 两个样式类

5、 注意事项

①. 一定要为ReactCSSTransitionGroup的所有子级提供 key属性。即使只渲染一个项目。React靠key来决定哪一个子级进入,离开,或者停留。

②、动画持续时间需要被同时在CSS和渲染方法里被指定。这告诉React什么时候从元素中移除动画类,并且如果它正在离开,决定何时从DOM移除元素。

③、ReactCSSTransitionGroup必须已经挂载到了DOM才能工作。为了使过渡效果应用到子级上,ReactCSSTransitionGroup必须已经挂载到了DOM或者 prop transitionAppear 必须被设置为 true。ReactCSSTransitionGroup 不能随同新项目被挂载,而是新项目应该在它内部被挂载。

6、 劣势

ReactCSSTransitionGroup的优势是非常明显的,简化代码、提高性能等,但是其劣势我们也需要了解,以在做实际项目时进行适当的取舍。

① 不兼容较老的、不支持CSS3的浏览器;

② 不支持为CSS属性之外的东西(比如滚动条位置或canvas绘画)添加动画;

③ 可控粒度不够细。CSS3动画只支持start、end、iteration三个事件,不支持对中间状态进行处理。

④ transitionEnd和animationEnd事件不稳定。

7、 V0.14动画新特性

新增属性:

transitionAppearTimeouttransitionEnterTimeouttransitionLeaveTimeout

控制动画持续时间,解决animationend transitionend 事件不稳定、时有时没有的现象,v0.15版本将彻底放弃监听animationend transitionend 事件。

官方原话是: To improve reliability, CSSTransitionGroup will no longer listen to transition events. Instead, you should specify transition durations manually using props such as transitionEnterTimeout={500}.

原理上其实是简化了,还是以componentWillEnter为例,伪代码如下:

componentWillEnter (callback) {    letel = ReactDOM.findDOMNode(this);    el.classList.add(styles.enter);    requestAnimationFrame(() => {           el.classList.add(styles.active);    });    setTimeout( () => {        callback && callback();        el.classList.remove(styles.enter);        el.classList.remove(styles.active);    }, props.transitionEnterTimeout);}

所以我们的轮播图就要改为这样实现:

<ReactCSSTransitionGrouptransitionName={this.props.transitionName} transitionEnterTimeout={300} transitionLeaveTimeout={300} >        <imgsrc={this.props.imageSrc} key={this.props.imageSrc} /></ReactCSSTransitionGroup>

(三) 间隔动画

深入了解了CSS渐变组,大家也看到了它并不是万能的,所以需要间隔动画来做辅助,或者说是第二选择。

间隔动画实现方式很简单,有两种:

1、 requestAnimationFrame

2、 setTimeout

requestAnimationFrame可以以最小的性能损耗实现最流畅的动画,它被调用的次数频繁度超出你想象。在requestAnimationFrame不支持或不可用的情况下,就要考虑降级到不那么智能的setTimeout了。

间隔动画在实现原理上其实很简单,就是周期性的触发组件的状态更新,通过在组件的render方法中加入这个状态值,组件能够在每次状态更新触发的重渲染中正确表示当前的动态阶段。

以实现元素右移100px为例,代码实现如下所示:

1、requestAnimationFrame实现

var Todo = React.createClass( getInitialState: function() { return { left: 0 }; }, componentWillUpdate: function() { requestAnimationFrame(this. resolveAnimationFrame); }, render: function() { return <divstyle={{left: this.state.left}}>This willanimate!</div>; }, resolveAnimationFrame: function() { if(this.state.left <= 100) { this.setState({ left: this.state.left + 1 }); } });

2、requestAnimationFrame实现

var Todo = React.createClass( getInitialState: function() { return { left: 0 }; }, componentWillUpdate: function() { setTimeout(this. resolveAnimationFrame, this.props.tick); }, render: function() { return <divstyle={{left: this.state.left}}>This willanimate!</div>; }, resolveAnimationFrame: function() { if(this.state.left <= 100) { this.setState({ left: this.state.left + 1 }); } });

是不是很简单呢?

大家一定会想,React也提供了我们可以直接操作DOM的接口,我还是不习惯React的写法,为什么不能像原生js那样实现动画效果呢?那么我可以明确的告诉你,React就是不允许你这么做,它就是要规避前端这种肆无忌惮的写法,规范你的代码,降低维护成本。

至于性能,这里顺便简单提一下React的渲染过程,大家可以体会下。

首次渲染时,从JSX渲染成真实DOM的大体过程如下:

1、parse过程将JSX解析成Virtual DOM,是一种抽象语法树(AST);

2、compile过程则将AST通过DOM API 编译成页面真实的DOM。

二次渲染过程如下:

1、每次生成的页面DOM渲染后,其对应的Virtual Dom也会缓存起来;

2、当JSX发生变化,,会首先根据新的JSX生成一个全新的Virtual Dom;

3、新的Virtual Dom生成后,会检测是否存在旧的Virtual Dom;

4、发现存在,则通过react diff算法比较新旧Virtual Dom之间的差异,得出一个从旧Virtual Dom转换到新Virtual Dom 的最少操作(minimum operating);

5、最后,页面旧的真实Dom,根据刚刚react diff算法得出的最少操作,通过Dom api进行节点的增、删、改,得出新的真实Dom;

大家一定在怀疑diff算法的性能,因为传统的用递归算法来比较两棵树的时间复杂度是O(n^3),真是烂到了极致,但是,React通过几个先验条件将diff的算法复杂度控制在了O(n)。下面讲一下这几个条件:

1、 只在同层级做比较

在React 的diff算法中,两个virtual dom树的比较只在同层级进行。这样,只需一遍,即可遍历整棵树。这样做,是忽略了节点的跨层移动,因为web中节点的跨层操作较少。同时我们在使用React时,也要尽量避免这样做。

示例如下:

算法计算得出的操作是:删除body的子节点p及其子节点,创建div的子节点p,创建p的子节点a。

通过react的diff算法,两个Virtual Dom 比较后,因移动节点不同级,因此不做移动操作,而是直接删除重建。

2、 基于组件比较

在React 的diff算法中,virtual dom树的比较只在同组件进行。对于不同组件,即使结构相似,也不进行比较,而是直接执行删除+重建操作。这样做,是强化组件的概念,因为正常情况下,不同组件的页面结构是不一样的。

示例如下:

算法计算得出的操作是:删除body的子节点div及其子节点,创建body子节点div及其子节点p和子节点input。

如使用传统的diff算法,会计算出只需删除div的子节点a,并创建div子节点input。

而采用react的diff算法,两个Virtual Dom 比较时,发现绿框内结构为不同的组件,则绿框内容不做比较,直接删除重建。

3、节点使用唯一属性key

在React 的diff算法中,virtual dom树的节点可以通过key标识其身份,提高节点同级同组移动时的性能。增加身份标识来作为节点是否需要修改的一个条件。

算法计算得出的操作只需要:移动div节点到最后即可。

若使用传统的diff算法,判断body第一个子节点,旧的为div,新的为p,节点不一样,则删除div节点,新增插入p节点。之后节点操作类似,因此总的需要进行三次节点删除和新增。

而采用react的diff算法,因为节点多了key来标识,两个Virtual Dom 比较时,发现level1下的三个节点其实是一样的(key=1、key=2、key=3)。

相信通过上面的介绍,大家对React有了更进一步的了解。

4. 概要

1. React を使用してアニメーション効果を実現する場合は、まず CSS グラデーション グループを検討してください。それが機能しない場合は、インターバル レンダリングの使用を検討してください。

2. カスタマイズする必要がある機能が多い場合は、React に付属の CSSTransitionGroup プラグインを使用しないことをお勧めします。たとえば、アニメーションの最後に onEnd コールバックを渡したいとします。React のソース コードを変更すると、CSSTransitionGroup は、transitionGroup に依存し、transitionGroup は他のプラグインとメソッドに依存します。変更するのは困難ですが、問題を修正するのは簡単です。私は一連の CSSTransitionGroup プラグインを自分で実装しました。これについては、今後さらに共有する予定です。

お読みいただきありがとうございます

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。