├── .gitignore ├── README.md ├── app-loading.css ├── app-loading.js ├── app-loading.min.css ├── app-loading.min.js ├── app-loading.min.js.map ├── bower.json ├── gulpfile.js ├── index.html ├── lib └── app-loading.js ├── package.json └── test ├── index.html └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # app-loading 2 | 3 | [![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) 4 | [![NPM](https://nodei.co/npm/app-loading.png)](https://nodei.co/npm/app-loading/) 5 | 6 | ## Quick start 7 | 8 | **bower install app-loading** works with browsers 9 | 10 | ```javascript 11 | appLoading.start() 12 | appLoading.stop() 13 | ``` 14 | 15 | **npm install app-loading** works with webpack 16 | 17 | ```javascript 18 | import appLoading from 'app-loading' 19 | appLoading.start() 20 | ``` 21 | 22 | ## Advance usage 23 | 24 | ```javascript 25 | // set up some custom color 26 | // globally 27 | appLoading.setColor('#333') 28 | appLoading.start() 29 | // reset 30 | appLoading.setColor(null) 31 | 32 | // or for temporary use, this will override the `appLoading.setColor` setting 33 | appLoading.start('#f83') 34 | appLoading.start('yellow') 35 | appLoading.start('rgba(76, 207, 177, 0.7)') 36 | 37 | // use on server side 38 | if (typeof window !== 'undefined') { 39 | import appLoading from 'app-loading' 40 | appLoading.start() 41 | } 42 | ``` 43 | 44 | ## Changelog 45 | 46 | **v0.0.5** - 2015/09/03 47 | 48 | - Fix `setColor` and add test 49 | 50 | **v0.0.4** - 2015/08/22 51 | 52 | - Rewrite in ES6 53 | 54 | **v0.0.3** - 2015/07/20 55 | 56 | - add `setColor` method 57 | 58 | **v0.0.2** 59 | 60 | - Clean code 61 | - Update demo 62 | 63 | **v0.0.1** 64 | 65 | - Prototype. 66 | - Medium style loading bar. 67 | 68 | ## License 69 | 70 | This project is released under SOX license that means you can do whatever you want to do, but you have to open source your copy on github if you let the public uses it. All copies should be released under the same license. The owner of each copy is only reponsible for his own copy, not for the parents, not for the children. 71 | 72 | permitted use: 73 | fork on github 74 | change 75 | do evil with your copy 76 | 77 | prohibted use: 78 | do evil with copies not of your own 79 | open source your copy without declaring your parent copy 80 | -------------------------------------------------------------------------------- /app-loading.css: -------------------------------------------------------------------------------- 1 | .loading-bar { 2 | position: fixed; 3 | display: none; 4 | top: 0; 5 | left: 0; 6 | right: 0; 7 | height: 2px; 8 | z-index: 99999; 9 | background: #60d778; 10 | -webkit-transform: translateX(100%); 11 | -moz-transform: translateX(100%); 12 | -o-transform: translateX(100%); 13 | transform: translateX(100%); 14 | } 15 | .app-loading .loading-bar { 16 | display: block; 17 | -webkit-animation: shift-rightwards 1s ease-in-out infinite; 18 | -moz-animation: shift-rightwards 1s ease-in-out infinite; 19 | -ms-animation: shift-rightwards 1s ease-in-out infinite; 20 | -o-animation: shift-rightwards 1s ease-in-out infinite; 21 | animation: shift-rightwards 1s ease-in-out infinite; 22 | -webkit-animation-delay: .4s; 23 | -moz-animation-delay: .4s; 24 | -o-animation-delay: .4s; 25 | animation-delay: .4s; 26 | } 27 | @-webkit-keyframes shift-rightwards { 28 | 0% { 29 | -webkit-transform: translateX(-100%); 30 | -moz-transform: translateX(-100%); 31 | -o-transform: translateX(-100%); 32 | transform: translateX(-100%); 33 | } 34 | 40% { 35 | -webkit-transform: translateX(0%); 36 | -moz-transform: translateX(0%); 37 | -o-transform: translateX(0%); 38 | transform: translateX(0%); 39 | } 40 | 60% { 41 | -webkit-transform: translateX(0%); 42 | -moz-transform: translateX(0%); 43 | -o-transform: translateX(0%); 44 | transform: translateX(0%); 45 | } 46 | 100% { 47 | -webkit-transform: translateX(100%); 48 | -moz-transform: translateX(100%); 49 | -o-transform: translateX(100%); 50 | transform: translateX(100%); 51 | } 52 | } 53 | @-moz-keyframes shift-rightwards { 54 | 0% { 55 | -webkit-transform: translateX(-100%); 56 | -moz-transform: translateX(-100%); 57 | -o-transform: translateX(-100%); 58 | transform: translateX(-100%); 59 | } 60 | 40% { 61 | -webkit-transform: translateX(0%); 62 | -moz-transform: translateX(0%); 63 | -o-transform: translateX(0%); 64 | transform: translateX(0%); 65 | } 66 | 60% { 67 | -webkit-transform: translateX(0%); 68 | -moz-transform: translateX(0%); 69 | -o-transform: translateX(0%); 70 | transform: translateX(0%); 71 | } 72 | 100% { 73 | -webkit-transform: translateX(100%); 74 | -moz-transform: translateX(100%); 75 | -o-transform: translateX(100%); 76 | transform: translateX(100%); 77 | } 78 | } 79 | @-o-keyframes shift-rightwards { 80 | 0% { 81 | -webkit-transform: translateX(-100%); 82 | -moz-transform: translateX(-100%); 83 | -o-transform: translateX(-100%); 84 | transform: translateX(-100%); 85 | } 86 | 40% { 87 | -webkit-transform: translateX(0%); 88 | -moz-transform: translateX(0%); 89 | -o-transform: translateX(0%); 90 | transform: translateX(0%); 91 | } 92 | 60% { 93 | -webkit-transform: translateX(0%); 94 | -moz-transform: translateX(0%); 95 | -o-transform: translateX(0%); 96 | transform: translateX(0%); 97 | } 98 | 100% { 99 | -webkit-transform: translateX(100%); 100 | -moz-transform: translateX(100%); 101 | -o-transform: translateX(100%); 102 | transform: translateX(100%); 103 | } 104 | } 105 | @keyframes shift-rightwards { 106 | 0% { 107 | -webkit-transform: translateX(-100%); 108 | -moz-transform: translateX(-100%); 109 | -o-transform: translateX(-100%); 110 | transform: translateX(-100%); 111 | } 112 | 40% { 113 | -webkit-transform: translateX(0%); 114 | -moz-transform: translateX(0%); 115 | -o-transform: translateX(0%); 116 | transform: translateX(0%); 117 | } 118 | 60% { 119 | -webkit-transform: translateX(0%); 120 | -moz-transform: translateX(0%); 121 | -o-transform: translateX(0%); 122 | transform: translateX(0%); 123 | } 124 | 100% { 125 | -webkit-transform: translateX(100%); 126 | -moz-transform: translateX(100%); 127 | -o-transform: translateX(100%); 128 | transform: translateX(100%); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app-loading.js: -------------------------------------------------------------------------------- 1 | /* 2 | * app-loading 3 | * (c) 2015 4 | * github.com/egoist/app-loading 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | (function () { 14 | 15 | var definition = function definition(W, D) { 16 | var AppLoading = (function () { 17 | function AppLoading() { 18 | _classCallCheck(this, AppLoading); 19 | 20 | this.opts = { 21 | className: 'app-loading', 22 | loadingBar: '.loading-bar', 23 | color: null 24 | }; 25 | } 26 | 27 | _createClass(AppLoading, [{ 28 | key: 'start', 29 | value: function start(color) { 30 | this.showBar(color); 31 | return this; 32 | } 33 | }, { 34 | key: 'stop', 35 | value: function stop() { 36 | this.hideBar(); 37 | return this; 38 | } 39 | }, { 40 | key: 'showBar', 41 | value: function showBar(color) { 42 | var bar = this.getBar(); 43 | if (this.opts.color) { 44 | bar.style.backgroundColor = this.opts.color; 45 | } 46 | if (color) { 47 | bar.style.backgroundColor = color; 48 | } 49 | D.querySelector('body').classList.add(this.opts.className); 50 | } 51 | }, { 52 | key: 'hideBar', 53 | value: function hideBar() { 54 | D.querySelector('body').classList.remove(this.opts.className); 55 | this.getBar().style.backgroundColor = null; 56 | } 57 | }, { 58 | key: 'getBar', 59 | value: function getBar() { 60 | var bar = D.querySelector(this.opts.loadingBar); 61 | if (!bar) { 62 | bar = this.initBar(); 63 | } 64 | return bar; 65 | } 66 | }, { 67 | key: 'initBar', 68 | value: function initBar() { 69 | var bar = D.createElement('div'); 70 | bar.className = this.opts.loadingBar.substring(1); 71 | D.body.appendChild(bar); 72 | return bar; 73 | } 74 | }, { 75 | key: 'setColor', 76 | value: function setColor(color) { 77 | this.opts.color = color; 78 | this.getBar().style.backgroundColor = color; 79 | return this; 80 | } 81 | }]); 82 | 83 | return AppLoading; 84 | })(); 85 | 86 | return new AppLoading(); 87 | };(function (context, name, definition) { 88 | if (typeof module !== 'undefined') { 89 | module.exports = definition; 90 | } else if (typeof context !== 'undefined') { 91 | context[name] = definition; 92 | } 93 | })(window, 'appLoading', definition(window, document)); 94 | })(); -------------------------------------------------------------------------------- /app-loading.min.css: -------------------------------------------------------------------------------- 1 | .loading-bar{position:fixed;display:none;top:0;left:0;right:0;height:2px;z-index:99999;background:#60d778;-webkit-transform:translateX(100%);-moz-transform:translateX(100%);-o-transform:translateX(100%);transform:translateX(100%)}.app-loading .loading-bar{display:block;-webkit-animation:shift-rightwards 1s ease-in-out infinite;-moz-animation:shift-rightwards 1s ease-in-out infinite;-ms-animation:shift-rightwards 1s ease-in-out infinite;-o-animation:shift-rightwards 1s ease-in-out infinite;animation:shift-rightwards 1s ease-in-out infinite;-webkit-animation-delay:.4s;-moz-animation-delay:.4s;-o-animation-delay:.4s;animation-delay:.4s}@-webkit-keyframes shift-rightwards{0%{-webkit-transform:translateX(-100%);-moz-transform:translateX(-100%);-o-transform:translateX(-100%);transform:translateX(-100%)}40%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}60%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}100%{-webkit-transform:translateX(100%);-moz-transform:translateX(100%);-o-transform:translateX(100%);transform:translateX(100%)}}@-moz-keyframes shift-rightwards{0%{-webkit-transform:translateX(-100%);-moz-transform:translateX(-100%);-o-transform:translateX(-100%);transform:translateX(-100%)}40%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}60%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}100%{-webkit-transform:translateX(100%);-moz-transform:translateX(100%);-o-transform:translateX(100%);transform:translateX(100%)}}@-o-keyframes shift-rightwards{0%{-webkit-transform:translateX(-100%);-moz-transform:translateX(-100%);-o-transform:translateX(-100%);transform:translateX(-100%)}40%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}60%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}100%{-webkit-transform:translateX(100%);-moz-transform:translateX(100%);-o-transform:translateX(100%);transform:translateX(100%)}}@keyframes shift-rightwards{0%{-webkit-transform:translateX(-100%);-moz-transform:translateX(-100%);-o-transform:translateX(-100%);transform:translateX(-100%)}40%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}60%{-webkit-transform:translateX(0%);-moz-transform:translateX(0%);-o-transform:translateX(0%);transform:translateX(0%)}100%{-webkit-transform:translateX(100%);-moz-transform:translateX(100%);-o-transform:translateX(100%);transform:translateX(100%)}} 2 | -------------------------------------------------------------------------------- /app-loading.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function e(e,t){for(var o=0;o" 7 | ], 8 | "license": "MIT", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "test", 14 | "tests", 15 | "test.*", 16 | "lib", 17 | "index.html" 18 | ] 19 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp') 4 | var babel = require('gulp-babel') 5 | var serve = require('gulp-serve') 6 | var sourcemaps = require('gulp-sourcemaps') 7 | var uglify = require('gulp-uglify') 8 | var rename = require('gulp-rename') 9 | var qunit = require('gulp-qunit') 10 | 11 | gulp.task('serve', serve({ 12 | root: ['.'], 13 | port: 3001 14 | })) 15 | 16 | gulp.task('babel', function() { 17 | var stream = gulp.src('lib/app-loading.js') 18 | .pipe(babel()) 19 | .pipe(gulp.dest(__dirname)); 20 | return stream; 21 | }) 22 | 23 | gulp.task('js', ['babel'], function() { 24 | var stream = gulp.src('./app-loading.js') 25 | .pipe(sourcemaps.init()) 26 | .pipe(uglify()) 27 | .pipe(rename({ 28 | suffix: '.min' 29 | })) 30 | .pipe(sourcemaps.write('.')) 31 | .pipe(gulp.dest('.')); 32 | return stream; 33 | }) 34 | 35 | gulp.task('test', function() { 36 | gulp.src('./test/index.html') 37 | .pipe(qunit({ 38 | timeout: 20 39 | })) 40 | }) 41 | 42 | gulp.task('watch', function() { 43 | return gulp.watch('./lib/*.js', ['js']) 44 | }) 45 | 46 | gulp.task('build', ['js']) 47 | 48 | gulp.task('default', ['build', 'serve', 'test', 'watch']) 49 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | egoist/app-loading 7 | 8 | 50 | 51 | 52 |
53 |

app-loading

54 |

Focus on Medium-like app loading style

55 | 56 | 57 | 58 | 59 | 60 | 64 |
65 | 66 | 67 | 68 | 69 |
70 |
71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /lib/app-loading.js: -------------------------------------------------------------------------------- 1 | /* 2 | * app-loading 3 | * (c) 2015 4 | * github.com/egoist/app-loading 5 | */ 6 | 7 | () => { 8 | 9 | let definition = (W, D) => { 10 | class AppLoading { 11 | constructor () { 12 | this.opts = { 13 | className: 'app-loading', 14 | loadingBar: '.loading-bar', 15 | color: null 16 | } 17 | } 18 | 19 | start (color) { 20 | this.showBar(color) 21 | return this 22 | } 23 | 24 | stop () { 25 | this.hideBar() 26 | return this 27 | } 28 | 29 | showBar (color) { 30 | let bar = this.getBar() 31 | if (this.opts.color) { 32 | bar.style.backgroundColor = this.opts.color 33 | } 34 | if (color) { 35 | bar.style.backgroundColor = color 36 | } 37 | D.querySelector('body').classList.add(this.opts.className) 38 | } 39 | 40 | hideBar () { 41 | D.querySelector('body').classList.remove(this.opts.className) 42 | this.getBar().style.backgroundColor = null 43 | } 44 | 45 | getBar () { 46 | let bar = D.querySelector(this.opts.loadingBar) 47 | if (!bar) { 48 | bar = this.initBar() 49 | } 50 | return bar 51 | } 52 | 53 | initBar () { 54 | let bar = D.createElement('div') 55 | bar.className = this.opts.loadingBar.substring(1) 56 | D.body.appendChild(bar) 57 | return bar 58 | } 59 | 60 | setColor (color) { 61 | this.opts.color = color 62 | this.getBar().style.backgroundColor = color 63 | return this 64 | } 65 | 66 | } 67 | 68 | return new AppLoading() 69 | } 70 | 71 | ;(context, name, definition) => { 72 | if (typeof module !== 'undefined') { 73 | module.exports = definition 74 | } else if (typeof context !== 'undefined') { 75 | context[name] = definition 76 | } 77 | 78 | }(window, 'appLoading', definition(window, document)) 79 | 80 | }() 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-loading", 3 | "version": "0.0.51", 4 | "description": "Focus on Medium-like app loading style", 5 | "main": "app-loading.js", 6 | "scripts": { 7 | "test": "gulp test", 8 | "dev": "gulp", 9 | "build": "gulp build" 10 | }, 11 | "keywords": [ 12 | "medium", 13 | "progress", 14 | "loading" 15 | ], 16 | "author": "AprilOrange ", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "babel-eslint": "^4.1.0", 20 | "gulp": "^3.8.11", 21 | "gulp-babel": "^5.2.1", 22 | "gulp-qunit": "^1.2.1", 23 | "gulp-rename": "^1.2.2", 24 | "gulp-serve": "^1.0.0", 25 | "gulp-sourcemaps": "^1.5.2", 26 | "gulp-uglify": "^1.2.0" 27 | }, 28 | "standard": { 29 | "parser": "babel-eslint" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | test('loading bar is visible', function() { 2 | appLoading.start() 3 | ok($('.loading-bar').is(':visible')) 4 | }) 5 | 6 | test('set loading bar color globally', function() { 7 | appLoading.setColor('#333') 8 | ok(rgb2hex($('.loading-bar').css('background-color')) == '#333333') 9 | }) 10 | 11 | test('restart loading with red color', function() { 12 | appLoading.stop() 13 | appLoading.start('#FF002F') 14 | ok(rgb2hex($('.loading-bar').css('background-color')) == '#FF002F') 15 | }) 16 | 17 | test('chain api', function () { 18 | var loading = appLoading.start('yellow').stop().setColor('orange').start() 19 | ok(typeof loading === 'object') 20 | }) 21 | 22 | function rgb2hex(orig){ 23 | var rgb = orig.replace(/\s/g,'').match(/^rgba?\((\d+),(\d+),(\d+)/i); 24 | var hex = (rgb && rgb.length === 4) ? "#" + 25 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 26 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 27 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : orig; 28 | 29 | return hex.toUpperCase() 30 | 31 | } 32 | --------------------------------------------------------------------------------