css transition
First take a look at this version vue
css
How transitions and animations are applied :
<p class="msg" v-if="show" v-transition="expand">Hello!</p>
<p class="animated" v-if="show" v-transition="bounce">Look at me!</p>
.msg {
transition: all .3s ease;
height: 30px;
padding: 10px;
background-color: #eee;
overflow: hidden;
}
.msg.expand-enter, .msg.expand-leave {
height: 0;
padding: 0 10px;
opacity: 0;
}
.animated {
display: inline-block;
}
.animated.bounce-enter {
animation: bounce-in .5s;
}
.animated.bounce-leave {
animation: bounce-out .5s;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
@keyframes bounce-out {
0% {
transform: scale(1);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(0);
}
}
You can see that it is also through instructions , This version only supports two classes , One is added when entering v-enter
, The other is added when leaving v-leave
.
Take a look at this instruction first :
module.exports = {
isLiteral: true,// by true Does not create watcher example
bind: function () {
this.update(this.expression)
},
update: function (id) {
var vm = this.el.__vue__ || this.vm
this.el.__v_trans = {
id: id,
// This version of vue have access to transitions Options to define JavaScript Animation
fns: vm.$options.transitions[id]
}
}
}
This instruction will not create watcher
, Because the value of the instruction is either css
The name of the class , Or JavaScript
The name of the animation option , No observation is required . What you do when you bind an instruction is to give el
Element adds a custom attribute , The value of the expression is saved , Here is expand
、JavaScript
Animation functions , Here is undefined
.
To trigger the animation, you need to modify it if
Instructions show
Value , Suppose the beginning is false
, Let's change it to true
, This triggers if
The directive update
Method , According to part III vue0.11 Version source code reading series 3 : Instruction compilation The last part is right if
During the parsing of the instruction procedure, we know that transition.blockAppend(frag, this.end, vm)
, Called when leaving transition.blockRemove(this.start, this.end, this.vm)
, Obviously, it will be called here blockAppend
:
// block It includes if Code snippet of instruction binding element
// target Is an annotation node , stay if The location of the instruction binding element
exports.blockAppend = function (block, target, vm) {
// Child nodes of code snippets
var nodes = _.toArray(block.childNodes)
for (var i = 0, l = nodes.length; i < l; i++) {
apply(nodes[i], 1, function () {
_.before(nodes[i], target)
}, vm)
}
}
Traversal element call apply
Method :
var apply = exports.apply = function (el, direction, op, vm, cb) {
var transData = el.__v_trans
if (
!transData ||// No transition data
!vm._isCompiled ||// The current instance has not been called $mount Method is inserted into the page
(vm.$parent && !vm.$parent._isCompiled)// The parent component was not inserted into the page
) {// Animation is not required for the above situations , Just skip
op()
if (cb) cb()
return
}
var jsTransition = transData.fns
// JavaScript Animation , Look at the next section
if (jsTransition) {
applyJSTransition(
el,
direction,
op,
transData,
jsTransition,
vm,
cb
)
} else if (
_.transitionEndEvent &&
// No transition if the page is not visible
!(doc && doc.hidden)
) {
// css
applyCSSTransition(
el,
direction,
op,
transData,
cb
)
} else {
// No transition is required
op()
if (cb) cb()
}
}
This method will determine whether it is an application JavaScript
Animation or css
Animation , And distributed to different functions to handle . Function dolls , Again applyCSSTransition
Method :
module.exports = function (el, direction, op, data, cb) {
var prefix = data.id || 'v'// Here is expand
var enterClass = prefix + '-enter'// expand-enter
var leaveClass = prefix + '-leave'// expand-leave
if (direction > 0) { // Get into
// Add the incoming class name to the element
addClass(el, enterClass)
// op Namely _.before(nodes[i], target) operation , This step will add elements to the page
op()
push(el, direction, null, enterClass, cb)
} else { // Leave
// Add the left class name to the element
addClass(el, leaveClass)
push(el, direction, op, leaveClass, cb)
}
}
You can see that there is a difference between entering and leaving , This time we are putting show
Change the value of to true
, So I will go direction > 0
The branch of , First add the incoming class name to the element , Then actually insert the element into the page , Last call push
Method ;
If you leave, you will first add the left class name to the element , And then call push
Method ;
to glance at push
Method :
var queue = []
var queued = false
function push (el, dir, op, cls, cb) {
queue.push({
el : el,
dir : dir,
cb : cb,
cls : cls,
op : op
})
if (!queued) {
queued = true
_.nextTick(flush)
}
}
Add this task to the queue , An asynchronous callback is registered and executed in the next frame , About nextTick
For detailed analysis, please go to vue0.11 Version source code reading series 5 : How is batch update done .
addClass
and op
It's all synchronization tasks , Will be executed immediately , If there are more than one by this at the moment if
The elements controlled by the instruction are added to the queue in turn , The result is that these elements will be added to the page , But because what we set for the incoming style is height: 0;opacity: 0;
, So it's invisible , After these synchronous tasks are executed, they will go to the asynchronous queue to register flush
Method is pulled out for execution :
function flush () {
// This method is used to trigger forced backflow , Make sure we add expand-enter The style can take effect , But I've tried to work without backflow
var f = document.documentElement.offsetHeight
queue.forEach(run)
queue = []
queued = false
}
flush
Method just added to queue
Task object call in run
Method , Because of asynchronous batch update , Therefore, if there are multiple element animations at the same time, only one reflow will be triggered :
function run (job) {
var el = job.el
var data = el.__v_trans
var cls = job.cls
var cb = job.cb
var op = job.op
// getTransitionType Method to get yes transition Transition or animation Animation , The principle is to judge elements style Object or getComputedStyle() Method in the style object obtained by the transitionDuration or animationDuration Whether the attribute exists and whether it is 0s
var transitionType = getTransitionType(el, data, cls)
if (job.dir > 0) { // Get into
if (transitionType === 1) {// transition transition
// because v-enter The style of is the style of the hidden element , In addition, because the element is set transition: all .3s ease, So as long as you delete this class, the transition effect will be applied naturally
removeClass(el, cls)
// Listen only when there is a callback transitionend event
if (cb) setupTransitionCb(_.transitionEndEvent)
} else if (transitionType === 2) {// animation Animation
// animation Animation as long as you add v-enter Class triggers itself , All you need to do is listen animationend Event deletes this class after the animation
setupTransitionCb(_.animationEndEvent, function () {
removeClass(el, cls)
})
} else {
// There is no transition
removeClass(el, cls)
if (cb) cb()
}
} else { // Leave
// Leaving animation is easy , Both, just add v-leave Class can trigger the animation
// All you have to do is listen for the event at the end of the animation, delete the element from the page and delete the class name from the element
if (transitionType) {
var event = transitionType === 1
? _.transitionEndEvent
: _.animationEndEvent
setupTransitionCb(event, function () {
op()
removeClass(el, cls)
})
} else {
op()
removeClass(el, cls)
if (cb) cb()
}
}
}
Now look at when show
The value of is determined by true
Change to false
Called when blockRemove
Method :
// start and end Are two annotation nodes , Surrounded the if All elements controlled by the instruction
exports.blockRemove = function (start, end, vm) {
var node = start.nextSibling
var next
while (node !== end) {
next = node.nextSibling
apply(el, -1, function () {
_.remove(el)
}, vm, cb)
node = next
}
}
Traversal elements are also called apply
Method , It's just that the parameters are passed -1
It means leaving .
Here's a summary vue
Of css
transition :
1. Get into
Add to the element first v-enter
class , Then insert the element into the page , Finally, create a task and add it to the queue , If there are more than one element, it will be completed at one time , Then perform the task just added at the next frame :
1.1css transition
v-enter
Styles in class names are generally used to hide elements , For example, set the width and height of the element to 0
、 Transparency set to 0
wait , It's right to be invisible anyway , To trigger the animation, you need to delete the class name , So the task here is to remove the elements v-enter
Class name , Then the browser will apply the transition effect itself .
1.2css Animation
animation
Dissimilarity ,v-enter
The style of a class is generally defined animation
The attribute value , such as :animation: bounce-out .5s;
, Just add the class name , The animation will begin , So the task here is to listen to the animation end event to remove the element v-enter
Class name .
2. Leave
css
Transition and animation are the same when they leave , Is to add one to the element v-leave
Class is OK ,v-leave
Class to set the general and v-enter
It's the same , Unless the in and out effect is different , Otherwise, it is necessary to make the element invisible , Then add a task , Because the style is not visible, but the element is actually on the page , So the final task is to listen to the animation end event and really remove the element from the page , Of course , Corresponding v-leave
Class is also important Removed from element .
JavaScript Animation
To use in this version JavaScript
For animation transitions, you need to use the declare transition option :
Vue.transition('fade', {
beforeEnter: function (el) {
// The element is called before it is inserted into the document. , For example, extraction makes elements invisible , Otherwise, there will be a flash screen problem
},
enter: function (el, done) {
// Element already inserted into DOM, After the animation is completed, you need to call... Manually done Method
$(el)
.css('opacity', 0)
.animate({ opacity: 1 }, 1000, done)
// Returns a function called when the animation is cancelled
return function () {
$(el).stop()
}
},
leave: function (el, done) {
$(el).animate({ opacity: 0 }, 1000, done)
return function () {
$(el).stop()
}
}
})
Is to define three hook functions , Defined JavaScript
Transition options , stay transition
The directive update
Method can get , This will lead to the above apply
In the method jsTransition
Branch , call applyJSTransition
Method :
module.exports = function (el, direction, op, data, def, vm, cb) {
if (data.cancel) {
data.cancel()
data.cancel = null
}
if (direction > 0) { // Get into
// call beforeEnter hook
if (def.beforeEnter) {
def.beforeEnter.call(vm, el)
}
op()// Insert element into page dom
// call enter hook
if (def.enter) {
data.cancel = def.enter.call(vm, el, function () {
data.cancel = null
if (cb) cb()
})
} else if (cb) {
cb()
}
} else { // Leave
// call leave hook
if (def.leave) {
data.cancel = def.leave.call(vm, el, function () {
data.cancel = null
// Leaving the animation is over, removing elements from the page
op()
if (cb) cb()
})
} else {
op()
if (cb) cb()
}
}
}
Follow css
Transition comparison ,JavaScript
The transition is simple , Entering the transition is to execute the following initialization method before the element is actually inserted into the page , Then insert the element into the page , Next call enter
The hook moves the elements as you like , Adjust it after the animation vue
Injection method vue
The animation is over , Before leaving the transition, adjust your exit hook , Delete elements from the page after your animation , The logic is simple .