我正在使用React构建一个动画简介,其中包含三个与动画SVG图标配对的标语。
我选择TweenMax来管理SVG动画,因为它提供了强大的跨浏览器支持。这也允许我在d
元素上执行<path>
属性的简单morph。我将上述内容与ReactTransitionGroup
但是,在尝试使TweenMax和React运行时,我遇到了以下问题:
componentWillReceiveProps
在控制台中以某种方式被调用两次。这意味着,我的动画方法将被调用两次。造成这种情况的原因以及使用Redux存储标记索引在这个用例中是更好的选择吗?this.refs
和findDOMNode()
。当然必须有更好的方法吗?我的代码结构可以通过哪些方式进行改进?我完全被难以接受,并希望被指向正确的方向。
亲切的问候,杰森
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
background: rgb(240, 90, 48);
font: bold 1em sans-serif;
color: rgb(255, 255, 255);
text-align: center;
}
.reveal-wrap {
position: absolute;
width: 200px;
height: 200px;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
overflow: hidden;
}
.reveal-icon {
width: 100px;
height: 100px;
margin: 0 auto 2em auto;
}
.reveal-icon svg {
width: 100%;
height: 100%;
}
.reveal-icon .fill {
fill: rgb(251, 163, 10);
}
.reveal-icon .mask {
fill: rgb(240, 90, 48);
}
.reveal-icon .stroke {
stroke: rgb(251, 163, 10);
stroke-linecap: round;
stroke-width: 5;
}
.reveal-text {
position: absolute;
width: 100%;
}
.switch-locale {
position: fixed;
top: 1em;
left: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<script src="https://fb.me/react-with-addons-0.13.3.min.js"></script>
<div id="react-root"></div>
<script type="text/babel">
const ReactTransitionGroup = React.addons.TransitionGroup
const UI_TEXT = {
EN_US: {
REVEAL: [
{ ID: 0, TEXT: 'Tagline 1' },
{ ID: 1, TEXT: 'Tagline 2' },
{ ID: 2, TEXT: 'Tagline 3' }
]
},
NL_NL: {
REVEAL: [
{ ID: 0, TEXT: 'Slagzin 1' },
{ ID: 1, TEXT: 'Slagzin 2' },
{ ID: 2, TEXT: 'Slagzin 3' }
]
}
}
class Reveal extends React.Component {
constructor() {
super();
this.state = {
index: 0,
locale: 'EN_US'
}
}
nextTagline() {
this.setState({index: this.state.index + 1})
console.log('this.state.index @ ' + this.state.index)
}
switchLocale() {
let locale = (this.state.locale === 'EN_US') ? 'NL_NL' : 'EN_US'
this.setState({locale})
}
render() {
return (
<ReactTransitionGroup className='reveal-wrap' component='div'>
<RevealIcon tag={this.state.index} nextTag={() => this.nextTagline()} />
<RevealText tag={this.state.index} locale={this.state.locale} key={this.state.index} />
<SwitchLocale switchLocale={() => this.switchLocale()} />
</ReactTransitionGroup>
)
}
}
class RevealText extends React.Component {
fadeIn(callback, delay) {
TweenLite.fromTo(React.findDOMNode(this), 0.5,
{
y: '100px',
opacity: 0
},
{
y: 0,
delay: delay,
opacity: 1,
ease: Quad.easeOut,
onComplete: callback,
onCompleteScope: this
}
)
}
fadeOut(callback, delay) {
TweenLite.fromTo(React.findDOMNode(this), 0.5,
{
y: 0,
opacity: 1
},
{
y: '+=100px',
delay: delay,
opacity: 0,
ease: Quad.easeIn,
onComplete: callback,
onCompleteScope: this
}
)
}
componentWillAppear(callback) {
//console.log('RevealText will appear')
this.fadeIn(callback, 1)
}
componentDidAppear() {
//console.log("RevealText did appear")
}
componentWillLeave(callback) {
this.fadeOut(callback, 0)
}
componentDidLeave() {
//console.log('RevealText did leave')
}
componentWillEnter(callback) {
this.fadeIn(callback, 1)
}
componentDidEnter() {
//console.log("RevealText did enter")
}
render() {
return (
<div className='reveal-text'>
{ UI_TEXT[this.props.locale].REVEAL[this.props.tag].TEXT }
</div>
)
}
}
class RevealIcon extends React.Component {
componentWillAppear(callback) {
const HAND_1 = [React.findDOMNode(this.refs.HAND_1),
React.findDOMNode(this.refs.HAND_1_MASK),
React.findDOMNode(this.refs.HAND_1_THUMB)]
const HAND_2 = [React.findDOMNode(this.refs.HAND_2),
React.findDOMNode(this.refs.HAND_2_MASK)]
const HAND_3 = React.findDOMNode(this.refs.HAND_LINES)
const HAND_4 = [React.findDOMNode(this.refs.HAND_LINES_1),
React.findDOMNode(this.refs.HAND_LINES_2),
React.findDOMNode(this.refs.HAND_LINES_3),
React.findDOMNode(this.refs.HAND_LINES_4),
React.findDOMNode(this.refs.HAND_LINES_5),
React.findDOMNode(this.refs.HAND_LINES_6),
React.findDOMNode(this.refs.HAND_LINES_7),
React.findDOMNode(this.refs.HAND_LINES_8)]
let anim = new TimelineMax({
delay: 2,
onComplete: this.props.nextTag,
onCompleteScope: this
})
anim.fromTo(HAND_1, 0.5,
{
y: '-=100px',
x: '+=100px',
opacity: 0
},
{
y: 0,
x: 0,
opacity: 1,
ease: Quad.easeOut
})
.fromTo(HAND_2, 0.5,
{
y: '-=100px',
x: '-=100px',
opacity: 0
},
{
y: 0,
x: 0,
opacity: 1,
ease: Quad.easeOut
}, '-=0.20')
.fromTo(HAND_3, 0.75,
{
scaleX: 0.5,
scaleY: 0.5,
transformOrigin: '50% 50%'
},
{
scaleX: 1,
scaleY: 1,
ease: Quad.easeOut
})
.fromTo(HAND_4, 0.5,
{
opacity: 0,
},
{
opacity: 1,
ease: Quad.easeOut
}, '-=0.75')
.fromTo(HAND_4, 1,
{
'stroke-dasharray': '25px',
'stroke-dashoffset': '0px'
},
{
'stroke-dasharray': '25px',
'stroke-dashoffset': '25px',
ease: Power3.easeOut
}, '-=0.75')
.set({}, {}, '+=1')
// .set is used to lengthen the animation by 1 second
}
componentWillReceiveProps(nextProps) {
console.log('RevealIcon will receive props', 'nextProps.tag: ' + nextProps.tag)
if(nextProps.tag === 1){
// Animation code / reference to method here
} else if (nextProps.tag === 2) {
// Animation code / reference to method here
} else if (nextProps.tag === 3) {
// Animation code / reference to method here
}
}
render() {
return (
<div className='reveal-icon' >
<svg height="200" width="200" viewBox="0, 0, 200, 200">
<path ref="HAND_1" className="fill" d="M146.8,79.9l-55.2,55.2c-1.8,1.8-4.8,1.8-6.7,0c-1.8-1.8-1.8-4.8,0-6.7h0l18.4-18.4l-3.3-3.3
l-18.4,18.4c-0.9,0.9-2.1,1.4-3.3,1.4s-2.5-0.5-3.3-1.4c-0.9-0.9-1.4-2.1-1.4-3.3c0-1.3,0.5-2.5,1.4-3.3L93.3,100L90,96.7
l-18.4,18.4c-1.8,1.8-4.8,1.8-6.7,0c-1.8-1.8-1.8-4.8,0-6.7l41.8-41.8l-3.3-3.3L61.5,105c-3.7,3.7-3.7,9.7,0,13.4
c1.8,1.8,4.3,2.8,6.7,2.8c0.2,0,0.4,0,0.6,0c0,0.2,0,0.4,0,0.6c0,2.5,1,4.9,2.8,6.7c1.8,1.8,4.2,2.8,6.7,2.8c0.2,0,0.4,0,0.6,0
c-0.2,2.6,0.7,5.3,2.7,7.3c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8l55.2-55.2L146.8,79.9z"/>
<path ref="HAND_2_MASK" className="mask" d="M138.5,105l-22.7-22.7L83.3,49.8L49.8,83.3l32.5,32.5l22.7,22.7
c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8c2-2,2.9-4.7,2.7-7.3c0.2,0,0.4,0,0.6,0c2.5,0,4.9-1,6.7-2.8
c1.8-1.8,2.8-4.2,2.8-6.7c0-0.2,0-0.4,0-0.6c0.2,0,0.4,0,0.6,0c2.4,0,4.8-0.9,6.7-2.8c1.8-1.8,2.8-4.2,2.8-6.7
C141.2,109.2,140.2,106.8,138.5,105z"/>
<path ref="HAND_2" className="fill" d="M138.5,105L83.3,49.8l-3.3,3.3l55.2,55.2c0.9,0.9,1.4,2.1,1.4,3.3c0,1.3-0.5,2.5-1.4,3.3
c-1.8,1.8-4.8,1.8-6.7,0L110,96.7l-3.3,3.3l18.4,18.4c0.9,0.9,1.4,2.1,1.4,3.3c0,1.3-0.5,2.5-1.4,3.3c-0.9,0.9-2.1,1.4-3.3,1.4
s-2.5-0.5-3.3-1.4L100,106.7l-3.3,3.3l18.4,18.4c1.8,1.8,1.8,4.8,0,6.7c-1.8,1.8-4.8,1.8-6.7,0L53.2,79.9l-3.3,3.3l55.2,55.2
c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8c2-2,2.9-4.7,2.7-7.3c0.2,0,0.4,0,0.6,0c2.5,0,4.9-1,6.7-2.8
c1.8-1.8,2.8-4.2,2.8-6.7c0-0.2,0-0.4,0-0.6c0.2,0,0.4,0,0.6,0c2.4,0,4.8-0.9,6.7-2.8c1.8-1.8,2.8-4.2,2.8-6.7
C141.2,109.2,140.2,106.8,138.5,105z"/>
<path ref="HAND_1_MASK" className="mask" d="M116.7,49.8l-5,5l-3.3-3.3c-1.8-1.8-4.2-2.8-6.7-2.8c-2.5,0-4.9,1-6.7,2.8
L73.2,73.2c-3.7,3.7-3.7,9.7,0,13.4c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8l20.1-20.1l5-5l8.4-8.4L116.7,49.8z"/>
<path ref="HAND_1_THUMB" className="fill" d="M116.7,49.8l-5,5l-3.3-3.3l0,0c-1.8-1.8-4.2-2.8-6.7-2.8c-2.5,0-4.9,1-6.7,2.8
L73.2,73.2c-3.7,3.7-3.7,9.7,0,13.4c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8l20.1-20.1l-3.3-3.3L83.3,83.3
c-1.8,1.8-4.8,1.8-6.7,0s-1.8-4.8,0-6.7l21.7-21.7c0.9-0.9,2.1-1.4,3.3-1.4c1.3,0,2.5,0.5,3.3,1.4l6.7,6.7l8.4-8.4L116.7,49.8z"/>
<g ref="HAND_LINES">
<line ref="HAND_LINES_8" className="stroke" x1="32.6" y1="32.6" x2="49.4" y2="49.4"/>
<line ref="HAND_LINES_7" className="stroke" x1="4.7" y1="100" x2="28.4" y2="100"/>
<line ref="HAND_LINES_6" className="stroke" x1="32.6" y1="167.4" x2="49.4" y2="150.6"/>
<line ref="HAND_LINES_5" className="stroke" x1="100" y1="195.3" x2="100" y2="171.6"/>
<line ref="HAND_LINES_4" className="stroke" x1="167.4" y1="167.4" x2="150.6" y2="150.6"/>
<line ref="HAND_LINES_3" className="stroke" x1="195.3" y1="100" x2="171.6" y2="100"/>
<line ref="HAND_LINES_2" className="stroke" x1="167.4" y1="32.6" x2="150.6" y2="49.4"/>
<line ref="HAND_LINES_1" className="stroke" x1="100" y1="4.7" x2="100" y2="28.4"/>
</g>
</svg>
</div>
)
}
}
class SwitchLocale extends React.Component {
render() {
return (
<button className='switch-locale' onClick={this.props.switchLocale}>
Switch locale
</button>
)
}
}
React.render(<Reveal/>, document.getElementById('react-root'))
</script>
在左边,我目前在React创建的动画,在右边,我仍然需要以相同的方式实现的图标。