接着继续添加冲击波的CSS定义,还是在style标签里,添加一下代码:
.blaze { position: absolute; bottom: 300px; width: 50px; height: 50px; opacity: 0; animation-timing-function: ease-out; animation-duration: 1000ms;
} .blaze.toward-left {background-image: url(../../static/images/blaze-left.png);} .blaze.toward-right {background-image: url(../../static/images/blaze-right.png);}
@keyframes blaze-toward-left {
0%, 20% {opacity: 1; transform: translate3d(300px, 0, 0)}
80% {transform: translate3d(100px, 0, 0);}
100% {opacity: 0; transform: translate3d(100px, 0, 0);}
} .blaze.toward-left.attack {animation-name: blaze-toward-left;} .blaze.toward-right.attack {animation-name: blaze-toward-right;}
我们看看blaze-toward-left所定义的变化,它首先使得图片blaze-left.png出现在x坐标轴300px的地方,接着向左移动一直到100px的地方,然后他的透明度变成0,也就是消失看不到了,这个效果就类似于页面右边的扑克牌放出了一个指向左边的冲击波,冲击波从右向左移动,抵达左边扑克牌的位置后消失,此时左边扑克牌触发shake变化,于是扑克牌产生出一种被击打后左右颤抖的效果。
接下来我们需要使用js实现整个动画流程,这是整个项目的难点所在。我们要实现的效果是,用户从底部选择一张扑克牌后,选中的牌显示在界面的右边,然后敌对扑克牌出现在坐标,一旦敌对扑克牌出现后,右边扑克牌发出一个blaze-left.png表示的冲击波,冲击波打中左边扑克牌后,扑克牌产生一个左右摇摆的颤抖效果。
我们注意到,一个html元素能够产生两种特效,一种叫transform,一种叫animation,后者是由一系列前者组成的。由于我们现在需要做的是一种特效完成后,由程序接着触发另一种特效,这就需要我们的程序知道特效在哪个时刻完成,好在浏览器给我们提供了相应机制,当一个元素完成transform或animation之后,浏览器就可以通知我们的js程序。
当一个元素完成transform变换时,它会发出一个消息叫webkitTransitionEnd,当元素完成animation变换时,它会发出一个消息叫webkitAnimationEnd,我们只要监听这两个消息,然后才行相应动作就好。当前能产生相应变换的只有两个元素,一个是属性为'card opponent'的div,另一个是属性为'blaze toward-left'的div。所以我们的代码要监听这两个元素所发出的相应消息。
我们先在组件中添加相关变量定义:
<script>
import Constant from './constant'
export default {
data () { return {
....
opponentCardObject: null, blazeTowardLeftObject: null, blazeAttackLeft: false, opponentCardShake: false, transitionState: ''
}
....
}
其中opponentCardObject将用来获得属性为'card opponent'的div实例,blazeTowardLeftObject将用来获得属性为'blaze toward-left'的div实例。我们在组件的mounted调用中添加如下代码:
mounted () {
....
this.opponentCardObject = document.querySelector('.card.opponent') this.blazeTowardLeftObject = document.querySelector('.blaze.toward-left')
}
当敌对扑克牌要出现在页面左边时,代码需要把变量cardOpponentOut设置成false,然后'in'属性就会添加到属性为'card opponent'的div元素上,我们在看看css定义的in属性:
.card.player.in { transform: translate3d(0, 0, 0);
}
in属性对应的是一个transform变换,也就是说div具备了in属性后,就会执行上面定义的变换,div执行上面的变换后,它就出现在页面上了。前面我们提到过transform变换结束后,元素会发出一个webkitTransitionEnd消息,所以只要我们程序监听到div发出这个消息时,我们就可以判定左边扑克牌出现在页面上了。因此在组件的methods区域添加下面代码:
handleTransitionEnd (htmlObj) { var listener = function (e) {
e.target.removeEventListener('webkitTransitionEnd', listener) this.handleTransitionEvent(e)
}.bind(this)
htmlObj.addEventListener('webkitTransitionEnd', listener)
}
只要我们执行handleTransitionEnd(this.opponentCardObject),那么程序就可以监听div发出的webkitTransitionEnd消息,这个消息一旦监控到,里面定义的listener函数会被调用,然后他会调用组件的handleTransitionEvent接口来处理消息。我们以同样的方式来监控元素发出的webkitAnimationEnd消息,在组件中添加如下代码:
handleAnimationEnd (htmlObj) { var listener = function (e) {
e.target.removeEventListener('webkitAnimationEnd', listener) this.handleTransitionEvent(e)
}.bind(this)
htmlObj.addEventListener('webkitAnimationEnd', listener)
}
接着修改beginBattaleAnimation代码:
beginBattleAnimation () { this.handleTransitionEnd(this.opponentCardObject) this.transitionState = Constant.OPPONENT_CARD_TRANSITION_END this.cardOpponentOut = false
}
this.handleTransitionEnd(this.opponentCardObject)作用是监听opponentCardObject对象发出的webkitTransitionEnd消息,然后将变量transitionState设置成Constant.OPPONENT_CARD_TRANSITION_END,然后将cardOpponentOut设置成false,这样opponentCardObject对应的div元素会添加上in属性,于是他就会执行in属性定义的transform变换,一旦变换完成后,组件的handleTransitionEvent接口就会被调用,我们看看该接口的实现,在组件中添加如下代码:
handleTransitionEvent (e) { switch (this.transitionState) { case Constant.OPPONENT_CARD_TRANSITION_END: if (this.cardOpponentOut === false) { this.transitionState = Constant.BLAZE_TOWARD_LEFT_ANIMATION_END this.blazeAttackLeft = true
this.handleAnimationEnd(this.blazeTowardLeftObject)
} break
case Constant.BLAZE_TOWARD_LEFT_ANIMATION_END: this.opponentCardShake = true
break
}
}
当handleTransitionEvent被调用,而且transitionState的值是Constant.OPPONENT_CARD_TRANSITION_END,这就表明opponentCardObject对应的div对象刚完成了一个transform变化,如果变量cardOpponentOut的值是false,我们就确定它刚完成了属性in定义的变换,也就是说敌对扑克牌在出现在页面上了。
然后我们把transitionState的值变为Constant.BLAZE_TOWARD_LEFT_ANIMATION_END,然后将blazeAttackLeft属性设置为true,于是attack属性就会添加到属性为'blaze toward-left'所对应的div上,于是该元素就会执行attack所定义的animation变换,this.handleAnimationEnd(this.blazeTowardLeftObject)让我们的代码监听blazeTowardLeftObject元素发出的webkitAnimationEnd消息,一旦这个消息被监控到后,handleTransitionEvent又再次被调用,它执行时发现transitionState的值是Constant.BLAZE_TOWARD_LEFT_ANIMATION_END,此时我们就可以确定blazeTowardLeftObject所对应的div元素,也就是属性为'blaze toward-left'的div元素完成了CSS属性attack所定义的动画特效,也就是说敌对扑克牌被冲击波打到了,因此我们就可以把oppoentCardShake设置成true,一旦设置后,shake属性就会添加到属性为'card opponent'的div元素上,因此该元素就会执行CSS属性shake所定义的动画特效,也就是左右颤抖的效果。
最后我们在Constant组件里添加如下代码:
script> import Vue from 'vue'
export default {
...
OPPONENT_CARD_TRANSITION_END: 'opponent_card_transition_end', BLAZE_TOWARD_LEFT_ANIMATION_END: 'blaze_left_animation_end'}
完成上面代码,然后加载到浏览器后,运行起来可以看到如下效果:

这里写图片描述
一个冲击波从右边发出,打到左边扑克牌后,左边的牌发出一个左右颤抖的动画效果,
作者:望月从良
链接:https://www.jianshu.com/p/3aaa95c61007