Bladeren bron

transition wip

Evan You 10 jaren geleden
bovenliggende
commit
52ac04d235

+ 2 - 2
src/compiler/codegen/index.js

@@ -92,8 +92,8 @@ function genData (el) {
     data += `style:${el.styleBinding},`
   }
   // transition
-  if (el.transition) {
-    data += `transition:${el.transition},`
+  if (el.transition != null) {
+    data += `transition:__resolveTransition__(${el.transition}),`
   }
   // props
   if (el.props) {

+ 1 - 1
src/compiler/helpers.js

@@ -49,7 +49,7 @@ export function addHandler (el, name, value, modifiers) {
 
 export function getBindingAttr (el, name, getStatic) {
   const staticValue = getStatic !== false && getAndRemoveAttr(el, name)
-  return staticValue
+  return staticValue || staticValue === ''
     ? JSON.stringify(staticValue)
     : (getAndRemoveAttr(el, ':' + name) || getAndRemoveAttr(el, 'v-bind:' + name))
 }

+ 79 - 0
src/runtime/dom-backend/class-util.js

@@ -0,0 +1,79 @@
+import { isIE9 } from '../util/index'
+import { svgNS } from './node-ops'
+
+/**
+ * In IE9, setAttribute('class') will result in empty class
+ * if the element also has the :class attribute; However in
+ * PhantomJS, setting `className` does not work on SVG elements...
+ * So we have to do a conditional check here.
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+export function setClass (el, cls) {
+  /* istanbul ignore else */
+  if (!isIE9 || el.namespaceURI === svgNS) {
+    el.setAttribute('class', cls)
+  } else {
+    el.className = cls
+  }
+}
+
+/**
+ * Add class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+export function addClass (el, cls) {
+  if (el.classList) {
+    el.classList.add(cls)
+  } else {
+    let cur = ' ' + getClass(el) + ' '
+    if (cur.indexOf(' ' + cls + ' ') < 0) {
+      setClass(el, (cur + cls).trim())
+    }
+  }
+}
+
+/**
+ * Remove class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+export function removeClass (el, cls) {
+  if (el.classList) {
+    el.classList.remove(cls)
+  } else {
+    let cur = ' ' + getClass(el) + ' '
+    let tar = ' ' + cls + ' '
+    while (cur.indexOf(tar) >= 0) {
+      cur = cur.replace(tar, ' ')
+    }
+    setClass(el, cur.trim())
+  }
+  if (!el.className) {
+    el.removeAttribute('class')
+  }
+}
+
+/**
+ * For IE9 compat: when both class and :class are present
+ * getAttribute('class') returns wrong value... but className
+ * on SVG elements returns an object.
+ *
+ * @param {Element} el
+ * @return {String}
+ */
+
+function getClass (el) {
+  var classname = el.className
+  if (typeof classname === 'object') {
+    classname = classname.baseVal || ''
+  }
+  return classname
+}

+ 3 - 1
src/runtime/dom-backend/index.js

@@ -6,6 +6,7 @@ import props from './modules/props'
 import attrs from './modules/attrs'
 import events from './modules/events'
 import directives from './modules/directives'
+import transition from './modules/transition'
 
 export const patch = createPatchFunction({
   nodeOps,
@@ -15,6 +16,7 @@ export const patch = createPatchFunction({
     attrs,
     style,
     events,
-    directives
+    directives,
+    transition
   ]
 })

+ 11 - 25
src/runtime/dom-backend/modules/class.js

@@ -1,20 +1,25 @@
-import { isIE9, isArray, isObject } from '../../util/index'
+import { isArray, isObject } from '../../util/index'
+import { setClass } from '../class-util'
 
 function updateClass (oldVnode, vnode) {
   let dynamicClass = vnode.data.class
   let staticClass = vnode.data.staticClass
-  if (staticClass || dynamicClass) {
+  const el = vnode.elm
+  const activeClass = el._activeClass
+  if (staticClass || dynamicClass || activeClass) {
     dynamicClass = genClass(dynamicClass)
-    let cls = staticClass
-      ? staticClass + (dynamicClass ? ' ' + dynamicClass : '')
-      : dynamicClass
+    const cls = concatClass(concatClass(staticClass, dynamicClass), activeClass)
     if (cls !== oldVnode.class) {
-      setClass(vnode.elm, cls)
+      setClass(el, cls)
     }
     vnode.class = cls
   }
 }
 
+function concatClass (a, b) {
+  return a ? b ? (a + ' ' + b) : a : (b || '')
+}
+
 function genClass (data) {
   if (!data) {
     return ''
@@ -38,25 +43,6 @@ function genClass (data) {
   }
 }
 
-/**
- * In IE9, setAttribute('class') will result in empty class
- * if the element also has the :class attribute; However in
- * PhantomJS, setting `className` does not work on SVG elements...
- * So we have to do a conditional check here.
- *
- * @param {Element} el
- * @param {String} cls
- */
-
-export function setClass (el, cls) {
-  /* istanbul ignore if */
-  if (isIE9 && !/svg$/.test(el.namespaceURI)) {
-    el.className = cls
-  } else {
-    el.setAttribute('class', cls)
-  }
-}
-
 export default {
   create: updateClass,
   update: updateClass

+ 3 - 3
src/runtime/dom-backend/modules/directives.js

@@ -1,11 +1,11 @@
 export default {
-  create: function (oldVnode, vnode) {
+  create: function bindDirectives (oldVnode, vnode) {
     applyDirectives(oldVnode, vnode, 'bind')
   },
-  update: function (oldVnode, vnode) {
+  update: function updateDirectives (oldVnode, vnode) {
     applyDirectives(oldVnode, vnode, 'update', true)
   },
-  destroy: function (vnode) {
+  destroy: function unbindDirectives (vnode) {
     applyDirectives(null, vnode, 'unbind')
   }
 }

+ 60 - 0
src/runtime/dom-backend/modules/transition.js

@@ -0,0 +1,60 @@
+import { addClass, removeClass } from '../class-util'
+import {
+  isIE9,
+  inBrowser,
+  transitionProp,
+  transitionEndEvent,
+  animationProp,
+  animationEndEvent
+} from '../../util/index'
+
+export default isIE9 ? {} : {
+  create: function applyEnterTransition (_, vnode) {
+    let data = vnode.data.transition
+    const el = vnode.elm
+    if (data != null) {
+      if (typeof data === 'string') {
+        // pure CSS
+        data = cssTransition(data)
+      }
+      // apply enter class
+      const enterClass = data.enterClass
+      if (enterClass) {
+        addClass(el, enterClass)
+        nextFrame(() => {
+          removeClass(el, enterClass)
+        })
+      }
+      const enterActiveClass = data.enterActiveClass
+      if (enterActiveClass) {
+        el._activeClass = enterActiveClass
+        addClass(el, enterActiveClass)
+        el.addEventListener(transitionEndEvent, () => {
+          el._activeClass = null
+          removeClass(el, enterActiveClass)
+        })
+      }
+    }
+  },
+
+  remove: function applyLeaveTransition (vnode, rm) {
+
+  }
+}
+
+const raf = (inBrowser && window.requestAnimationFrame) || setTimeout
+function nextFrame (fn) {
+  raf(() => {
+    raf(fn)
+  })
+}
+
+function cssTransition (name) {
+  name = name || 'v'
+  return {
+    enterClass: `${name}-enter`,
+    leaveClass: `${name}-leave`,
+    enterActiveClass: `${name}-enter-active`,
+    leaveActiveClass: `${name}-leave-active`
+  }
+}

+ 1 - 1
src/runtime/dom-backend/node-ops.js

@@ -1,4 +1,4 @@
-const svgNS = 'http://www.w3.org/2000/svg'
+export const svgNS = 'http://www.w3.org/2000/svg'
 
 export function createElement (tagName) {
   return document.createElement(tagName)

+ 7 - 0
src/runtime/instance/render.js

@@ -34,6 +34,13 @@ export function renderMixin (Vue) {
     return resolveAsset(this.$options, 'directives', id, true)
   }
 
+  // resolve transition
+  Vue.prototype.__resolveTransition__ = function (id) {
+    return id && typeof id === 'string'
+      ? resolveAsset(this.$options, 'transitions', id) || id
+      : id
+  }
+
   // toString for mustaches
   Vue.prototype.__toString__ = function (val) {
     return val == null