共计 11667 个字符,预计需要花费 30 分钟才能阅读完成。
一、React动画Mixin-ReactTransitionGroup
常见的动画分为几大类:CSS动画,JS动画,rAF动画,SVG动画等等
CSS动画:就是给组件的每一个状态先定义好指定的动画类,再在相应的状态加上该类。
JS动画:主要通过setTimeout()和setInterval()通过控制style对应的样式来实现动画,缺点动画时间不精确。
rAF动画:通过requestAnimationFrame()来实现动画,用法和setTimeout差不多,唯一区别是性能做过优化,优点动画时间精确。
此外以上缺点:都有兼容性的问题。
下面举例,如何在react中使用动画。
栗子1:CSS动画
<script src="../JS/react-0.14.7/build/react-with-addons.js" type="text/javascript" charset="utf-8"></script>
<script src="../JS/react-0.14.7/build/react-dom.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
.example-enter {
opacity: 0.01;
}
.example-enter.example-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
.example-leave {
opacity: 1;
}
.example-leave.example-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
</style>
<div id="app"></div>
<script type="text/babel">
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var TodoList = React.createClass({
getInitialState: function() {
return {items: ['hello', 'world', 'click', 'me']};
},
handleAdd: function() {
var newItems = this.state.items.concat([prompt('Enter some text')]);
this.setState({items: newItems});
},
handleRemove: function(i) {
var newItems = this.state.items;
newItems.splice(i, 1);
this.setState({
items: newItems
});
},
render: function() {
var items = this.state.items.map(function(item, i) {
return (
<div key={item} ref={item} onClick={this.handleRemove.bind(this, i)}>
{item}
</div>
);
}.bind(this));
return (
<div>
<button onClick={this.handleAdd}>Add Item</button>
<ReactCSSTransitionGroup
ref = "example"
transitionName="example"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}
transitionAppear={true}
transitionAppearTimeout={500}
>
{items}
</ReactCSSTransitionGroup>
</div>
);
}
});
ReactDOM.render(<TodoList/>, document.getElementById('app'));
</script
栗子2:JS动画
首先,先定义样式
body {
background: #fff;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 15px;
line-height: 1.7;
margin: 0;
padding: 30px;
}
a {
color: #4183c4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
code {
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 3px;
font-family: "Bitstream Vera Sans Mono", Consolas, Courier, monospace;
font-size: 12px;
margin: 0 2px;
padding: 0px 5px;
}
h1, h2, h3, h4 {
font-weight: bold;
margin: 0 0 15px;
padding: 0;
}
h1 {
border-bottom: 1px solid #ddd;
font-size: 2.5em;
font-weight: bold;
margin: 0 0 15px;
padding: 0;
}
h2 {
border-bottom: 1px solid #eee;
font-size: 2em;
}
h3 {
font-size: 1.5em;
}
h4 {
font-size: 1.2em;
}
p, ul {
margin: 15px 0;
}
ul {
padding-left: 30px;
}
.exampleT-enter,
.exampleT-leave {
-webkit-transition: all .25s;
transition: all .25s;
}
.exampleT-enter,
.exampleT-leave.exampleT-leave-active {
opacity: 0.01;
}
.exampleT-leave.exampleT-leave-active {
margin-left: -128px;
}
.exampleT-enter {
margin-left: 128px;
}
.exampleT-enter.exampleT-enter-active,
.exampleT-leave {
margin-left: 0;
opacity: 1;
}
.animateExample {
display: block;
height: 128px;
position: relative;
width: 384px;
}
.animateItem {
color: white;
font-size: 36px;
font-weight: bold;
height: 128px;
line-height: 128px;
position: absolute;
text-align: center;
-webkit-transition: all .25s; /* TODO: make this a move animation */
transition: all .25s; /* TODO: make this a move animation */
width: 128px;
}
接着
<script type="text/babel">
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
var INTERVAL = 2000;
var AnimateDemo = React.createClass({
getInitialState: function() {
return {current: 0};
},
componentDidMount: function() {
this.interval = setInterval(this.tick, INTERVAL);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
tick: function() {
this.setState({current: this.state.current + 1});
},
render: function() {
var children = [];
var pos = 0;
var colors = ['red', 'gray', 'blue'];
for (var i = this.state.current; i < this.state.current + colors.length; i++) {
var style = {
left: pos * 128,
background: colors[i % colors.length]
};
pos++;
children.push(<div key={i} className="animateItem" style={style}>{i}</div>);
}
return (
<CSSTransitionGroup
className="animateExample"
transitionEnterTimeout={250}
transitionLeaveTimeout={250}
transitionName="exampleT">
{children}
</CSSTransitionGroup>
);
}
});
ReactDOM.render(
<AnimateDemo />,
document.getElementById('app2')
);
</script>
上面栗子中使用的setInterval,使用setTimeout也行,如下改动;
componentDidMount:function(){
this.interval = setTimeout(this.tick, INTERVAL);
},
componentWillUnmount: function() {
clearTimeout(this.interval);
},
componentDidUpdate:function(){
this.interval = setTimeout(this.tick, INTERVAL);
},
栗子3:rAF动画
<script type="text/babel">
var CSSTransitionGroup = React.addons.CSSTransitionGroup;
var INTERVAL = 2000;
var AnimateDemo = React.createClass({
getInitialState: function() {
return {current: 0};
},
componentDidMount:function(){
if(this.props.current){
requestAnimationFrame(this.tick);
}
},
componentDidUpdate:function(){
if(this.state.current < this.props.current){
requestAnimationFrame(this.tick);
}
},
tick:function() {
this.setState({
current: this.state.current + 1
});
},
render: function() {
var children = [];
var pos = 0;
var colors = ['red', 'gray', 'blue'];
for (var i = this.state.current; i < this.state.current + colors.length; i++) {
var style = {
left: pos * 128,
background: colors[i % colors.length]
};
pos++;
children.push(<div key={i} className="animateItem" style={style}>{i}</div>);
}
return (
<CSSTransitionGroup
className="animateExample"
transitionEnterTimeout={250}
transitionLeaveTimeout={250}
transitionName="exampleT">
{children}
</CSSTransitionGroup>
);
}
});
var props={
current:100
};
ReactDOM.render(
<AnimateDemo {...props} />,
document.getElementById('dong')
);
</script>
制定类
可以为你的每一步过渡使用制定类名字。代理传递一个字符串到transitionName,你可以传递一个含有enter
或者leave
类名的对象,或者一个含有 enter
, enter-active
, leave-active
, 和 leave
类名的对象。只要提供了enter 和 leave 的类,enter-active 和 leave-active 类会被决定为后缀’-active’ 到类名的尾部。这里是使用制定类的例子:
//jsx中
<ReactCSSTransitionGroup
transitionName={ {
enter: 'enter',
enterActive: 'enterActive',
leave: 'leave',
leaveActive: 'leaveActive',
appear: 'appear',
appearActive: 'appearActive'
} }>
{item}
</ReactCSSTransitionGroup>
<ReactCSSTransitionGroup
transitionName={ {
enter: 'enter',
leave: 'leave',
appear: 'appear'
} }>
{item2}
</ReactCSSTransitionGroup
在CSS样式中就能对应的使用
.enter{
margin-left:100px;
}
.enterActive{
margin-left:0px;
transition: margin 500ms ease-in;
}
...
禁用动画
如果你想,你可以禁用 enter
或者 leave
动画。例如,有时你可能想要一个 enter
动画,不要 leave
动画,但是 ReactCSSTransitionGroup
会在移除你的DOM节点之前等待一个动画完成。你可以添加transitionEnter={false}
或者 transitionLeave={false}
props 到 ReactCSSTransitionGroup
来禁用这些动画。
底层 API: ReactTransitionGroup
ReactTransitionGroup
是动画的基础。它通过 require('react-addons-transition-group')
访问。当子级被声明式的从其中添加或移除(就像上面的例子)时,特殊的生命周期挂钩会在它们上面被调用。
componentWillAppear(callback)
对于被初始化挂载到 TransitionGroup
的组件,它和 componentDidMount()
在相同时间被调用 。它将会阻塞其它动画发生,直到callback
被调用。它只会在 TransitionGroup
初始化渲染时被调用。
componentDidAppear()
在 传给componentWillAppear
的 回调
函数被调用后调用。
componentWillEnter(callback)
对于被添加到已存在的 TransitionGroup
的组件,它和 componentDidMount()
在相同时间被调用 。它将会阻塞其它动画发生,直到callback
被调用。它不会在 TransitionGroup
初始化渲染时被调用。
componentDidEnter()
在传给 componentWillEnter
的回调
函数被调用之后调用。
componentWillLeave(callback)
在子级从 ReactTransitionGroup
中移除时调用。虽然子级被移除了,ReactTransitionGroup
将会保持它在DOM中,直到callback
被调用。
componentDidLeave()
在willLeave
callback
被调用的时候调用(与 componentWillUnmount
同一时间)。
二、React双向绑定Mixin-ReactLink
在React里,数据单向流动: 从拥有者到子级。这是因为数据只单向流动the Von Neumann model of computing。你可以把它想象为 “单向数据绑定”。
然而,有很多应用需要你去读某些数据并回流他们到你的程序。例如,当开发forms,你会常常想更新一些React state
当你收到用户输入的时候。或者也许你想在JavaScript完成布局并相应一些DOM元素大小的变化。
在React里,你可以用监听 “change” 事件来实现它,从你的数据源(通常是DOM)读取并在你的某个组件调用 setState()
。明确的”Closing the data flow loop” 致使了更容易理解和维护的程序。更多信息见our forms documentation.
双向绑定 — 隐含的强迫DOM里的某些值总是和某些React state
同步 — 简洁并支持大量多样的应用。 我们提供了 ReactLink
:设置如上描述的通用数据回流模式的语法糖,或者 “linking” 某些数据结构到 React state
.
举个梨子:
<script type="text/babel">
var LinkedStateMixin = React.addons.LinkedStateMixin;
var WithLink = React.createClass({
mixins: [LinkedStateMixin],
getInitialState: function() {
return {
message: 'Hello!'
};
},
render: function() {
return (<div>
<input type="text" valueLink={this.linkState('message')} />
<p>{this.state.message}</p>
</div>);
}
});
var WithoutLink = React.createClass({
mixins: [LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function() {
var valueLink = this.linkState('message');
var handleChange = function(e) {
valueLink.requestChange(e.target.value);
};
return (<div>
<input type="text" value={valueLink.value} onChange={handleChange} />;
<p>{this.state.message}</p>
</div>);
}
});
ReactDOM.render(<WithLink />, document.getElementById('app'));
ReactDOM.render(<WithoutLink />, document.getElementById('app1'));
</script>
在栗子WithoutLin中使用了this.linkState(‘message’).value和this.linkState(‘message’).requestChange()方法,这是这个mixin自带的方法和取值用法。
三、React拷贝组件Mixin-cloneWithProps(React.cloneElement)
拷贝组件,这个组件已经被最新版本弃用,可以改用React.cloneElement代替;
<script type="text/babel">
var cloneWithProps = React.addons.cloneWithProps;
var _makeBlue = function(element) {
return cloneWithProps(element, {style: {color: 'blue'}});
};
var Blue = React.createClass({
render: function() {
var blueChildren = React.Children.map(this.props.children, _makeBlue);
return (<div>
{blueChildren}
{blueChildren}
</div>);
}
});
ReactDOM.render(
<Blue>
<p>This text is blue.</p>
</Blue>,
document.getElementById('app2')
);
</script>
四、React多个节点Mixin-createFragment
在大多数情况下,您可以使用key属性来指定您从渲染返回的元素。然而,这打破了一种情况:如果您需要返回两个子组件,需要使用一个div包裹起来。
<script type="text/babel">
var createFragment = React.addons.createFragment;
var Swapper = React.createClass({
propTypes: {
leftChildren: React.PropTypes.node,
rightChildren: React.PropTypes.node,
swapped: React.PropTypes.bool
},
render: function() {
var children;
if (this.props.swapped) {
children = createFragment({
right: this.props.rightChildren,
left: this.props.leftChildren
});
} else {
children = createFragment({
left: this.props.leftChildren,
right: this.props.rightChildren
});
}
// if (this.props.swapped) {
// children = [this.props.rightChildren, this.props.leftChildren];
// } else {
// children = [this.props.leftChildren, this.props.rightChildren];
// }
return <div>{children}</div>;
}
});
var props = {
swapped:false,
leftChildren:'hello',
rightChildren:'world'
}
ReactDOM.render(
<Swapper {...props}></Swapper>,
document.getElementById('app3')
);
</script>
五、React纯渲染Mixin-PureRenderMixin
PureRenderMixin其实使用shouldComponentUpdate的方法。提高性能!
<script type="text/babel">
var PureRenderMixin = React.addons.PureRenderMixin;
var Pure = React.createClass({
mixins: [PureRenderMixin],
render: function() {
return <div className={this.props.className}>foo</div>;
}
});
ReactDOM.render(
<Pure className="hallo"/>,
document.getElementById('app4')
);
</script>
六、React浅比较Mixin-shallowCompare
shallowCompare
对当前的 props
和 nextProps
对象 执行一个浅的相等检查,同样对于 state
和 nextState
对象。 它 通过迭代比较对象的keys 并在 对象的key值不严格相等时返回false 实现此功能.
shallowCompare
返回 true
如果对 props 或 state的浅比较失败,因此组件应该更新。 shallowCompare
返回 false
如果对 props 或 state的浅比较都通过了,因此组件不应该更新。
var shallowCompare = require('react-addons-shallow-compare');
export class SampleComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}
render() {
return <div className={this.props.className}>foo</div>;
}
}
最后是ReactMixin的全部方法
其中有两个方法是开发版本才有的:perf和testUtils,有兴趣的伙伴可以下去研究一下。