diff --git a/src/platforms/weex/runtime/components/index.js b/src/platforms/weex/runtime/components/index.js index 8631fa65..6bfe5780 100644 --- a/src/platforms/weex/runtime/components/index.js +++ b/src/platforms/weex/runtime/components/index.js @@ -1,5 +1,7 @@ -import Transition from 'web/runtime/components/transition' +import Transition from './transition' +import TransitionGroup from './transition-group' export default { - Transition + Transition, + TransitionGroup } diff --git a/src/platforms/weex/runtime/components/transition-group.js b/src/platforms/weex/runtime/components/transition-group.js new file mode 100644 index 00000000..4e436889 --- /dev/null +++ b/src/platforms/weex/runtime/components/transition-group.js @@ -0,0 +1,151 @@ +import { warn, extend } from 'core/util/index' +import { transitionProps, extractTransitionData } from './transition' + +const props = extend({ + tag: String, + moveClass: String +}, transitionProps) + +delete props.mode + +export default { + props, + + created () { + const dom = this._requireWeexModule('dom') + this.getPosition = el => new Promise((resolve, reject) => { + dom.getComponentRect(el.ref, res => { + if (!res.result) { + reject(new Error(`failed to get rect for element: ${el.tag}`)) + } else { + resolve(res.size) + } + }) + }) + + const animation = this._requireWeexModule('animation') + this.animate = (el, options) => new Promise(resolve => { + animation.transition(el.ref, options, resolve) + }) + }, + + render (h) { + const tag = this.tag || this.$vnode.data.tag || 'span' + const map = Object.create(null) + const prevChildren = this.prevChildren = this.children + const rawChildren = this.$slots.default || [] + const children = this.children = [] + const transitionData = extractTransitionData(this) + + for (let i = 0; i < rawChildren.length; i++) { + const c = rawChildren[i] + if (c.tag) { + if (c.key != null && String(c.key).indexOf('__vlist') !== 0) { + children.push(c) + map[c.key] = c + ;(c.data || (c.data = {})).transition = transitionData + } else if (process.env.NODE_ENV !== 'production') { + const opts = c.componentOptions + const name = opts + ? (opts.Ctor.options.name || opts.tag) + : c.tag + warn(` children must be keyed: <${name}>`) + } + } + } + + if (prevChildren) { + const kept = [] + const removed = [] + const positionPromises = [] + prevChildren.forEach(c => { + c.data.transition = transitionData + positionPromises.push(this.getPosition(c.elm).then(pos => { + c.data.pos = pos + })) + if (map[c.key]) { + kept.push(c) + } else { + removed.push(c) + } + }) + this.kept = h(tag, null, kept) + this.removed = removed + this.pendingPositions = Promise.all(positionPromises) + } + + return h(tag, null, children) + }, + + beforeUpdate () { + // force removing pass + this.__patch__( + this._vnode, + this.kept, + false, // hydrating + true // removeOnly (!important, avoids unnecessary moves) + ) + this._vnode = this.kept + }, + + updated () { + const children = this.prevChildren + const moveClass = this.moveClass || ((this.name || 'v') + '-move') + const moveData = children.length && this.getMoveData(children[0].context, moveClass) + if (!moveData) { + return + } + + children.forEach(callPendingCbs) + this.pendingPositions.then(() => Promise.all(children.map(c => { + // record new position + return this.getPosition(c.elm).then(pos => { + c.data.newPos = pos + }) + }))).then(() => Promise.all(children.map(c => { + const oldPos = c.data.pos + const newPos = c.data.newPos + const dx = oldPos.left - newPos.left + const dy = oldPos.top - newPos.top + if (dx || dy) { + c.data.moved = true + return this.animate(c.elm, { + styles: { + transform: `translate(${dx}px,${dy}px)` + } + }) + } + }))).then(() => { + children.forEach(c => { + if (c.data.moved) { + this.animate(c.elm, { + styles: { + transform: '' + }, + duration: moveData.duration || 0, + delay: moveData.delay || 0, + timingFunction: moveData.timingFunction || 'linear' + }) + } + }) + }) + }, + + methods: { + getMoveData (context, moveClass) { + const stylesheet = context.$options.style || {} + return stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][moveClass] + } + } +} + +function callPendingCbs (c) { + /* istanbul ignore if */ + if (c.elm._moveCb) { + c.elm._moveCb() + } + /* istanbul ignore if */ + if (c.elm._enterCb) { + c.elm._enterCb() + } +} diff --git a/src/platforms/weex/runtime/components/transition.js b/src/platforms/weex/runtime/components/transition.js new file mode 100644 index 00000000..a6955dbb --- /dev/null +++ b/src/platforms/weex/runtime/components/transition.js @@ -0,0 +1,2 @@ +// reuse same transition component logic from web +export * from 'web/runtime/components/transition' diff --git a/src/platforms/weex/runtime/modules/transition.js b/src/platforms/weex/runtime/modules/transition.js index 9abb4ce2..cae15714 100644 --- a/src/platforms/weex/runtime/modules/transition.js +++ b/src/platforms/weex/runtime/modules/transition.js @@ -1,3 +1,4 @@ +import { warn } from 'core/util/debug' import { extend, once, noop } from 'shared/util' import { activeInstance } from 'core/instance/lifecycle' import { resolveTransition } from 'web/runtime/transition-util' @@ -233,11 +234,12 @@ function getEnterTargetState (el, stylesheet, startClass, endClass, activeClass, for (const key in startState) { targetState[key] = el.style[key] if ( + process.env.NODE_ENV !== 'production' && targetState[key] == null && (!activeState || activeState[key] == null) && (!endState || endState[key] == null) ) { - console.log( + warn( `transition property "${key}" is declared in enter starting class (.${startClass}), ` + `but not declared anywhere in enter ending class (.${endClass}), ` + `enter active cass (.${activeClass}) or the element's default styling. ` +