From b89f737c97c688ca7179f6e2204b35059b867238 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Thu, 21 Jan 2016 05:40:52 +0100 Subject: [PATCH] # This is a combination of 2 commits. # The first commit's message is: add a css-transitions player This commit also moves easing configuration to the different players. # This is the 2nd commit message: Modified eslintrc rules * Add eslint to devDependencies * Add eslint npm/gulp tasks * Modified eslintrc rules * eslinted source --- .eslintrc | 8 ++++-- .gitignore | 1 + gulpfile.js | 3 +++ package.json | 3 +++ src/core.js | 14 ++-------- src/css.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/flip.js | 2 ++ src/gsap.js | 12 +++++++++ src/raf.js | 12 +++++---- 9 files changed, 110 insertions(+), 19 deletions(-) create mode 100644 src/css.js diff --git a/.eslintrc b/.eslintrc index 89516eb..792ba45 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,10 +8,14 @@ "parser": "babel-eslint", "extends": "eslint:recommended", "rules": { + "indent": [2, 2], + "semi": [2, "always"], "max-len": [2, 80, 2], - "quotes": [2, "single"], + "quotes": [2, "single", "avoid-escape"], "eol-last": [0], "no-console": [0], - "no-unused-vars": [2, {"vars": "all", "args": "none"}] + "no-unused-vars": [2, {"vars": "all", "args": "none"}], + "space-before-function-paren": [2, "always"], + "curly": [2, "multi-or-nest", "consistent"] } } diff --git a/.gitignore b/.gitignore index 72c54e6..4cc78ff 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ docs demos node_modules +npm-debug.log diff --git a/gulpfile.js b/gulpfile.js index ad9bf49..ac8ee12 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -30,6 +30,7 @@ var gulp = require('gulp'), license = require('gulp-license'), replace = require('gulp-replace'), bump = require('gulp-bump'), + eslint = require('eslint'), shell = require('gulp-shell'); var version = null; @@ -130,3 +131,5 @@ gulp.task('docs', shell.task( gulp.task('dev', function() { return runSequence('clean', 'getversion', 'scripts', 'watch'); }); + +gulp.task('lint', shell.task(['npm run lint'])); diff --git a/package.json b/package.json index d232292..e285405 100644 --- a/package.json +++ b/package.json @@ -4,16 +4,19 @@ "description": "A FLIP helper", "main": "index.js", "scripts": { + "lint": "eslint --fix src/", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "Paul Lewis [Google]", "license": "Apache-2.0", "devDependencies": { + "babel-eslint": "^4.1.6", "babel-preset-es2015": "^6.1.18", "babelify": "^7.2.0", "browserify": "^12.0.1", "del": "^2.1.0", + "eslint": "^1.10.3", "gulp": "^3.9.0", "gulp-bump": "^1.0.0", "gulp-license": "^1.0.0", diff --git a/src/core.js b/src/core.js index 80f0b74..457ead0 100644 --- a/src/core.js +++ b/src/core.js @@ -144,7 +144,7 @@ export default class FLIP { flips.forEach(flip => flip.play(startTime)); } - } + }; } /** @@ -165,7 +165,7 @@ export default class FLIP { let defaults = { duration: 330, delay: 0, - easing: function (t) { return t; }, + easing: 'linear', transform: true, opacity: true, play: 'rAF' @@ -176,16 +176,6 @@ export default class FLIP { if (typeof config.element === 'undefined') throw new Error('Element must be provided.'); - // If the easing property is not a function, check for a TweenMax/Lite style - // object with a getRatio function, and, if that exists, use it, otherwise - // throw an error. - if (typeof config.easing !== 'function') { - if (typeof config.easing.getRatio !== 'undefined') { - config.easing = config.easing.getRatio; - } else { - throw new Error('Easing function must be provided.'); - } - } this.element_ = config.element; this.first_ = { diff --git a/src/css.js b/src/css.js new file mode 100644 index 0000000..d071590 --- /dev/null +++ b/src/css.js @@ -0,0 +1,74 @@ +'use strict'; + + +function once (element, type, handler) { + let off; + const proxy = e => { + if (e.target !== element) return; + handler(e); + off(); + }; + + off = () => element.removeEventListener(type, proxy); + element.addEventListener(type, proxy); + return off; +} + + +export default { + + /** + * Sets up the animation to use CSS transitions + * + * @private + */ + play_ () { + if (typeof this.easing_ === 'function') { + throw new Error( + 'The CSS player does not support passing easing as a function.' + + ' Expected a string or an array of cubic-bezier control points.' + ); + } + if (Array.isArray(this.easing_)) + this.easing_ = `cubic-bezier(${this.easing_.join(',')})`; + + const transitions = []; + if (this.updateTransform_) + transitions.push('transform'); + if (this.updateOpacity_) + transitions.push('opacity'); + + this.element_.offsetLeft; // pliz brawzer recalc stylz ! + + this.element_.style.transitionProperty = transitions.join(','); + this.element_.style.transitionTimingFunction = this.easing_; + this.element_.style.transitionDuration = `${this.duration_}ms`; + this.element_.style.transitionDelay = `${this.delay_}ms`; + + const cleanup = () => { + this.element_.style.transitionDuration = ''; + this.element_.style.transitionTimingFunction = ''; + this.element_.style.transitionProperty = ''; + this.element_.style.transitionDelay = ''; + this.element_.classList.remove('flip-animating'); + this.cleanUpAndFireEvent_(); + }; + + // Use CSS Transitions Level 2 events if available. + // https://drafts.csswg.org/css-transitions-2/#transition-events + once(this.element_, 'transitionstart', () => { + this.start_ = window.performance.now(); + }); + once(this.element_, 'transitionend', cleanup); + once(this.element_, 'transitioncancel', cleanup); + + requestAnimationFrame(() => { + this.start_ = window.performance.now() + this.delay_; + this.element_.classList.add('flip-animating'); + if (this.updateTransform_) + this.element_.style.transform = ''; + if (this.updateOpacity_) + this.element_.style.opacity = ''; + }); + } +}; diff --git a/src/flip.js b/src/flip.js index 1ff854d..204d6d7 100644 --- a/src/flip.js +++ b/src/flip.js @@ -15,9 +15,11 @@ */ import Core from './core'; +import CSS from './css'; import rAF from './raf'; import GSAP from './gsap'; +Core.extend('CSS', CSS); Core.extend('rAF', rAF); Core.extend('GSAP', GSAP); diff --git a/src/gsap.js b/src/gsap.js index 1dd4c29..8f8f0d8 100644 --- a/src/gsap.js +++ b/src/gsap.js @@ -39,6 +39,18 @@ export default { else throw new Error('GSAP requested, but TweenMax/Lite not available.'); + // If the easing property is not a function, check for a TweenMax/Lite style + // object with a getRatio function, and, if that exists, use it, otherwise + // throw an error. + if (this.easing_ === 'linear') { + this.easing_ = t => t; + } else if (typeof this.easing_ !== 'function') { + if (typeof this.easing_.getRatio !== 'undefined') + this.easing_ = this.easing_.getRatio; + else + throw new Error('Easing function must be provided.'); + } + let options = { ease: this.easing_, onComplete: this.cleanUpAndFireEvent_.bind(this) diff --git a/src/raf.js b/src/raf.js index 79587da..e38d9cf 100644 --- a/src/raf.js +++ b/src/raf.js @@ -26,6 +26,10 @@ export default { * FLIP animations. */ play_: function (startTime) { + if (this.easing_ === 'linear') + this.easing_ = t => t; + else if (typeof this.easing_ !== 'function') + throw new Error('An easing function must be provided.'); if (typeof startTime === 'undefined') this.start_ = window.performance.now() + this.delay_; @@ -60,14 +64,12 @@ export default { scale(${update.sx}, ${update.sy})`; } - if (this.updateOpacity_) { + if (this.updateOpacity_) this.element_.style.opacity = update.a; - } - if (time < 1) { + if (time < 1) requestAnimationFrame(this.update_); - } else { + else this.cleanUpAndFireEvent_(); - } } };