PHP Classes

File: public/js/lib/vue/src/transition/transition.js

Recommend this page to a friend!
  Classes of Sergey Beskorovayniy   Silex MVC Blog   public/js/lib/vue/src/transition/transition.js   Download  
File: public/js/lib/vue/src/transition/transition.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Silex MVC Blog
MVC based blog using on the Silex micro-framework
Author: By
Last change:
Date: 8 years ago
Size: 10,485 bytes
 

Contents

Class file image Download
import { pushJob } from './queue' import { on, off, bind, addClass, removeClass, cancellable, transitionEndEvent, animationEndEvent, transitionProp, animationProp, warn, inBrowser } from '../util/index' const TYPE_TRANSITION = 'transition' const TYPE_ANIMATION = 'animation' const transDurationProp = transitionProp + 'Duration' const animDurationProp = animationProp + 'Duration' /** * If a just-entered element is applied the * leave class while its enter transition hasn't started yet, * and the transitioned property has the same value for both * enter/leave, then the leave transition will be skipped and * the transitionend event never fires. This function ensures * its callback to be called after a transition has started * by waiting for double raf. * * It falls back to setTimeout on devices that support CSS * transitions but not raf (e.g. Android 4.2 browser) - since * these environments are usually slow, we are giving it a * relatively large timeout. */ const raf = inBrowser && window.requestAnimationFrame const waitForTransitionStart = raf /* istanbul ignore next */ ? function (fn) { raf(function () { raf(fn) }) } : function (fn) { setTimeout(fn, 50) } /** * A Transition object that encapsulates the state and logic * of the transition. * * @param {Element} el * @param {String} id * @param {Object} hooks * @param {Vue} vm */ export default function Transition (el, id, hooks, vm) { this.id = id this.el = el this.enterClass = (hooks && hooks.enterClass) || id + '-enter' this.leaveClass = (hooks && hooks.leaveClass) || id + '-leave' this.hooks = hooks this.vm = vm // async state this.pendingCssEvent = this.pendingCssCb = this.cancel = this.pendingJsCb = this.op = this.cb = null this.justEntered = false this.entered = this.left = false this.typeCache = {} // check css transition type this.type = hooks && hooks.type /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production') { if ( this.type && this.type !== TYPE_TRANSITION && this.type !== TYPE_ANIMATION ) { warn( 'invalid CSS transition type for transition="' + this.id + '": ' + this.type, vm ) } } // bind var self = this ;['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone'] .forEach(function (m) { self[m] = bind(self[m], self) }) } var p = Transition.prototype /** * Start an entering transition. * * 1. enter transition triggered * 2. call beforeEnter hook * 3. add enter class * 4. insert/show element * 5. call enter hook (with possible explicit js callback) * 6. reflow * 7. based on transition type: * - transition: * remove class now, wait for transitionend, * then done if there's no explicit js callback. * - animation: * wait for animationend, remove class, * then done if there's no explicit js callback. * - no css transition: * done now if there's no explicit js callback. * 8. wait for either done or js callback, then call * afterEnter hook. * * @param {Function} op - insert/show the element * @param {Function} [cb] */ p.enter = function (op, cb) { this.cancelPending() this.callHook('beforeEnter') this.cb = cb addClass(this.el, this.enterClass) op() this.entered = false this.callHookWithCb('enter') if (this.entered) { return // user called done synchronously. } this.cancel = this.hooks && this.hooks.enterCancelled pushJob(this.enterNextTick) } /** * The "nextTick" phase of an entering transition, which is * to be pushed into a queue and executed after a reflow so * that removing the class can trigger a CSS transition. */ p.enterNextTick = function () { // prevent transition skipping this.justEntered = true waitForTransitionStart(() => { this.justEntered = false }) var enterDone = this.enterDone var type = this.getCssTransitionType(this.enterClass) if (!this.pendingJsCb) { if (type === TYPE_TRANSITION) { // trigger transition by removing enter class now removeClass(this.el, this.enterClass) this.setupCssCb(transitionEndEvent, enterDone) } else if (type === TYPE_ANIMATION) { this.setupCssCb(animationEndEvent, enterDone) } else { enterDone() } } else if (type === TYPE_TRANSITION) { removeClass(this.el, this.enterClass) } } /** * The "cleanup" phase of an entering transition. */ p.enterDone = function () { this.entered = true this.cancel = this.pendingJsCb = null removeClass(this.el, this.enterClass) this.callHook('afterEnter') if (this.cb) this.cb() } /** * Start a leaving transition. * * 1. leave transition triggered. * 2. call beforeLeave hook * 3. add leave class (trigger css transition) * 4. call leave hook (with possible explicit js callback) * 5. reflow if no explicit js callback is provided * 6. based on transition type: * - transition or animation: * wait for end event, remove class, then done if * there's no explicit js callback. * - no css transition: * done if there's no explicit js callback. * 7. wait for either done or js callback, then call * afterLeave hook. * * @param {Function} op - remove/hide the element * @param {Function} [cb] */ p.leave = function (op, cb) { this.cancelPending() this.callHook('beforeLeave') this.op = op this.cb = cb addClass(this.el, this.leaveClass) this.left = false this.callHookWithCb('leave') if (this.left) { return // user called done synchronously. } this.cancel = this.hooks && this.hooks.leaveCancelled // only need to handle leaveDone if // 1. the transition is already done (synchronously called // by the user, which causes this.op set to null) // 2. there's no explicit js callback if (this.op && !this.pendingJsCb) { // if a CSS transition leaves immediately after enter, // the transitionend event never fires. therefore we // detect such cases and end the leave immediately. if (this.justEntered) { this.leaveDone() } else { pushJob(this.leaveNextTick) } } } /** * The "nextTick" phase of a leaving transition. */ p.leaveNextTick = function () { var type = this.getCssTransitionType(this.leaveClass) if (type) { var event = type === TYPE_TRANSITION ? transitionEndEvent : animationEndEvent this.setupCssCb(event, this.leaveDone) } else { this.leaveDone() } } /** * The "cleanup" phase of a leaving transition. */ p.leaveDone = function () { this.left = true this.cancel = this.pendingJsCb = null this.op() removeClass(this.el, this.leaveClass) this.callHook('afterLeave') if (this.cb) this.cb() this.op = null } /** * Cancel any pending callbacks from a previously running * but not finished transition. */ p.cancelPending = function () { this.op = this.cb = null var hasPending = false if (this.pendingCssCb) { hasPending = true off(this.el, this.pendingCssEvent, this.pendingCssCb) this.pendingCssEvent = this.pendingCssCb = null } if (this.pendingJsCb) { hasPending = true this.pendingJsCb.cancel() this.pendingJsCb = null } if (hasPending) { removeClass(this.el, this.enterClass) removeClass(this.el, this.leaveClass) } if (this.cancel) { this.cancel.call(this.vm, this.el) this.cancel = null } } /** * Call a user-provided synchronous hook function. * * @param {String} type */ p.callHook = function (type) { if (this.hooks && this.hooks[type]) { this.hooks[type].call(this.vm, this.el) } } /** * Call a user-provided, potentially-async hook function. * We check for the length of arguments to see if the hook * expects a `done` callback. If true, the transition's end * will be determined by when the user calls that callback; * otherwise, the end is determined by the CSS transition or * animation. * * @param {String} type */ p.callHookWithCb = function (type) { var hook = this.hooks && this.hooks[type] if (hook) { if (hook.length > 1) { this.pendingJsCb = cancellable(this[type + 'Done']) } hook.call(this.vm, this.el, this.pendingJsCb) } } /** * Get an element's transition type based on the * calculated styles. * * @param {String} className * @return {Number} */ p.getCssTransitionType = function (className) { /* istanbul ignore if */ if ( !transitionEndEvent || // skip CSS transitions if page is not visible - // this solves the issue of transitionend events not // firing until the page is visible again. // pageVisibility API is supported in IE10+, same as // CSS transitions. document.hidden || // explicit js-only transition (this.hooks && this.hooks.css === false) || // element is hidden isHidden(this.el) ) { return } var type = this.type || this.typeCache[className] if (type) return type var inlineStyles = this.el.style var computedStyles = window.getComputedStyle(this.el) var transDuration = inlineStyles[transDurationProp] || computedStyles[transDurationProp] if (transDuration && transDuration !== '0s') { type = TYPE_TRANSITION } else { var animDuration = inlineStyles[animDurationProp] || computedStyles[animDurationProp] if (animDuration && animDuration !== '0s') { type = TYPE_ANIMATION } } if (type) { this.typeCache[className] = type } return type } /** * Setup a CSS transitionend/animationend callback. * * @param {String} event * @param {Function} cb */ p.setupCssCb = function (event, cb) { this.pendingCssEvent = event var self = this var el = this.el var onEnd = this.pendingCssCb = function (e) { if (e.target === el) { off(el, event, onEnd) self.pendingCssEvent = self.pendingCssCb = null if (!self.pendingJsCb && cb) { cb() } } } on(el, event, onEnd) } /** * Check if an element is hidden - in that case we can just * skip the transition alltogether. * * @param {Element} el * @return {Boolean} */ function isHidden (el) { if (/svg$/.test(el.namespaceURI)) { // SVG elements do not have offset(Width|Height) // so we need to check the client rect var rect = el.getBoundingClientRect() return !(rect.width || rect.height) } else { return !( el.offsetWidth || el.offsetHeight || el.getClientRects().length ) } }