react 交互
Microinteractions guide a user through your application. They reinforce your user experience and provide delight.
微交互引導用戶完成您的應用程序。 它們可以增強您的用戶體驗并帶來愉悅感。
You may have seen some of the slick examples of microinteractions on Dribble or CodePen. But do you know how to build your own library of similar UI widgets?
您可能已經看到了Dribble或CodePen上的一些微交互示例。 但是,您知道如何構建自己的類似UI小部件的庫嗎?
In this article, I’ll focus on animated microinteractions using React, Facebook’s popular, component-oriented UI framework. I’ll build three interactions for a searchbox:
在本文中,我將重點介紹使用Facebook流行的面向組件的UI框架React的動畫微交互。 我將為搜索框構建三個交互:
- open and close the text box 打開和關閉文本框
- move to the top of the screen 移到屏幕頂部
- shake (indicating an error) 搖動(指示錯誤)
I’ll use a few different implementations:
我將使用一些不同的實現:
CSS transitions
CSS過渡
react-motion
React運動
react-animations
React動畫
Here’s a live demo and the code that powers it.
這是一個現場演示以及支持它的代碼 。
This is one of several posts about Higher Order (HOC) and Stateless Functional Components. The first post is about code reuse in React and React Native, via these techniques.
這是有關高階(HOC)和無狀態功能組件的幾篇文章之一。 第一篇文章是關于通過這些技術在React和React Native中的代碼重用。
什么是微交互? (What is a Microinteraction?)
Dan Saffer (who wrote the book) gives us this definition: “Microinteractions are contained product moments that revolve around a single use case — they have one main task.”
Dan Saffer (寫這本書的人)給了我們這樣的定義 :“微交互包含圍繞一個用例的產品時刻,它們具有一項主要任務。”
Examples might be clearer. Some microinteractions are everywhere, such as a cursor change when hovering over a link or the vibration of your phone when you switch to silent mode. Others, such as an item being added to a shopping cart, aren’t so common (yet).
例子可能更清楚。 到處都有一些微交互,例如,將鼠標懸停在鏈接上時會更改光標,或者切換到靜音模式時手機會振動。 其他事物(例如,添加到購物車中的商品)并不普遍(尚未)。
我為什么要關心微交互? (Why should I care about Microinteractions?)
Microinteractions can provide feedback and make your application memorable. When users have so many app choices, better microinteractions might be the clichéd better mousetrap you should build.
微交互可以提供反饋并使您的應用程序令人難忘。 當用戶有如此多的應用程序選擇時,更好的微交互可能是您應該構建的老套的更好的捕鼠器。
But I am not a UX designer. So I suggest reading Nick Babich’s post about microinteractions.
但是我不是用戶體驗設計師。 因此,我建議閱讀Nick Babich的有關微交互的文章 。
入門 (Getting Started)
I’ll use create-react-app to bootstrap a React application, but any React setup method will work. Also, I like Material-UI, so I’ll import that too. (This choice is arbitrary — you could use another widget library or manually style your elements.)
我將使用create-react-app引導React應用程序,但是任何React設置方法都可以使用。 另外,我喜歡Material-UI ,所以我也將其導入。 (此選擇是任意的,您可以使用其他小部件庫或手動設置元素樣式。)
create-react-app search-box-animation
cd search-box-animation
npm install --save material-ui react-tap-event-plugin
組件:一個簡單的搜索框 (The Component: a Simple Search Box)
I’ll create a simple search box. It will comprise two elements: a search icon button and a text box. I’ll create a stateless functional component for the search box. (Stateless functional components are functions that render React components and do not maintain state, i.e. use setState
. You can learn more in this tutorial or my previous post.)
我將創建一個簡單的搜索框。 它包含兩個元素:一個搜索圖標按鈕和一個文本框。 我將為搜索框創建一個無狀態的功能組件。 (無狀態功能組件是呈現React組件并且不維護狀態的函數,即使用setState
。您可以在本教程或我之前的文章中了解更多信息 。)
SearchBox.js
SearchBox.js
import React from 'react';
import {TextField, IconButton} from 'material-ui'
import SearchIcon from 'material-ui/svg-icons/action/search';
const SearchBox = ({isOpen, onClick}) => {const baseStyles = {open: {width: 300,},closed: {width: 0,},smallIcon: {width: 30,height: 30},icon: {width: 40,height: 40,padding: 5,top: 10},frame: {border: 'solid 1px black',borderRadius: 5}};
const textStyle = isOpen ? baseStyles.open : baseStyles.closed;
const divStyle = Object.assign({}, textStyle, baseStyles.frame);divStyle.width += baseStyles.icon.width + 5;
return (<div style={divStyle}><IconButton iconStyle={baseStyles.smallIcon} style={baseStyles.icon} onClick={() => onClick()}><SearchIcon /></IconButton><TextField name='search' style={textStyle}/></div>);
};
export default SearchBox;
(I’ll use the onClick
callback later.)
(稍后我將使用onClick
回調。)
The isOpen
prop sets the SearchBox
open or closed rendering.
isOpen
道具將SearchBox
設置為打開或關閉渲染。
使用高階分量來分離問題 (Using Higher Order Components to Separate Concerns)
I could change SearchBox
to a regular component and add code that would open and close the text box when clicked, for example.
例如,我可以將SearchBox
更改為常規組件,并添加代碼,這些代碼將在單擊時打開和關閉文本框。
But I prefer to separate the animation from the core purpose of the search box. The search box shows/captures a query value and submits this query to some other controller. This is a subjective design decision, but it has practical benefits: I can reuse the microinteraction logic with another user input component.
但是我更喜歡將動畫與搜索框的核心目的分開。 搜索框顯示/捕獲查詢值,并將此查詢提交給其他控制器。 這是一個主觀的設計決定,但是它具有實際的好處:我可以將微交互邏輯與另一個用戶輸入組件一起重用。
Higher Order Components (HOC) are functions that return a new component. This component wraps a component(s) and adds functionality. I will create an HOC to add the open/close behavior to the SearchBox
.
高階組件 (HOC)是返回新組件的函數。 該組件包裝組件并添加功能。 我將創建一個HOC來將打開/關閉行為添加到SearchBox
。
Create expanding-animation.js
創建expanding-animation.js
import React, {Component} from 'react';
const makeExpanding = (Target) => {return class extends Component {constructor(props) {super(props);this.state = {isOpen: false};}onClick = () => {this.setState({isOpen: !this.state.isOpen});};render() {return (<Target {...this.props}isOpen={this.state.isOpen}onClick={this.onClick}/>);}}
};
export default makeExpanding;
Update App.js
as follows:
如下更新App.js
:
import React, {Component} from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';// (Make material-ui happy)
// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();import SearchBox from './SearchBox'
import makeExpanding from './expanding-animation';const ExpandingSearchBox = makeExpanding(SearchBox);class App extends Component {render() {//https://css-tricks.com/quick-css-trick-how-to-center-an-object-exactly-in-the-center/const style = {position: 'fixed',top: '50%',left: '50%',transform: 'translate(-50%, -50%)',};return (<MuiThemeProvider><div style={style}><ExpandingSearchBox/></div></MuiThemeProvider>);}
}
export default App;
If you run npm start
, you’ll have a search icon that you can click to open and close the text box.
如果您運行npm start
,則會有一個搜索圖標,您可以單擊該圖標以打開和關閉文本框。
It works, but the opening and closing is jarring. An animation can smooth the effect.
它有效,但是打開和關閉很麻煩。 動畫可以使效果平滑。
動畫制作 (Animations)
There are three general approaches to animations.
動畫有三種通用方法。
- CSS transitions CSS過渡
- CSS animations CSS動畫
- rapid and repeated rendering of an element to simulate motion (manual key framing) 快速重復渲染元素以模擬運動(手動關鍵幀)
CSS transitions change a property value (like width) over some time duration. The change doesn’t have to be linear; you can specify functions for changing the values.
CSS過渡會在一段時間內更改屬性值(如width)。 變化不必是線性的。 您可以指定用于更改值的函數。
CSS animations change the style for an element (like size, color, and position). Each incremental style is a keyframe. You create a keyframe series to achieve a desired effect.
CSS動畫會更改元素的樣式(如大小,顏色和位置)。 每個增量樣式都是一個關鍵幀。 您創建關鍵幀系列以實現所需的效果。
Both CSS tactics repeatedly render elements to simulate motion. You can do the calculations yourself, i.e. option (3). Several Javascript animation frameworks use this approach, managing the calculations. (I’ll use react-motion in a later example.)
兩種CSS策略都反復渲染元素以模擬運動。 您可以自己進行計算,即選項(3)。 一些Javascript動畫框架使用這種方法來管理計算。 (我將在后面的示例中使用react-motion。)
I will use all these techniques in the examples below, but I’ll start with CSS transitions.
我將在下面的示例中使用所有這些技術,但是我將從CSS過渡開始。
展開搜索框 (Expanding the Search Box)
The expanding text box animation needs one CSS property: transition
擴展的文本框動畫需要一個CSS屬性: transition
Change expanding-animation.js
as follows,
如下更改expanding-animation.js
,
import React, {Component} from 'react';
const animationStyle = {transition: 'width 0.75s cubic-bezier(0.000, 0.795, 0.000, 1.000)'
};
const makeExpanding = (Target) => {return class extends Component {constructor(props) {super(props);this.state = {isOpen: false};}onClick = () => {this.setState({isOpen: !this.state.isOpen});};render() {return (<Target {...this.props}isOpen={this.state.isOpen}onClick={this.onClick}additionalStyles={{text: animationStyle, frame: animationStyle}}/>);}}
};
export default makeExpanding;
Looking at the change in line 21, additionalStyles
, SearchBox
will merge this style with it’s existing styles in line 29 and 31 below. (I’ll return to the transition CSS property in line 2 in a moment.)
看著在第21行的變化, additionalStyles
, SearchBox
將在第29行及以下31合并這種風格與它的現有樣式。 (稍后我將在第2行中返回Transition CSS屬性。)
Update SearchBox.js
更新SearchBox.js
import React from 'react';
import {TextField, IconButton} from 'material-ui'
import SearchIcon from 'material-ui/svg-icons/action/search';
const SearchBox = ({isOpen, onClick, additionalStyles}) => {const baseStyles = {open: {width: 300,},closed: {width: 0,},smallIcon: {width: 30,height: 30},icon: {width: 40,height: 40,padding: 5,top: 10},frame: {border: 'solid 1px black',borderRadius: 5}};let textStyle = isOpen ? baseStyles.open : baseStyles.closed;textStyle = Object.assign(textStyle, additionalStyles ? additionalStyles.text : {});const divStyle = Object.assign({}, textStyle, baseStyles.frame, additionalStyles ? additionalStyles.frame : {});divStyle.width += baseStyles.icon.width + 5;return (<div style={divStyle}><IconButton iconStyle={baseStyles.smallIcon} style={baseStyles.icon} onClick={() => onClick()}><SearchIcon /></IconButton><TextField name='search' style={textStyle}/></div>);
};
export default SearchBox;
With the styles merged, the animation will take effect.
合并樣式后,動畫將生效。
The result is a smooth expansion of the text box width, giving the appearance it opens. The CSS transition
property controls this (from line 2 in expanding-animation.js
).
結果是文本框寬度的平滑擴展,使文本框具有打開的外觀。 CSS transition
屬性對此進行控制(從expanding-animation.js
第2行開始)。
transition: 'width 0.75s cubic-bezier(0.000, 0.795, 0.000, 1.000)'
I encourage you to read the documentation for the CSS transition property, as there are a variety of options. In the example, there are three parameters:
我建議您閱讀CSS過渡屬性的文檔 ,因為有很多選擇。 在示例中,有三個參數:
property to change:
width
要更改的屬性:
width
duration of transition:
0.75s
過渡時間:
0.75s
function to control timing:
cubic-bezier(0.000, 0.795, 0.000, 1.000)’
控制時序的功能:
cubic-bezier(0.000, 0.795, 0.000, 1.000)'
While I chose cubic-bezier
as the function, linear
or ease
are among other options. There are interactive tools that help you select these values, such as this cubic-bezier builder.
當我選擇cubic-bezier
作為函數時, linear
或ease
是其他選擇。 有一些交互式工具可以幫助您選擇這些值,例如: cubic-bezier builder 。
移動搜索框 (Moving the Search Box)
Check out the following concept animation I found on Dribble:
看看我在Dribble上發現的以下概念動畫:
There are multiple elements in the interaction; but I’d like to focus on the movement of the search box to the top of the screen.
交互中包含多個元素; 但我想著重將搜索框移動到屏幕頂部。
I can move my humble search box with a CSS transition. Create a new HOC, move-up-animation.js
我可以通過CSS轉換來移動不起眼的搜索框。 創建一個新的HOC, move-up-animation.js
import React, {Component} from 'react';
const animationStyle = {transform: 'translateY(-150px)',transition: 'transform 1s ease'
};
const makeMoveUp = (Target) => {return class extends Component {constructor(props) {super(props);this.state = {moveTop: false};}onClick = () => {this.setState({moveTop: !this.state.moveTop});};render() {return (<Target isOpen={true}onClick={this.onClick}additionalStyles={{text: {}, frame: this.state.moveTop ? animationStyle : {}}}/>);}}
};
export default makeMoveUp;
view rawmove-up-animation.js hosted with ? by GitHub
This is like the makeExpanding
HOC function, except does a translation (move up). Also, the animation style applies only to the outer frame (div
).
就像makeExpanding
HOC函數一樣,只是進行翻譯(向上移動)。 此外,動畫樣式僅適用于外框( div
)。
Update App.js
,
更新App.js
,
import React, {Component} from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';// (Make material-ui happy)
// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();import SearchBox from './SearchBox'
import makeMoveUp from './move-up-animation';
const MoveUpSearchBox = makeMoveUp(SearchBox);
class App extends Component {render() {//https://css-tricks.com/quick-css-trick-how-to-center-an-object-exactly-in-the-center/const style = {position: 'fixed',top: '50%',left: '50%',transform: 'translate(-50%, -50%)',};return (<MuiThemeProvider><div style={style}><MoveUpSearchBox/></div></MuiThemeProvider>);}
}
export default App;
view rawApp.js-2 hosted with ? by GitHub
and you should see
你應該看到
Perhaps you want a bouncy effect. You could use react-motion. It is a popular React library which uses spring dynamics to control animations. (A good introduction, by Nash Vail, is here.)
也許您想要有彈性的效果。 您可以使用react-motion 。 這是一個流行的React庫,它使用彈簧動力學來控制動畫。 ( Nash Vail的一個很好的介紹在這里 。)
npm install --save react-motion
Create spring-up-animation.js
創建spring-up-animation.js
import React, {Component} from 'react';
import {Motion, spring, presets} from 'react-motion'
const makeSpringUp = (Target) => {return class extends Component {constructor(props) {super(props);this.state = {moveTop: false};}onClick = () => {this.setState({moveTop: !this.state.moveTop});};render() {const style = {translateY: this.state.moveTop ? spring(-150, presets.wobbly) : spring(0)};return (<Motion style={style}>{({translateY}) => (<Target isOpen={true}onClick={this.onClick}additionalStyles={{text: {},frame: {transform: `translateY(${translateY}px)`}}}/>)}</Motion>);}}
};
export default makeSpringUp;
view rawspring-up-animation.js hosted with ? by GitHub
As this isn’t a react-motion tutorial, I will briefly summarize how this works. React-motion wraps the animated component, Target
, with its own component, Motion
. (There are other react-motion components, such as TransitionMotion
and Staggered Motion
.)
由于這不是React運動教程,因此我將簡要概述其工作原理。 React-motion將動畫組件Target
封裝為其自己的Motion
組件。 (還有其他React運動組件,例如TransitionMotion
和Staggered Motion
。)
React-motion interpolates, using spring dynamics, a series of interim values. It provides the values to the animated component as a style. This style determines the visual transition in the animation.
React運動使用彈簧動力學插值一系列中間值。 它將值作為樣式提供給動畫組件。 此樣式確定動畫中的視覺過渡。
The image below shows the result (with a wobbly spring to highlight the effect).
下圖顯示了結果(帶有顫抖的彈簧以突出顯示效果)。
You could use react-motion for a range of effects. For example, you could change the text box to expand like a spring.
您可以將react-motion用于多種效果。 例如,您可以更改文本框使其像彈簧一樣展開。
(spring-up-animation.js
and move-up-animation.js
have the same onClick
state logic, so I refactored the common parts. Details are here.)
( spring-up-animation.js
和move-up-animation.js
具有相同的onClick
狀態邏輯,因此我重構了公共部分。詳細信息在這里 。)
搖動搜索框 (Shaking the Search Box)
I want to provide feedback to the user about erroneous queries. You could use error messages, but I’d like to do something more whimsical: shake the search box.
我想向用戶提供有關錯誤查詢的反饋。 您可以使用錯誤消息,但我想做一些更怪異的操作:搖動搜索框。
I could use react-motion, but I’d like to look at another technique: keyframe animation.
我可以使用react-motion,但我想看看另一種技術:關鍵幀動畫。
React-animations is a React library for keyframe animations. It injects CSS keyframes into a DOM style sheet. (The other examples have only used inline styles.)
React-animations是一個用于關鍵幀動畫的React庫。 它將CSS關鍵幀注入到DOM樣式表中。 (其他示例僅使用內聯樣式。)
npm install --save react-animations
I also need a library, like Radium or Aphrodite, to handle the CSS style sheet injection. I’ve chosen Aphrodite, as I’ve used it before.
我還需要一個庫,例如Radium或Aphrodite ,來處理CSS樣式表注入。 我選擇了Aphrodite,就像我之前使用過的一樣。
npm install --save aphrodite
Create another HOC, shake-animation.js
創建另一個HOC, shake-animation.js
import React, {Component} from 'react';
import {headShake} from 'react-animations';
import {StyleSheet, css} from 'aphrodite';
const styles = StyleSheet.create({headShake: {animationName: headShake,animationDuration: '1s'}
});
const makeValidationErrorAnimation = (Target) => {return class extends Component {constructor(props) {super(props);this.state = {shouldShake: false};}onClick = () => {this.setState({shouldShake: true}, () => {const self = this;setTimeout(() => self.setState({shouldShake: false}), 1000);});};render() {return (<Target isOpen={true}onClick={this.onClick}additionalStyles={{text: {}, frame: {}}}frameClass={this.state.shouldShake ? css(styles.headShake) : ''}/>);}}
};
export default makeValidationErrorAnimation;
There are a few key sections. Line 4 uses Aphrodite to create the style sheet for the react-animations effect, head-shake
. Line 29 sets the CSS class for the animation on Target
. (This requires a tweak to SearchBox
to use the CSS class. Look at the use of frameClass
in the source of SearchBox.js
.) The onClick
handler on line 17 is more complicated.
有幾個關鍵部分。 第4行使用Aphrodite創建用于React動畫效果的樣式表head-shake
。 第29行在Target
上設置動畫CSS類。 (這需要對SearchBox
進行調整才能使用CSS類。請在SearchBox.js
的源代碼中查看frameClass
的用法。)第17行的onClick
處理程序更加復雜。
重新啟動動畫 (Restarting an Animation)
I’d like to do the ‘head shake’ on each validation error (or whatever trigger is used). But since the animation is a CSS class, I can’t simply set the same class again; it would have no effect. This CSS Tricks post outlines a few options. The simplest is a timeout that removes the CSS animation class. When you add it again (for a new event), you’ll see the ‘head shake’.
我想對每個驗證錯誤(或使用任何觸發器)進行“搖頭”操作。 但是由于動畫是CSS類,所以我不能簡單地再次設置相同的類。 它不會有任何效果。 此CSS技巧文章概述了一些選項。 最簡單的方法是刪除CSS動畫類的超時。 再次添加(針對新事件)時,您會看到“搖頭”。
放在一起:組成一個復雜的組件 (Putting It Together: Composing a Complex Component)
I’ve created several HOCs for different animations. But you can also chain the HOCs to create a compound component. It will open the text box when clicked and shake on erroneous input.
我為不同的動畫創建了多個HOC。 但是,您也可以鏈接HOC以創建復合組件。 單擊它會打開文本框,并搖晃錯誤的輸入。
First, you’ll need to make a few changes toSearchBox
首先,您需要對SearchBox
進行一些更改
import React from 'react';
import {TextField, IconButton} from 'material-ui'
import SearchIcon from 'material-ui/svg-icons/action/search';
const baseStyles = {open: {width: 300,},closed: {width: 0,},smallIcon: {width: 30,height: 30},icon: {width: 40,height: 40,padding: 5,top: 10},frame: {border: 'solid 1px black',borderRadius: 5}
};
const SearchBox = ({isOpen, query, onClick, onSubmit, onQueryUpdate, additionalStyles, frameClass}) => {const handleKeyDown = (event) => {const ENTER_KEY = 13;if (event.keyCode === ENTER_KEY) {event.preventDefault();onSubmit();}};let textStyle = isOpen ? baseStyles.open : baseStyles.closed;textStyle = Object.assign(textStyle, additionalStyles ? additionalStyles.text : {});const divStyle = Object.assign({}, textStyle, baseStyles.frame, additionalStyles ? additionalStyles.frame : {});divStyle.width += baseStyles.icon.width + 5;return (<div style={divStyle} className={frameClass ? frameClass : ''}><IconButton iconStyle={baseStyles.smallIcon} style={baseStyles.icon} onClick={() => onClick()}><SearchIcon /></IconButton><TextField name='search'style={textStyle}value={query}onKeyDown={handleKeyDown}onChange={(event, value) => onQueryUpdate(value)}/></div>);
};
export default SearchBox;
SearchBox
is now a controlled component (fancy term for using React to manage the text box’s input value). It also provides a callback, onSubmit
, for submitting the search query (when a user presses the Enter key).
SearchBox
現在是一個受控組件 (使用React使用該術語來管理文本框的輸入值)。 它還提供了一個回調onSubmit
,用于提交搜索查詢(當用戶按下Enter鍵時)。
You also need to change shake-animation.js
. Clicking the search icon should not cause the shake. Instead, I want another component to determine when to ‘shake’. This separates the validation logic from code that controls the animation.
您還需要更改shake-animation.js
。 單擊搜索圖標不應引起抖動。 相反,我希望另一個組件來確定何時“搖動”。 這將驗證邏輯與控制動畫的代碼分開。
startShake
is a flag to reset the animation. But this is an implementation detail. It should be encapsulated, as internal state, in the makeShakeAnimation
HOC.
startShake
是用于重置動畫的標志。 但這是一個實現細節。 應該將其作為內部狀態封裝在makeShakeAnimation
HOC中。
import React, {Component} from 'react';
import {headShake} from 'react-animations';
import {StyleSheet, css} from 'aphrodite';
const styles = StyleSheet.create({headShake: {animationName: headShake,animationDuration: '1s'}
});
const makeShakeAnimation = (Target) => {return class extends Component {constructor(props) {super(props);this.state = {startShake: props.shouldShake};}componentWillReceiveProps(nextProps) {this.setState({startShake: nextProps.shouldShake}, () => {const self = this;setTimeout(() => self.setState({startShake: false}), 1000);});//https://css-tricks.com/restart-css-animation/ for discussion on restart}render() {return (<Target {...this.props}frameClass={this.state.startShake ? css(styles.headShake) : ''}/>);}}
};
export default makeShakeAnimation;
startShake
is dependent on shouldShake
. I can use componentWillReceiveProps to respond to prop changes. (It’s parent, the validation component, provides these props.) So I moved the previous onClick
logic to componentWillReceiveProps
.
startShake
取決于shouldShake
。 我可以使用componentWillReceiveProps來響應道具更改。 (它的父級(驗證組件)提供了這些道具。)因此,我將先前的onClick
邏輯移至componentWillReceiveProps
。
The change in line 27, {...this.props}
, passes all props to the wrapped component, Target
. (I need to similarly change the render
method in expanding-animation.js
. The details are here.)
第27行的更改{...this.props}
將所有props傳遞給包裝的組件Target
。 (我需要類似地更改expanding-animation.js
的render
方法。詳細信息在這里 。)
I can now add a component that will control when to shake.
現在,我可以添加一個控件來控制何時搖動。
Create search-box-controller.js
創建search-box-controller.js
import React, {Component} from 'react';import makeExpanding from './expanding-animation';
import makeShakingAnimation from './shake-animation';const makeAnimatedValidationSearchBox = (Target) => {const WrappedComponent = makeShakingAnimation(makeExpanding(Target));return class extends Component {constructor(props) {super(props);this.state = {query: '', hasError: false};}onQueryUpdate = (value) => {this.setState({query: value, hasError:false});};onSubmit = () => {this.setState({hasError: true});};render() {return (<WrappedComponentonQueryUpdate={this.onQueryUpdate}query={this.state.query}onSubmit={this.onSubmit}shouldShake={this.state.hasError}/>);}}
};export default makeAnimatedValidationSearchBox;
This is another HOC. It does not have visual elements, but it controls the logical behavior of the wrapped component. (Dan Abramov has a good post explaining such separation.) In this case, all queries as erroneous, but in a real application, I’d validate queries and connect to APIs.
這是另一個HOC。 它沒有視覺元素,但是它控制了包裝組件的邏輯行為。 ( Dan Abramov有一篇很好的文章解釋了這種分離。)在這種情況下,所有查詢都是錯誤的,但是在實際應用程序中,我將驗證查詢并連接到API。
Lastly, I want to highlight that makeAnimatedValidationSearchBox
is an HOC that chains two other HOCs.
最后,我想強調一下, makeAnimatedValidationSearchBox
是一個鏈接其他兩個HOC的HOC。
const WrappedComponent =makeShakingAnimation(makeExpanding(Target));
Another small update toApp.js
App.js
另一個小更新
import React, {Component} from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';// (Make material-ui happy)
// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();
import SearchBox from './SearchBox'import makeAnimatedValidationSearchBox from './search-box-controller';
const AnimatedSearchBox = makeAnimatedValidationSearchBox(SearchBox);class App extends Component {render() {//https://css-tricks.com/quick-css-trick-how-to-center-an-object-exactly-in-the-center/const style = {position: 'fixed',top: '50%',left: '50%',transform: 'translate(-50%, -50%)',};return (<MuiThemeProvider><div style={style}><AnimatedSearchBox/></div></MuiThemeProvider>);}
}
export default App;
(Line 12 uses the new HOC)
(第12行使用新的HOC)
and execute run npm start
并執行run npm start
I’ve created a compound component that uses multiple microinteractions. They are reusable and discrete.
我創建了使用多個微交互的復合組件。 它們是可重用和離散的。
結語 (Wrapping Up)
I’ve sampled each of the approaches: CSS transitions, react-motion and react-animations. I wish you could pick one approach, but it’s hard to contort a single approach for all use cases. Thankfully, you can mix-and-match libraries and techniques. And you can encapsulate the details in reusable HOCs.
我已經對每種方法進行了采樣:CSS過渡,react-motion和react-animations。 我希望您可以選擇一種方法,但是很難針對所有用例扭曲一種方法。 值得慶幸的是,您可以混合和匹配庫和技術。 您可以將細節封裝在可重用的HOC中。
You might want to check out libraries such recompose, that make HOC creation easier.
您可能想檢出諸如recompose之類的庫,這些庫使HOC的創建更加容易。
The GitHub repo for this project is here.
這個項目的GitHub倉庫在這里 。
Please ? this post and follow me for future stories. Thanks for reading.
請?這篇文章,并跟隨我的未來故事。 謝謝閱讀。
翻譯自: https://www.freecodecamp.org/news/how-to-build-animated-microinteractions-in-react-aab1cb9fe7c8/
react 交互