Browse Source

rework transition

Evan You 10 years ago
parent
commit
a0e8df3957

+ 0 - 9
src/compiler/compile.js

@@ -577,15 +577,6 @@ function compileDirectives (attrs, options) {
       })
     } else
 
-    // transition
-    if (name === 'transition') {
-      dirs.push({
-        name: 'transition',
-        descriptors: [newDirParser.parse(value)],
-        def: options.directives.transition
-      })
-    } else
-
     // should not see props here
     /* istanbul ignore if */
     if (process.env.NODE_ENV !== 'production' && propRE.test(name)) {

+ 3 - 2
src/directives/for.js

@@ -1,5 +1,6 @@
 var _ = require('../util')
 var config = require('../config')
+var transition = require('../transition')
 var FragmentFactory = require('../fragment/factory')
 var isObject = _.isObject
 var uid = 0
@@ -427,8 +428,8 @@ module.exports = {
 
   getStagger: function (frag, index, total, type) {
     type = type + 'Stagger'
-    var transition = frag.node.__v_trans
-    var hooks = transition && transition.hooks
+    var trans = transition.get(frag.node, this.vm)
+    var hooks = trans && trans.hooks
     var hook = hooks && (hooks[type] || hooks.stagger)
     return hook
       ? hook.call(frag, index, total)

+ 3 - 2
src/directives/repeat.js

@@ -2,6 +2,7 @@ var _ = require('../util')
 var config = require('../config')
 var isObject = _.isObject
 var isPlainObject = _.isPlainObject
+var transition = require('../transition')
 var textParser = require('../parsers/text')
 var expParser = require('../parsers/expression')
 var templateParser = require('../parsers/template')
@@ -648,8 +649,8 @@ module.exports = {
 
   getStagger: function (vm, index, total, type) {
     type = type + 'Stagger'
-    var transition = vm.$el.__v_trans
-    var hooks = transition && transition.hooks
+    var trans = transition.get(vm.$el, vm)
+    var hooks = trans && trans.hooks
     var hook = hooks && (hooks[type] || hooks.stagger)
     return hook
       ? hook.call(vm, index, total)

+ 2 - 0
src/directives/transition.js

@@ -1,3 +1,5 @@
+// TODO: remove in 1.0.0
+
 var _ = require('../util')
 var Transition = require('../transition/transition')
 

+ 27 - 1
src/transition/index.js

@@ -1,4 +1,5 @@
 var _ = require('../util')
+var Transition = require('./transition')
 
 /**
  * Append with transition.
@@ -73,7 +74,7 @@ exports.removeThenAppend = function (el, target, vm, cb) {
  */
 
 var apply = exports.apply = function (el, direction, op, vm, cb) {
-  var transition = el.__v_trans
+  var transition = exports.get(el, vm)
   if (
     !transition ||
     // skip if there are no js hooks and CSS transition is
@@ -93,3 +94,28 @@ var apply = exports.apply = function (el, direction, op, vm, cb) {
   var action = direction > 0 ? 'enter' : 'leave'
   transition[action](op, cb)
 }
+
+/**
+ * Get the transition object from an element, will create
+ * one if it has the "transition" attribute but doesn't have
+ * a transition object, or if the current transition object's
+ * id doesn't match the desired id.
+ *
+ * @param {Element} el
+ * @param {Vue} vm
+ * @return {Transition|undefined}
+ */
+
+exports.get = function (el, vm) {
+  var transition = el.__v_trans
+  var id = el.getAttribute && el.getAttribute('transition')
+  // create new transition object if
+  // 1. element has "transition" attribute
+  // 2. current transition object's id doesn't match
+  if (id != null && (!transition || transition.id !== id)) {
+    var hooks = _.resolveAsset(vm.$options, 'transitions', id)
+    id = id || 'v'
+    transition = el.__v_trans = new Transition(el, id, hooks, el.__vue__ || vm)
+  }
+  return transition
+}

+ 1 - 3
src/transition/transition.js

@@ -10,8 +10,6 @@ var animDurationProp = _.animationProp + 'Duration'
 var TYPE_TRANSITION = 1
 var TYPE_ANIMATION = 2
 
-var uid = 0
-
 /**
  * A Transition object that encapsulates the state and logic
  * of the transition.
@@ -23,7 +21,7 @@ var uid = 0
  */
 
 function Transition (el, id, hooks, vm) {
-  this.id = uid++
+  this.id = id
   this.el = el
   this.enterClass = id + '-enter'
   this.leaveClass = id + '-leave'

+ 3 - 0
src/util/dom.js

@@ -182,6 +182,9 @@ exports.removeClass = function (el, cls) {
     }
     el.setAttribute('class', cur.trim())
   }
+  if (!el.className) {
+    el.removeAttribute('class')
+  }
 }
 
 /**

+ 3 - 3
test/unit/specs/directives/for/for_spec.js

@@ -609,9 +609,9 @@ if (_.inBrowser) {
       vm.items.splice(1, 1, {a: 4})
       setTimeout(function () {
         expect(el.innerHTML).toBe(
-          '<div class="test-transition">1</div>' +
-          '<div class="test-transition">4</div>' +
-          '<div class="test-transition">3</div>'
+          '<div transition="test">1</div>' +
+          '<div transition="test">4</div>' +
+          '<div transition="test">3</div>'
         )
         document.body.removeChild(el)
         done()

+ 22 - 15
test/unit/specs/directives/for/for_stagger_spec.js

@@ -81,11 +81,13 @@ describe('v-for staggering transitions', function () {
     vm.list = [{a: 1}, {a: 2}]
     expect(el.innerHTML).toBe('')
     _.nextTick(function () {
-      expect(el.innerHTML).toBe('<div class="stagger-transition stagger-enter">1</div>')
+      expect(el.children.length).toBe(1)
+      expect(el.children[0].className).toBe('stagger-enter')
+      expect(el.children[0].textContent).toBe('1')
       vm.list = [vm.list[0]] // remove second
       setTimeout(function () {
         // should have only one
-        expect(el.innerHTML).toBe('<div class="stagger-transition">1</div>')
+        expect(el.innerHTML).toBe('<div transition="stagger">1</div>')
         done()
       }, delayAmount * multiplier)
     })
@@ -112,14 +114,16 @@ describe('v-for staggering transitions', function () {
     vm.list = [{a: 1}, {a: 2}, {a: 3}]
     expect(el.innerHTML).toBe('')
     _.nextTick(function () {
-      expect(el.innerHTML).toBe('<div class="stagger-transition stagger-enter">1</div>')
+      expect(el.children.length).toBe(1)
+      expect(el.children[0].className).toBe('stagger-enter')
+      expect(el.children[0].textContent).toBe('1')
       vm.list = [vm.list[2], vm.list[1], vm.list[0]] // reorder
       setTimeout(function () {
         // should have correct order
         expect(el.innerHTML).toBe(
-          '<div class="stagger-transition">3</div>' +
-          '<div class="stagger-transition">2</div>' +
-          '<div class="stagger-transition">1</div>'
+          '<div transition="stagger">3</div>' +
+          '<div transition="stagger">2</div>' +
+          '<div transition="stagger">1</div>'
         )
         done()
       }, delayAmount * 3)
@@ -130,22 +134,25 @@ describe('v-for staggering transitions', function () {
     vm.list = [{a: 1}, {a: 2}]
     expect(el.innerHTML).toBe('')
     _.nextTick(function () {
-      expect(el.innerHTML).toBe('<div class="stagger-transition stagger-enter">1</div>')
+      expect(el.children.length).toBe(1)
+      expect(el.children[0].className).toBe('stagger-enter')
+      expect(el.children[0].textContent).toBe('1')
       _.nextTick(function () {
-        expect(el.innerHTML).toBe('<div class="stagger-transition">1</div>')
+        expect(el.innerHTML).toBe('<div transition="stagger">1</div>')
         setTimeout(function () {
           expect(el.innerHTML).toBe(
-            '<div class="stagger-transition">1</div>' +
-            '<div class="stagger-transition">2</div>'
+            '<div transition="stagger">1</div>' +
+            '<div transition="stagger">2</div>'
           )
           vm.list = []
           _.nextTick(function () {
-            expect(el.innerHTML).toBe(
-              '<div class="stagger-transition stagger-leave">1</div>' +
-              '<div class="stagger-transition">2</div>'
-            )
+            expect(el.children.length).toBe(2)
+            expect(el.children[0].className).toBe('stagger-leave')
+            expect(el.children[0].textContent).toBe('1')
+            expect(el.children[1].className).toBe('')
+            expect(el.children[1].textContent).toBe('2')
             _.nextTick(function () {
-              expect(el.innerHTML).toBe('<div class="stagger-transition">2</div>')
+              expect(el.innerHTML).toBe('<div transition="stagger">2</div>')
               setTimeout(function () {
                 expect(el.innerHTML).toBe('')
                 done()

+ 69 - 0
test/unit/specs/directives/transition_spec.js

@@ -50,6 +50,7 @@ if (_.inBrowser) {
       expect(dir.el.__v_trans.vm).toBe(vm2)
     })
 
+    // TODO remove this in 1.0.0
     it('dynamic transitions', function (done) {
       var el = document.createElement('div')
       document.body.appendChild(el)
@@ -117,5 +118,73 @@ if (_.inBrowser) {
         expect(calls.b.leave).toBe(d)
       }
     })
+
+    it('dynamic transitions (new syntax)', function (done) {
+      var el = document.createElement('div')
+      document.body.appendChild(el)
+      var calls = {
+        a: { enter: 0, leave: 0 },
+        b: { enter: 0, leave: 0 }
+      }
+      var vm = new Vue({
+        el: el,
+        template: '<div v-show="show" bind-transition="trans"></div>',
+        data: {
+          show: true,
+          trans: 'a'
+        },
+        transitions: {
+          a: {
+            enter: function (el, done) {
+              calls.a.enter++
+              done()
+            },
+            leave: function (el, done) {
+              calls.a.leave++
+              done()
+            }
+          },
+          b: {
+            enter: function (el, done) {
+              calls.b.enter++
+              done()
+            },
+            leave: function (el, done) {
+              calls.b.leave++
+              done()
+            }
+          }
+        }
+      })
+
+      assertCalls(0, 0, 0, 0)
+      vm.show = false
+      _.nextTick(function () {
+        assertCalls(0, 1, 0, 0)
+        vm.trans = 'b'
+        vm.show = true
+        _.nextTick(function () {
+          assertCalls(0, 1, 1, 0)
+          vm.show = false
+          _.nextTick(function () {
+            assertCalls(0, 1, 1, 1)
+            vm.trans = 'a'
+            vm.show = true
+            _.nextTick(function () {
+              assertCalls(1, 1, 1, 1)
+              done()
+            })
+          })
+        })
+      })
+
+      function assertCalls (a, b, c, d) {
+        expect(el.firstChild.style.display).toBe(vm.show ? '' : 'none')
+        expect(calls.a.enter).toBe(a)
+        expect(calls.a.leave).toBe(b)
+        expect(calls.b.enter).toBe(c)
+        expect(calls.b.leave).toBe(d)
+      }
+    })
   })
 }

+ 4 - 4
test/unit/specs/stagger_transition_spec.js

@@ -18,7 +18,7 @@ describe('Staggering Transitions', function () {
   it('as attribute', function (done) {
     var vm = new Vue({
       el: el,
-      template: '<div v-repeat="list" transition="stagger" stagger="' + delayAmount + '">{{a}}</div>',
+      template: '<div v-repeat="list" v-transition="stagger" stagger="' + delayAmount + '">{{a}}</div>',
       data: {
         list: []
       },
@@ -39,7 +39,7 @@ describe('Staggering Transitions', function () {
   it('as hook', function (done) {
     var vm = new Vue({
       el: el,
-      template: '<div v-repeat="list" transition="stagger">{{a}}</div>',
+      template: '<div v-repeat="list" v-transition="stagger">{{a}}</div>',
       data: {
         list: []
       },
@@ -63,7 +63,7 @@ describe('Staggering Transitions', function () {
   it('remove while staggered', function (done) {
     var vm = new Vue({
       el: el,
-      template: '<div v-repeat="list" transition="stagger" stagger="' + delayAmount + '">{{a}}</div>',
+      template: '<div v-repeat="list" v-transition="stagger" stagger="' + delayAmount + '">{{a}}</div>',
       data: {
         list: []
       },
@@ -94,7 +94,7 @@ describe('Staggering Transitions', function () {
   it('reorder while staggered', function (done) {
     var vm = new Vue({
       el: el,
-      template: '<div v-repeat="list" transition="stagger" stagger="' + delayAmount + '">{{a}}</div>',
+      template: '<div v-repeat="list" v-transition="stagger" stagger="' + delayAmount + '">{{a}}</div>',
       data: {
         list: []
       },