├── .gitignore ├── README.md ├── bower.json ├── build ├── css │ ├── material-refresh.css │ └── material-refresh.min.css └── js │ ├── material-refresh.js │ └── material-refresh.min.js ├── demo ├── index.html ├── js │ └── demo.js ├── libs │ ├── zepto.js │ └── zepto.min.js ├── styles │ ├── css │ │ └── demo.css │ └── images │ │ ├── above.gif │ │ ├── below-color.gif │ │ ├── below.gif │ │ ├── button-action.gif │ │ ├── qr-above.png │ │ └── qr-below.png ├── test-jquery.html └── type1.html ├── gulpfile.js ├── material-refresh.css ├── material-refresh.min.css ├── material-refresh.min.js ├── package.json └── src ├── css └── material-refresh.styl └── js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /*.swp 3 | /*.log 4 | /dev 5 | *.swp 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Material Refresh 2 | 3 | > High Performance 4 | 5 | > Mobile only 6 | 7 | Google Material Design swipe (pull) to refresh. 8 | 9 | It uses CSS3 and JavaScript depend on [Zepto](https://github.com/madrobby/zepto) or [jQuery](https://github.com/jquery/jquery). 10 | 11 | Actually, it's easy to convert the dependent js library or just use the vanilla JavaScript. 12 | 13 | It's high performancenot which not impact the structure of sites. 14 | 15 | ## Types and preview 16 | 17 | ##### Type1: `Above surface` (default) 18 | 19 | 20 | 21 | ##### Type2: `Below surface` 22 | 23 | 24 | 25 | 26 | ##### Type3: `Button action` 27 | 28 | 29 | 30 | ## Demo 31 | 32 | #### [The Live Demo](http://lightningtgc.github.io/material-refresh/) 33 | 34 | ## Getting Started 35 | 36 | #### Install it 37 | 38 | Include `material-refresh.min.js` and `material-refresh.min.css` in your target html file. 39 | 40 | ```html 41 | 42 | 43 | 44 | ``` 45 | 46 | Cause it is a plugin for `Zepto` or `jQuery`, so we also need to include `Zepto` or `jQuery`: 47 | ```html 48 | 49 | 50 | 51 | 52 | ``` 53 | 54 | Usually, we will combine and compress all the css or js, depend on your needs. 55 | 56 | You can also install it via [Bower](https://github.com/bower/bower) or [npm](https://www.npmjs.com/): 57 | 58 | ``` 59 | bower install --save material-refresh 60 | ``` 61 | ``` 62 | npm install --save material-refresh 63 | ``` 64 | 65 | ## Basic usage 66 | 67 | Example for `Type1: Above surface (default)`: 68 | 69 | 1.Instantiation: 70 | 71 | ```js 72 | mRefresh(); 73 | ``` 74 | 75 | 2.Finish the refresh and hide it: 76 | 77 | ```js 78 | mRefresh.resolve(); 79 | ``` 80 | 81 | If you don't use this method, refesher will stop after the maxTime(Default: 6000ms). 82 | 83 | ## Relations of three types 84 | 85 | * `Type1` and `Type2` can not use in the same time. 86 | * `Type3` is depend on `Type1` or `Type2`, cause it will determine the refresher position 87 | * `Type3` and (`Type1` or `Type2`) can use in the same time. 88 | 89 | ## Advanced usage 90 | 91 | #### Options 92 | 93 | ```js 94 | // Default options 95 | var opts = { 96 | nav: '', //String, using for Type2 97 | scrollEl: '', //String 98 | onBegin: null, //Function 99 | onEnd: null, //Function 100 | top: '0px', //String 101 | theme: 'mui-blue-theme', //String 102 | index: 10001, //Number 103 | maxTime: 6000, //Number 104 | freeze: false //Boolen 105 | } 106 | mRefresh(opts); 107 | ``` 108 | 109 | ##### nav: 110 | 111 | -- Using for turn into `Type2`, refresh body will below the nav surface 112 | 113 | ```js 114 | // Example 115 | var opts = { 116 | nav: '#navMain' 117 | } 118 | ``` 119 | 120 | ##### scrollEl: 121 | 122 | -- Custom scroll wrapper element, decide which elemnt will allow trigger refresh action. 123 | 124 | -- Default:{ ios:document.body, android: document } 125 | 126 | ```js 127 | var opts = { 128 | scrollEl: '#mainWrapper' 129 | } 130 | ``` 131 | 132 | ##### onBegin: (Callback Function) 133 | 134 | -- Trigger when the refresh body `start` to rotate because of the right gesture(swipe). 135 | 136 | -- You can use this callback to pull ajax data or other action. 137 | 138 | ```js 139 | var opts = { 140 | onBegin: function(){ 141 | alert('Begin to rotate'); 142 | $.get('/whatevs.html', function(response){ 143 | $('#someDom').append(response); 144 | }); 145 | } 146 | } 147 | ``` 148 | 149 | ##### onEnd: (Callback Function) 150 | 151 | -- Trigger when `finished` the refresh 152 | 153 | -- Using like `onBegin` 154 | 155 | ##### top: 156 | 157 | -- Set `top` of the refresher. 158 | 159 | -- You can change its position finally by setting this option. 160 | 161 | -- Default{ `Type1` :'0px', `Type2`: depend on the height and top of the nav element } 162 | 163 | ```js 164 | var opts = { 165 | top: '50px' 166 | } 167 | ``` 168 | 169 | ##### theme: (Default: 'mui-blue-theme') 170 | 171 | -- Set color or custom style of the refresher. 172 | 173 | -- You can write your own style in css file by using the className like 'mui-somecolor-theme' 174 | 175 | ```js 176 | var opts = { 177 | theme: 'mui-red-theme' 178 | } 179 | ``` 180 | 181 | ##### index: 182 | 183 | -- Set `z-index` of the refresher to change it in z-space. 184 | 185 | -- Default { `Type1`: 10001, `Type2`: (the z-index of nav element) - 1} 186 | 187 | ```js 188 | var opts = { 189 | index: 99 190 | } 191 | ``` 192 | 193 | ##### maxTime: (Default: 6000ms) 194 | 195 | -- Refresher will stop after the maxTime if you don't use `mRefresh.resolve()` to stop it. 196 | 197 | -- You can change this maxTime to make it longer or shorter. 198 | 199 | ```js 200 | var opts = { 201 | maxTime: 2000 202 | } 203 | ``` 204 | 205 | ##### freeze: (Default: false) 206 | 207 | -- The touch event of the refresher will not trigger if freeze is true. 208 | 209 | -- You can use this option to prevent `Type1` or `Type2` and just allow `Type3: button action` 210 | 211 | ```js 212 | var opts = { 213 | freeze: true 214 | } 215 | ``` 216 | 217 | #### Type1: Above surface 218 | 219 | You can custom your own refresher like: 220 | 221 | ```js 222 | var opts = { 223 | maxTime: 3000, 224 | onBegin: function(){ 225 | $.get('/whatevs.html', function(response){ 226 | $('#someDom').append(response); 227 | }); 228 | }, 229 | onEnd: function(){ 230 | alert('Finish the refresh'); 231 | } 232 | } 233 | 234 | mRefresh(opts); 235 | ``` 236 | 237 | #### Type2: Below surface 238 | 239 | Use `Type2` by setting the option `nav` to the top of the elements: 240 | 241 | ```js 242 | // example 243 | var opts = { 244 | nav: '#navMain', 245 | onBegin: function(){ 246 | $.get('/whatevs.html', function(response){ 247 | $('#someDom').append(response); 248 | }); 249 | } 250 | } 251 | 252 | mRefresh(opts); 253 | ``` 254 | 255 | Then the refresher will below the surface of the `navMain` element. 256 | 257 | #### Type3: Button action 258 | 259 | If you had inited the refresher,you can bind the DOM event by using: 260 | ```js 261 | $('#buttonAction').on('tap', function(){ 262 | mRefresh.refresh(); 263 | }); 264 | ``` 265 | When you click the `buttonAction` element, the refresher will show. 266 | 267 | If you want to get some callback when start or stop to refresh,by using `onBegin` or `onEnd`: 268 | 269 | ```js 270 | $('#buttonAction').on('tap', function(){ 271 | var refreshOpts = { 272 | onBegin: function(){ 273 | // Do something 274 | $.get('/whatevs.html', function(response){ 275 | $('#someDom').append(response); 276 | }); 277 | }, 278 | onEnd: function(){ 279 | alert('Finish!') 280 | } 281 | } 282 | mRefresh.refresh(refreshOpts); 283 | }); 284 | ``` 285 | 286 | If you want not to trigger `Type1` or `Type2`, and just need `Type3`. 287 | 288 | ```js 289 | var opts = { 290 | freeze: true 291 | } 292 | mRefresh(opts); 293 | 294 | $('#buttonAction').on('tap', function(){ 295 | mRefresh.refresh(); 296 | }); 297 | ``` 298 | 299 | Or 300 | ```js 301 | mRefresh(); 302 | mRefresh.unbindEvents(); 303 | 304 | $('#buttonAction').on('tap', function(){ 305 | mRefresh.refresh(); 306 | }); 307 | ``` 308 | 309 | ## Browser support 310 | 311 | Android 3 + 312 | 313 | iOS 5 + 314 | 315 | ## Resource 316 | 317 | [Google Material Design](http://www.google.com/design/spec/patterns/swipe-to-refresh.html#swipe-to-refresh-swipe-to-refresh) 318 | 319 | ## License 320 | 321 | [MIT](http://opensource.org/licenses/mit-license.php) © [Gctang](https://github.com/lightningtgc) 322 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "material-refresh", 3 | "version": "0.1.0", 4 | "main": [ 5 | "material-refresh.min.js", 6 | "material-refresh.min.css" 7 | ], 8 | "scripts": [ 9 | "build/js/material-refresh.js" 10 | ], 11 | "styles": [ 12 | "build/css/material-refresh.css" 13 | ], 14 | "moduleType": [ 15 | "globals" 16 | ], 17 | "keywords": [ 18 | "Material", 19 | "Design", 20 | "pull", 21 | "to", 22 | "refresh", 23 | "CSS3" 24 | ], 25 | "authors": [ 26 | "gctang " 27 | ], 28 | "license": "MIT", 29 | "homepage": "http://lightningtgc.github.io/material-refresh/", 30 | "ignore": [ 31 | "**/.*", 32 | "node_modules", 33 | "bower_components", 34 | "test", 35 | "tests" 36 | ] 37 | } 38 | 39 | -------------------------------------------------------------------------------- /build/css/material-refresh.css: -------------------------------------------------------------------------------- 1 | .mui-refresh-main { 2 | position: absolute; 3 | left: 50%; 4 | margin-left: -25px; 5 | padding: 9px; 6 | z-index: 10001; 7 | background-color: #fff; 8 | overflow: hidden; 9 | border-radius: 999px; 10 | box-shadow: 0 3px 8px 0 rgba(0,0,0,0.19), 0 6px 13px 0 rgba(0,0,0,0.24); 11 | -webkit-box-shadow: 0 3px 8px 0 rgba(0,0,0,0.19), 0 6px 13px 0 rgba(0,0,0,0.24); 12 | } 13 | .mui-refresh-main { 14 | position: fixed; 15 | top: 0; 16 | -webkit-perspective: 1000; 17 | -webkit-backface-visibility: hidden; 18 | opacity: 0; 19 | -webkit-transform: scale(0); 20 | } 21 | /*transition*/ 22 | .mui-refresh-main-animat { 23 | /* transition: box-shadow 0.38s cubic-bezier(0.4, 0, 0.2, 1) */ 24 | /* -webkit-transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1) */ 25 | /* transition: all 0.43s cubic-bezier(.08,.55,.81,1.8) */ 26 | -webkit-transition: all 0.43s cubic-bezier(0.08, 0.55, 0.81, 1.8); 27 | } 28 | /* below nav bar */ 29 | .mui-refresh-nav { 30 | -webkit-transform: scale(1); 31 | } 32 | .mui-refresh-noshow { 33 | opacity: 0; 34 | -webkit-transform: scale(0.01) !important; 35 | -webkit-transition: all 0.25s ease-in-out !important; 36 | } 37 | .mui-refresh-wrapper { 38 | width: 25px; 39 | height: 25px; 40 | } 41 | .mui-arrow-wrapper { 42 | -webkit-transition: all 0.2s ease; 43 | } 44 | .mui-half-circle, 45 | .mui-arrow-main { 46 | position: absolute; 47 | top: 0; 48 | width: 25px; 49 | height: 25px; 50 | box-sizing: border-box; 51 | border-width: 3px; 52 | border-style: solid; 53 | border-color: #000 #000 transparent; 54 | border-radius: 999px; 55 | } 56 | .mui-arrow-main { 57 | margin-top: 10px; 58 | /* transform: rotate(-37deg) */ 59 | -webkit-transform: rotate(-37deg); 60 | /* arrow */ 61 | } 62 | .mui-arrow-main:before { 63 | content: ''; 64 | display: block; 65 | position: relative; 66 | top: 14px; 67 | left: 0px; 68 | width: 0; 69 | height: 0; 70 | border-width: 6px 6px; 71 | border-style: solid; 72 | transform: rotate(-56deg); 73 | -webkit-transform: rotate(-56deg); 74 | } 75 | .mui-spinner-main { 76 | width: 25px; 77 | height: 25px; 78 | position: relative; 79 | } 80 | .mui-spinner-main .mui-spinner-left, 81 | .mui-spinner-main .mui-spinner-right { 82 | position: absolute; 83 | top: 0; 84 | height: 25px; 85 | width: 13px; 86 | overflow: hidden; 87 | } 88 | .mui-spinner-main .mui-spinner-left { 89 | left: 0; 90 | } 91 | .mui-spinner-main .mui-spinner-left .mui-half-circle { 92 | left: 0; 93 | border-right-color: transparent; 94 | } 95 | .mui-spinner-main .mui-spinner-right { 96 | right: 0; 97 | } 98 | .mui-spinner-main .mui-spinner-right .mui-half-circle { 99 | right: 0; 100 | border-left-color: transparent; 101 | } 102 | /* Blue theme */ 103 | .mui-blue-theme .mui-arrow-main { 104 | border-color: #2196f3 #2196f3 rgba(0,0,0,0); 105 | } 106 | .mui-blue-theme .mui-arrow-main:before { 107 | border-color: #2196f3 #fff #fff #fff; 108 | } 109 | .mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle, 110 | .mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle { 111 | border-top-color: #2196f3; 112 | } 113 | .mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle { 114 | border-left-color: #2196f3; 115 | } 116 | .mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle { 117 | border-right-color: #2196f3; 118 | } 119 | /* Animation */ 120 | .mui-spinner-wrapper { 121 | -webkit-animation: outer-rotate 2.91667s linear infinite; 122 | animation: outer-rotate 2.91667s linear infinite; 123 | } 124 | .mui-spinner-main { 125 | -webkit-animation: sporadic-rotate 5.25s cubic-bezier(0.35, 0, 0.25, 1) infinite; 126 | animation: sporadic-rotate 5.25s cubic-bezier(0.35, 0, 0.25, 1) infinite; 127 | } 128 | .mui-spinner-main .mui-spinner-left .mui-half-circle, 129 | .mui-spinner-main .mui-spinner-right .mui-half-circle { 130 | -webkit-animation-iteration-count: infinite; 131 | animation-iteration-count: infinite; 132 | -webkit-animation-duration: 1.3125s; 133 | animation-duration: 1.3125s; 134 | -webkit-animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1); 135 | animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1); 136 | } 137 | .mui-spinner-main .mui-spinner-left .mui-half-circle { 138 | -webkit-animation-name: left-wobble; 139 | animation-name: left-wobble; 140 | } 141 | .mui-spinner-main .mui-spinner-right .mui-half-circle { 142 | -webkit-animation-name: right-wobble; 143 | animation-name: right-wobble; 144 | } 145 | @-webkit-keyframes outer-rotate { 146 | 100% { 147 | -webkit-transform: rotate(360deg); 148 | transform: rotate(360deg); 149 | } 150 | } 151 | @-webkit-keyframes left-wobble { 152 | 0%, 100% { 153 | -webkit-transform: rotate(130deg); 154 | transform: rotate(130deg); 155 | } 156 | 50% { 157 | -webkit-transform: rotate(-5deg); 158 | transform: rotate(-5deg); 159 | } 160 | } 161 | @-webkit-keyframes right-wobble { 162 | 0%, 100% { 163 | -webkit-transform: rotate(-130deg); 164 | transform: rotate(-130deg); 165 | } 166 | 50% { 167 | -webkit-transform: rotate(5deg); 168 | transform: rotate(5deg); 169 | } 170 | } 171 | @-webkit-keyframes sporadic-rotate { 172 | 12.5% { 173 | -webkit-transform: rotate(135deg); 174 | transform: rotate(135deg); 175 | } 176 | 25% { 177 | -webkit-transform: rotate(270deg); 178 | transform: rotate(270deg); 179 | } 180 | 37.5% { 181 | -webkit-transform: rotate(405deg); 182 | transform: rotate(405deg); 183 | } 184 | 50% { 185 | -webkit-transform: rotate(540deg); 186 | transform: rotate(540deg); 187 | } 188 | 62.5% { 189 | -webkit-transform: rotate(675deg); 190 | transform: rotate(675deg); 191 | } 192 | 75% { 193 | -webkit-transform: rotate(810deg); 194 | transform: rotate(810deg); 195 | } 196 | 87.5% { 197 | -webkit-transform: rotate(945deg); 198 | transform: rotate(945deg); 199 | } 200 | 100% { 201 | -webkit-transform: rotate(1080deg); 202 | transform: rotate(1080deg); 203 | } 204 | } 205 | @-moz-keyframes outer-rotate { 206 | 100% { 207 | -webkit-transform: rotate(360deg); 208 | transform: rotate(360deg); 209 | } 210 | } 211 | @-webkit-keyframes outer-rotate { 212 | 100% { 213 | -webkit-transform: rotate(360deg); 214 | transform: rotate(360deg); 215 | } 216 | } 217 | @-o-keyframes outer-rotate { 218 | 100% { 219 | -webkit-transform: rotate(360deg); 220 | transform: rotate(360deg); 221 | } 222 | } 223 | @keyframes outer-rotate { 224 | 100% { 225 | -webkit-transform: rotate(360deg); 226 | transform: rotate(360deg); 227 | } 228 | } 229 | @-moz-keyframes left-wobble { 230 | 0%, 100% { 231 | -webkit-transform: rotate(130deg); 232 | transform: rotate(130deg); 233 | } 234 | 50% { 235 | -webkit-transform: rotate(-5deg); 236 | transform: rotate(-5deg); 237 | } 238 | } 239 | @-webkit-keyframes left-wobble { 240 | 0%, 100% { 241 | -webkit-transform: rotate(130deg); 242 | transform: rotate(130deg); 243 | } 244 | 50% { 245 | -webkit-transform: rotate(-5deg); 246 | transform: rotate(-5deg); 247 | } 248 | } 249 | @-o-keyframes left-wobble { 250 | 0%, 100% { 251 | -webkit-transform: rotate(130deg); 252 | transform: rotate(130deg); 253 | } 254 | 50% { 255 | -webkit-transform: rotate(-5deg); 256 | transform: rotate(-5deg); 257 | } 258 | } 259 | @keyframes left-wobble { 260 | 0%, 100% { 261 | -webkit-transform: rotate(130deg); 262 | transform: rotate(130deg); 263 | } 264 | 50% { 265 | -webkit-transform: rotate(-5deg); 266 | transform: rotate(-5deg); 267 | } 268 | } 269 | @-moz-keyframes right-wobble { 270 | 0%, 100% { 271 | -webkit-transform: rotate(-130deg); 272 | transform: rotate(-130deg); 273 | } 274 | 50% { 275 | -webkit-transform: rotate(5deg); 276 | transform: rotate(5deg); 277 | } 278 | } 279 | @-webkit-keyframes right-wobble { 280 | 0%, 100% { 281 | -webkit-transform: rotate(-130deg); 282 | transform: rotate(-130deg); 283 | } 284 | 50% { 285 | -webkit-transform: rotate(5deg); 286 | transform: rotate(5deg); 287 | } 288 | } 289 | @-o-keyframes right-wobble { 290 | 0%, 100% { 291 | -webkit-transform: rotate(-130deg); 292 | transform: rotate(-130deg); 293 | } 294 | 50% { 295 | -webkit-transform: rotate(5deg); 296 | transform: rotate(5deg); 297 | } 298 | } 299 | @keyframes right-wobble { 300 | 0%, 100% { 301 | -webkit-transform: rotate(-130deg); 302 | transform: rotate(-130deg); 303 | } 304 | 50% { 305 | -webkit-transform: rotate(5deg); 306 | transform: rotate(5deg); 307 | } 308 | } 309 | @-moz-keyframes sporadic-rotate { 310 | 12.5% { 311 | -webkit-transform: rotate(135deg); 312 | transform: rotate(135deg); 313 | } 314 | 25% { 315 | -webkit-transform: rotate(270deg); 316 | transform: rotate(270deg); 317 | } 318 | 37.5% { 319 | -webkit-transform: rotate(405deg); 320 | transform: rotate(405deg); 321 | } 322 | 50% { 323 | -webkit-transform: rotate(540deg); 324 | transform: rotate(540deg); 325 | } 326 | 62.5% { 327 | -webkit-transform: rotate(675deg); 328 | transform: rotate(675deg); 329 | } 330 | 75% { 331 | -webkit-transform: rotate(810deg); 332 | transform: rotate(810deg); 333 | } 334 | 87.5% { 335 | -webkit-transform: rotate(945deg); 336 | transform: rotate(945deg); 337 | } 338 | 100% { 339 | -webkit-transform: rotate(1080deg); 340 | transform: rotate(1080deg); 341 | } 342 | } 343 | @-webkit-keyframes sporadic-rotate { 344 | 12.5% { 345 | -webkit-transform: rotate(135deg); 346 | transform: rotate(135deg); 347 | } 348 | 25% { 349 | -webkit-transform: rotate(270deg); 350 | transform: rotate(270deg); 351 | } 352 | 37.5% { 353 | -webkit-transform: rotate(405deg); 354 | transform: rotate(405deg); 355 | } 356 | 50% { 357 | -webkit-transform: rotate(540deg); 358 | transform: rotate(540deg); 359 | } 360 | 62.5% { 361 | -webkit-transform: rotate(675deg); 362 | transform: rotate(675deg); 363 | } 364 | 75% { 365 | -webkit-transform: rotate(810deg); 366 | transform: rotate(810deg); 367 | } 368 | 87.5% { 369 | -webkit-transform: rotate(945deg); 370 | transform: rotate(945deg); 371 | } 372 | 100% { 373 | -webkit-transform: rotate(1080deg); 374 | transform: rotate(1080deg); 375 | } 376 | } 377 | @-o-keyframes sporadic-rotate { 378 | 12.5% { 379 | -webkit-transform: rotate(135deg); 380 | transform: rotate(135deg); 381 | } 382 | 25% { 383 | -webkit-transform: rotate(270deg); 384 | transform: rotate(270deg); 385 | } 386 | 37.5% { 387 | -webkit-transform: rotate(405deg); 388 | transform: rotate(405deg); 389 | } 390 | 50% { 391 | -webkit-transform: rotate(540deg); 392 | transform: rotate(540deg); 393 | } 394 | 62.5% { 395 | -webkit-transform: rotate(675deg); 396 | transform: rotate(675deg); 397 | } 398 | 75% { 399 | -webkit-transform: rotate(810deg); 400 | transform: rotate(810deg); 401 | } 402 | 87.5% { 403 | -webkit-transform: rotate(945deg); 404 | transform: rotate(945deg); 405 | } 406 | 100% { 407 | -webkit-transform: rotate(1080deg); 408 | transform: rotate(1080deg); 409 | } 410 | } 411 | @keyframes sporadic-rotate { 412 | 12.5% { 413 | -webkit-transform: rotate(135deg); 414 | transform: rotate(135deg); 415 | } 416 | 25% { 417 | -webkit-transform: rotate(270deg); 418 | transform: rotate(270deg); 419 | } 420 | 37.5% { 421 | -webkit-transform: rotate(405deg); 422 | transform: rotate(405deg); 423 | } 424 | 50% { 425 | -webkit-transform: rotate(540deg); 426 | transform: rotate(540deg); 427 | } 428 | 62.5% { 429 | -webkit-transform: rotate(675deg); 430 | transform: rotate(675deg); 431 | } 432 | 75% { 433 | -webkit-transform: rotate(810deg); 434 | transform: rotate(810deg); 435 | } 436 | 87.5% { 437 | -webkit-transform: rotate(945deg); 438 | transform: rotate(945deg); 439 | } 440 | 100% { 441 | -webkit-transform: rotate(1080deg); 442 | transform: rotate(1080deg); 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /build/css/material-refresh.min.css: -------------------------------------------------------------------------------- 1 | .mui-refresh-main{left:50%;margin-left:-25px;padding:9px;z-index:10001;background-color:#fff;overflow:hidden;border-radius:999px;box-shadow:0 3px 8px 0 rgba(0,0,0,.19),0 6px 13px 0 rgba(0,0,0,.24);-webkit-box-shadow:0 3px 8px 0 rgba(0,0,0,.19),0 6px 13px 0 rgba(0,0,0,.24);position:fixed;top:0;-webkit-perspective:1000;-webkit-backface-visibility:hidden;opacity:0;-webkit-transform:scale(0)}.mui-refresh-main-animat{-webkit-transition:all .43s cubic-bezier(.08,.55,.81,1.8)}.mui-refresh-nav{-webkit-transform:scale(1)}.mui-refresh-noshow{opacity:0;-webkit-transform:scale(.01)!important;-webkit-transition:all .25s ease-in-out!important}.mui-refresh-wrapper{width:25px;height:25px}.mui-arrow-wrapper{-webkit-transition:all .2s ease}.mui-arrow-main,.mui-half-circle{position:absolute;top:0;width:25px;height:25px;box-sizing:border-box;border-width:3px;border-style:solid;border-color:#000 #000 transparent;border-radius:999px}.mui-arrow-main{margin-top:10px;-webkit-transform:rotate(-37deg)}.mui-arrow-main:before{content:'';display:block;position:relative;top:14px;left:0;width:0;height:0;border-width:6px;border-style:solid;transform:rotate(-56deg);-webkit-transform:rotate(-56deg)}.mui-spinner-main{width:25px;height:25px;position:relative}.mui-spinner-main .mui-spinner-left,.mui-spinner-main .mui-spinner-right{position:absolute;top:0;height:25px;width:13px;overflow:hidden}.mui-spinner-main .mui-spinner-left{left:0}.mui-spinner-main .mui-spinner-left .mui-half-circle{left:0;border-right-color:transparent}.mui-spinner-main .mui-spinner-right{right:0}.mui-spinner-main .mui-spinner-right .mui-half-circle{right:0;border-left-color:transparent}.mui-blue-theme .mui-arrow-main{border-color:#2196f3 #2196f3 transparent}.mui-blue-theme .mui-arrow-main:before{border-color:#2196f3 #fff #fff}.mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle,.mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle{border-top-color:#2196f3}.mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle{border-left-color:#2196f3}.mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle{border-right-color:#2196f3}.mui-spinner-wrapper{-webkit-animation:outer-rotate 2.91667s linear infinite;animation:outer-rotate 2.91667s linear infinite}.mui-spinner-main{-webkit-animation:sporadic-rotate 5.25s cubic-bezier(.35,0,.25,1) infinite;animation:sporadic-rotate 5.25s cubic-bezier(.35,0,.25,1) infinite}.mui-spinner-main .mui-spinner-left .mui-half-circle,.mui-spinner-main .mui-spinner-right .mui-half-circle{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-duration:1.3125s;animation-duration:1.3125s;-webkit-animation-timing-function:cubic-bezier(.35,0,.25,1);animation-timing-function:cubic-bezier(.35,0,.25,1)}.mui-spinner-main .mui-spinner-left .mui-half-circle{-webkit-animation-name:left-wobble;animation-name:left-wobble}.mui-spinner-main .mui-spinner-right .mui-half-circle{-webkit-animation-name:right-wobble;animation-name:right-wobble}@-moz-keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@-webkit-keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@-o-keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@-moz-keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@-webkit-keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@-o-keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@-moz-keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-o-keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}} -------------------------------------------------------------------------------- /build/js/material-refresh.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | (function($){ 4 | var $scrollEl = $(document.body); 5 | var $refreshMain, $spinnerWrapper, $arrowWrapper, $arrowMain; 6 | var scrollEl = document.body; 7 | 8 | var noShowClass = 'mui-refresh-noshow'; 9 | var mainAnimatClass = 'mui-refresh-main-animat'; 10 | var blueThemeClass = 'mui-blue-theme'; 11 | 12 | var isShowLoading = false; 13 | var isStoping = false; 14 | var isBtnAction = false; 15 | 16 | var NUM_POS_START_Y = -85; 17 | var NUM_POS_TARGET_Y = 0; // Where to stop 18 | var NUM_POS_MAX_Y = 65; // Max position for the moving distance 19 | var NUM_POS_MIN_Y = -25; // Min position for the moving distance 20 | var NUM_NAV_TARGET_ADDY = 20; // For custom nav bar 21 | 22 | var touchCurrentY; 23 | var touchStartY = 0; 24 | var customNavTop = 0; 25 | var verticalThreshold = 2; 26 | var maxRotateTime = 6000; //Max time to stop rotate 27 | var basePosY = 60; 28 | 29 | var onBegin = null; 30 | var onBtnBegin= null; 31 | var onEnd = null; 32 | var onBtnEnd = null; 33 | var stopAnimatTimeout = null; 34 | 35 | var refreshNav = ''; 36 | 37 | var lastTime = new Date().getTime(); 38 | 39 | var isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); 40 | 41 | var tmpl = '
\ 42 |
\ 43 |
\ 44 |
\ 45 |
\ 46 | \ 56 |
\ 57 |
'; 58 | 59 | // Defined the object to improve performance 60 | var touchPos = { 61 | top: 0, 62 | x1: 0, 63 | x2: 0, 64 | y1: 0, 65 | y2: 0 66 | } 67 | 68 | // Default options 69 | /* var opts = { */ 70 | /* scrollEl: '', //String */ 71 | /* nav: '', //String */ 72 | /* top: '0px', //String */ 73 | /* theme: '', //String */ 74 | /* index: 10001, //Number*/ 75 | /* maxTime: 3000, //Number */ 76 | /* freeze: false, //Boolen */ 77 | /* onBegin: null, //Function */ 78 | /* onEnd: null //Function */ 79 | /* } */ 80 | 81 | 82 | /* Known issue: 83 | * 1. iOS feature when scrolling ,animation will stop 84 | * 2. Animation display issue in anfroid like miui小米 85 | * 86 | * 87 | * TODO list: 88 | * 1. Using translate and scale together to replace top 89 | * 2. Optimize circle rotate animation 90 | */ 91 | 92 | // Main function to init the refresh style 93 | function mRefresh(options) { 94 | options = options || {}; 95 | 96 | scrollEl = options.scrollEl ? options.scrollEl : 97 | isIOS ? scrollEl : document; 98 | $scrollEl = $(scrollEl); 99 | 100 | // extend options 101 | onBegin = options.onBegin; 102 | onEnd = options.onEnd; 103 | maxRotateTime = options.maxTime || maxRotateTime; 104 | refreshNav = options.nav || refreshNav; 105 | 106 | if ($('#muirefresh').length === 0) { 107 | renderTmpl(); 108 | } 109 | 110 | $refreshMain = $('#muiRefresh'); 111 | $spinnerWrapper = $('.mui-spinner-wrapper', $refreshMain); 112 | $arrowWrapper = $('.mui-arrow-wrapper', $refreshMain); 113 | $arrowMain = $('.mui-arrow-main', $refreshMain); 114 | 115 | // Custom nav bar 116 | if (!isDefaultType()) { 117 | $refreshMain.addClass('mui-refresh-nav'); 118 | basePosY = $(refreshNav).height() + 20; 119 | if($(refreshNav).offset()){ 120 | customNavTop = $(refreshNav).offset().top; 121 | // Handle position fix 122 | if($(refreshNav).css('position') !== 'fixed'){ 123 | basePosY += customNavTop; 124 | } 125 | // Set the first Y position 126 | $refreshMain.css('top', customNavTop + 'px'); 127 | } 128 | 129 | //Set z-index to make sure ablow the nav bar 130 | var navIndex = $(refreshNav).css('z-index'); 131 | $refreshMain.css('z-index', navIndex - 1); 132 | } 133 | 134 | //Set custom z-index 135 | if(options.index){ 136 | $refreshMain.css('z-index', ~~options.index); 137 | } 138 | 139 | //Set custom top, to change the position 140 | if(options.top){ 141 | $refreshMain.css('top', options.top); 142 | } 143 | 144 | // Extract theme 145 | if (options.theme) { 146 | $refreshMain.addClass(options.theme); 147 | } else { 148 | $refreshMain.addClass(blueThemeClass); 149 | } 150 | 151 | // Add Animation Class 152 | $refreshMain.addClass(mainAnimatClass); 153 | 154 | if(!options.freeze){ 155 | bindEvents(); 156 | } 157 | } 158 | 159 | // Public Methods 160 | 161 | // Finish loading 162 | mRefresh.resolve = function() { 163 | if(!isStoping && stopAnimatTimeout){ 164 | clearTimeout(stopAnimatTimeout); 165 | stopAnimatTimeout = null; 166 | 167 | recoverRefresh(); 168 | } 169 | } 170 | 171 | // Destory refresh 172 | mRefresh.destroy = function(){ 173 | unbindEvents(); 174 | $refreshMain.remove(); 175 | 176 | } 177 | 178 | // Type3: Button action refresh 179 | mRefresh.refresh = function(opt) { 180 | // Do rotate 181 | if(!isShowLoading){ 182 | var realTargetPos = basePosY + NUM_POS_TARGET_Y - 20; 183 | isShowLoading = true; 184 | isBtnAction = true; 185 | 186 | opt = opt || {}; 187 | onBtnBegin = opt.onBegin; 188 | onBtnEnd = opt.onEnd; 189 | 190 | if (!isDefaultType()) { 191 | realTargetPos = realTargetPos + NUM_NAV_TARGET_ADDY; 192 | } 193 | 194 | // Handle freeze 195 | $refreshMain.show(); 196 | //Romove animat time 197 | $refreshMain.removeClass(mainAnimatClass); 198 | // move to target position 199 | $refreshMain.css('top', realTargetPos + 'px'); 200 | // make it small 201 | $refreshMain.css('-webkit-transform', 'scale(' + 0.01 + ')'); 202 | 203 | setTimeout(doRotate, 60); 204 | } 205 | } 206 | 207 | // Unbind touch events,for freeze type1 and type2 208 | mRefresh.unbindEvents = function(){ 209 | unbindEvents(); 210 | } 211 | 212 | mRefresh.bindEvents = function(){ 213 | bindEvents(); 214 | } 215 | 216 | // Render html template 217 | function renderTmpl(){ 218 | document.body.insertAdjacentHTML('beforeend', tmpl); 219 | } 220 | 221 | 222 | function touchStart(e){ 223 | if(isIOS && scrollEl == document.body){ 224 | touchPos.top = window.scrollY; 225 | }else if(scrollEl != document){ 226 | touchPos.top = document.querySelector(scrollEl).scrollTop; 227 | } else { 228 | touchPos.top = (document.documentElement || document.body.parentNode || document.body).scrollTop; 229 | } 230 | 231 | if (touchPos.top > 0 || isShowLoading) { 232 | return; 233 | } 234 | 235 | touchCurrentY = basePosY + NUM_POS_START_Y; 236 | $refreshMain.show(); 237 | 238 | // Fix jQuery touch event detect 239 | e = e.originalEvent || e; 240 | 241 | if (e.touches[0]) { 242 | touchPos.x1 = e.touches[0].pageX; 243 | touchStartY = touchPos.y1 = e.touches[0].pageY; 244 | } 245 | } 246 | 247 | function touchMove(e){ 248 | var thisTouch, distanceY; 249 | var now = new Date().getTime(); 250 | 251 | e = e.originalEvent || e; 252 | 253 | if (touchPos.top > 0 || isShowLoading || !e.touches || e.touches.length !== 1) { 254 | // Just allow one finger 255 | return; 256 | } 257 | 258 | thisTouch = e.touches[0]; 259 | 260 | touchPos.x2 = thisTouch.pageX; 261 | touchPos.y2 = thisTouch.pageY; 262 | 263 | // Distance for pageY change 264 | distanceY = touchPos.y2 - touchPos.y1; 265 | 266 | if (touchPos.y2 - touchStartY + verticalThreshold > 0) { 267 | e.preventDefault(); 268 | 269 | // Some android phone 270 | // Throttle, aviod jitter 271 | if (now - lastTime < 90) { 272 | return; 273 | } 274 | 275 | if (touchCurrentY < basePosY - customNavTop + NUM_POS_MAX_Y) { 276 | touchCurrentY += distanceY ; 277 | moveCircle(touchCurrentY); 278 | } else { 279 | // Move over the max position will do the rotate 280 | doRotate(); 281 | return; 282 | } 283 | 284 | } 285 | 286 | // y1 always is the current pageY 287 | touchPos.y1 = thisTouch.pageY; 288 | lastTime = now; 289 | } 290 | 291 | function touchEnd(e){ 292 | if (touchPos.top > 0 || isShowLoading) { 293 | return; 294 | } 295 | e.preventDefault(); 296 | 297 | if (touchCurrentY > basePosY - customNavTop + NUM_POS_MIN_Y) { 298 | // Should move over the min position 299 | doRotate(); 300 | } else { 301 | backToStart(); 302 | } 303 | } 304 | 305 | /** 306 | * backToStart 307 | * Return to start position 308 | */ 309 | function backToStart() { 310 | var realStartPos = basePosY + NUM_POS_START_Y; 311 | if ( isDefaultType() ) { 312 | $refreshMain.css('top', realStartPos + 'px'); 313 | $refreshMain.css('-webkit-transform', 'scale(' + 0 + ')'); 314 | } else { 315 | // Distance must greater than NUM_POS_MIN_Y 316 | $refreshMain.css('top', customNavTop + 'px'); 317 | /* $refreshMain.css('-webkit-transform', 'translateY(' + realStartPos + 'px)'); */ 318 | } 319 | setTimeout(function(){ 320 | // Handle button action 321 | if(!isShowLoading){ 322 | $refreshMain.css('opacity', 0); 323 | $refreshMain.hide(); 324 | } 325 | }, 300); 326 | } 327 | 328 | /** 329 | * moveCircle 330 | * touchmove change the circle style 331 | * 332 | * @param {number} y 333 | */ 334 | function moveCircle(y){ 335 | var scaleRate = 40; 336 | var scalePer = y / scaleRate > 1 ? 1 : y / scaleRate < 0 ? 0 : y / scaleRate; 337 | var currMoveY = basePosY + NUM_POS_START_Y + y; 338 | 339 | if (isDefaultType()) { 340 | // Small to Big 341 | $refreshMain.css('-webkit-transform', 'scale(' + scalePer + ')'); 342 | } 343 | /* $refreshMain.css('-webkit-transform', 'translateY('+ y + 'px)'); */ 344 | 345 | $refreshMain.css('opacity', scalePer); 346 | // Change the position 347 | $refreshMain.css('top', currMoveY + 'px'); 348 | $arrowMain.css('-webkit-transform', 'rotate(' + -(y * 3) + 'deg)'); 349 | /* $arrowMain.css('transform', 'rotate(' + -(y * 3) + 'deg)'); */ 350 | 351 | } 352 | 353 | 354 | /** 355 | * doRotate 356 | * Rotate the circle,and you can stop it by `mRefresh.resolve()` 357 | * or it wil stop within the time: `maxRotateTime` 358 | */ 359 | function doRotate(){ 360 | isShowLoading = true; 361 | // Do button action callback 362 | if (isBtnAction && typeof onBtnBegin === 'function') { 363 | onBtnBegin(); 364 | } else if (typeof onBegin === 'function') { 365 | // Do onBegin callback 366 | onBegin(); 367 | } 368 | 369 | // Make sure display entirely 370 | $refreshMain.css('opacity', 1); 371 | 372 | if (!isBtnAction) { 373 | var realTargetPos = basePosY + NUM_POS_TARGET_Y - 20; 374 | if (!isDefaultType()) { 375 | realTargetPos = realTargetPos + NUM_NAV_TARGET_ADDY; 376 | } 377 | $refreshMain.css('top', realTargetPos + 'px'); 378 | /* $refreshMain.css('-webkit-transform', 'translateY(' + realTargetPos + 'px)'); */ 379 | } else { 380 | $refreshMain.addClass(mainAnimatClass); 381 | $refreshMain.css('-webkit-transform', 'scale(' + 1 + ')'); 382 | } 383 | 384 | $arrowWrapper.hide(); 385 | 386 | // Start animation 387 | $spinnerWrapper.show(); 388 | 389 | // Timeout to stop animation 390 | stopAnimatTimeout = setTimeout(recoverRefresh, maxRotateTime); 391 | } 392 | 393 | /** 394 | * Recover Refresh 395 | * Hide the circle 396 | */ 397 | function recoverRefresh(){ 398 | // For aviod resolve 399 | isStoping = true; 400 | 401 | // Stop animation 402 | $refreshMain.addClass(noShowClass); 403 | 404 | $spinnerWrapper.hide(); 405 | 406 | setTimeout(function(){ 407 | $refreshMain.removeClass(noShowClass); 408 | $refreshMain.hide(); 409 | 410 | backToStart(); 411 | 412 | $arrowWrapper.show(); 413 | 414 | isShowLoading = false; 415 | isStoping = false; 416 | 417 | if (isBtnAction && typeof onBtnEnd === 'function') { 418 | onBtnEnd(); 419 | } else if (typeof onEnd === 'function') { 420 | onEnd(); 421 | } 422 | 423 | isBtnAction = false; 424 | 425 | }, 500); 426 | } 427 | 428 | /** 429 | * isDefaultType 430 | * Check is type1: Above surface 431 | * 432 | * @return {Boolen} 433 | */ 434 | function isDefaultType() { 435 | return $(refreshNav).length === 0; 436 | } 437 | 438 | function bindEvents() { 439 | $scrollEl.on('touchstart', touchStart); 440 | $scrollEl.on('touchmove', touchMove); 441 | $scrollEl.on('touchend', touchEnd); 442 | } 443 | 444 | function unbindEvents() { 445 | $scrollEl.off('touchstart', touchStart); 446 | $scrollEl.off('touchmove', touchMove); 447 | $scrollEl.off('touchend', touchEnd); 448 | } 449 | 450 | 451 | window.mRefresh = mRefresh; 452 | 453 | })(window.Zepto || window.jQuery); 454 | }).call(this); -------------------------------------------------------------------------------- /build/js/material-refresh.min.js: -------------------------------------------------------------------------------- 1 | (function(){"use strict";!function(e){function n(n){if(n=n||{},g=n.scrollEl?n.scrollEl:Q?g:document,y=e(g),U=n.onBegin,H=n.onEnd,M=n.maxTime||M,O=n.nav||O,0===e("#muirefresh").length&&o(),m=e("#muiRefresh"),p=e(".mui-spinner-wrapper",m),h=e(".mui-arrow-wrapper",m),v=e(".mui-arrow-main",m),!d()){m.addClass("mui-refresh-nav"),S=e(O).height()+20,e(O).offset()&&(A=e(O).offset().top,"fixed"!==e(O).css("position")&&(S+=A),m.css("top",A+"px"));var t=e(O).css("z-index");m.css("z-index",t-1)}n.index&&m.css("z-index",~~n.index),n.top&&m.css("top",n.top),m.addClass(n.theme?n.theme:T),m.addClass(b),n.freeze||f()}function o(){document.body.insertAdjacentHTML("beforeend",Z)}function t(e){F.top=Q&&g==document.body?window.scrollY:g!=document?document.querySelector(g).scrollTop:(document.documentElement||document.body.parentNode||document.body).scrollTop,F.top>0||E||(w=S+z,m.show(),e=e.originalEvent||e,e.touches[0]&&(F.x1=e.touches[0].pageX,j=F.y1=e.touches[0].pageY))}function i(e){var n,o,t=(new Date).getTime();if(e=e.originalEvent||e,!(F.top>0||E)&&e.touches&&1===e.touches.length){if(n=e.touches[0],F.x2=n.pageX,F.y2=n.pageY,o=F.y2-F.y1,F.y2-j+B>0){if(e.preventDefault(),90>t-P)return;if(!(S-A+Y>w))return void u();w+=o,r(w)}F.y1=n.pageY,P=t}}function s(e){F.top>0||E||(e.preventDefault(),w>S-A+R?u():c())}function c(){var e=S+z;d()?(m.css("top",e+"px"),m.css("-webkit-transform","scale(0)")):m.css("top",A+"px"),setTimeout(function(){E||(m.css("opacity",0),m.hide())},300)}function r(e){var n=40,o=e/n>1?1:0>e/n?0:e/n,t=S+z+e;d()&&m.css("-webkit-transform","scale("+o+")"),m.css("opacity",o),m.css("top",t+"px"),v.css("-webkit-transform","rotate("+-(3*e)+"deg)")}function u(){if(E=!0,k&&"function"==typeof q?q():"function"==typeof U&&U(),m.css("opacity",1),k)m.addClass(b),m.css("-webkit-transform","scale(1)");else{var e=S+D-20;d()||(e+=X),m.css("top",e+"px")}h.hide(),p.show(),N=setTimeout(a,M)}function a(){C=!0,m.addClass(x),p.hide(),setTimeout(function(){m.removeClass(x),m.hide(),c(),h.show(),E=!1,C=!1,k&&"function"==typeof L?L():"function"==typeof H&&H(),k=!1},500)}function d(){return 0===e(O).length}function f(){y.on("touchstart",t),y.on("touchmove",i),y.on("touchend",s)}function l(){y.off("touchstart",t),y.off("touchmove",i),y.off("touchend",s)}var m,p,h,v,w,y=e(document.body),g=document.body,x="mui-refresh-noshow",b="mui-refresh-main-animat",T="mui-blue-theme",E=!1,C=!1,k=!1,z=-85,D=0,Y=65,R=-25,X=20,j=0,A=0,B=2,M=6e3,S=60,U=null,q=null,H=null,L=null,N=null,O="",P=(new Date).getTime(),Q=!!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),Z='
',F={top:0,x1:0,x2:0,y1:0,y2:0};n.resolve=function(){!C&&N&&(clearTimeout(N),N=null,a())},n.destroy=function(){l(),m.remove()},n.refresh=function(e){if(!E){var n=S+D-20;E=!0,k=!0,e=e||{},q=e.onBegin,L=e.onEnd,d()||(n+=X),m.show(),m.removeClass(b),m.css("top",n+"px"),m.css("-webkit-transform","scale(0.01)"),setTimeout(u,60)}},n.unbindEvents=function(){l()},n.bindEvents=function(){f()},window.mRefresh=n}(window.Zepto||window.jQuery)}).call(this); -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Material Design swipe to refresh 8 | 9 | 10 | 11 | 12 | 20 | 21 |
22 | Fork me on GitHub 23 |
24 |

Intro

26 |

Google Material Design swipe (pull) to refresh.

27 |

It uses CSS3 and JavaScript depend on Zepto or jQuery.

28 |

Mobile Only and High Performance.

29 |

You can use 3 types of this Material Design refresher.

30 |
31 |
32 |

Type1: Above surface (default)

33 | Above surface 34 | 35 |

Source code:

36 |

37 |

38 | mRefresh(); 39 |
40 |

41 |

You can try it in mobile browser or webview by scanning the following QR code:

42 | 43 | QR 44 |

45 | URL: http://lightningtgc.github.io/material-refresh/type1.html 46 |

47 |
48 |
49 |

Type2: Below surface

50 | Below surface 51 | 52 |

Source code:

53 |
54 | mRefresh({ 55 |

56 | nav: '#navMain' 57 |

58 | }); 59 |
60 | 61 |

Actually, this page is the demo for Type2.

62 |

You can try it in chrome device mode or other mobile emulator.

63 | 64 |

Similarly, try it in mobile browser or webview by scanning the following QR code:

65 | 66 | QR 67 |

68 | URL: http://lightningtgc.github.io/material-refresh/index.html 69 |

70 | 71 |
72 |
73 |

Type3: Button action

74 | Button action 75 | 76 |

Source code:

77 |
78 | $('#buttonAction').on('tap', function(){ 79 |

80 | mRefresh.refresh(); 81 |

82 | }); 83 |
84 | 85 |

Click this button ↓↓ to try it and notice the top of page ↑↑

86 |
87 |
88 |
89 |
90 | 91 | 98 | 99 | 100 | 101 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /demo/js/demo.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | $(document).on("touchstart", function(e){ 4 | /* e.preventDefault(); */ 5 | }); 6 | }()); 7 | 8 | -------------------------------------------------------------------------------- /demo/libs/zepto.min.js: -------------------------------------------------------------------------------- 1 | /* Zepto 1.1.4 - zepto event ajax detect data touch callbacks deferred fx - zeptojs.com/license */ 2 | var Zepto=function(){function k(t){return null==t?String(t):E[S.call(t)]||"object"}function A(t){return"function"==k(t)}function D(t){return null!=t&&t==t.window}function L(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function _(t){return"object"==k(t)}function Z(t){return _(t)&&!D(t)&&Object.getPrototypeOf(t)==Object.prototype}function $(t){return"number"==typeof t.length}function F(t){return a.call(t,function(t){return null!=t})}function R(t){return t.length>0?n.fn.concat.apply([],t):t}function W(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function z(t){return t in f?f[t]:f[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function q(t,e){return"number"!=typeof e||c[W(t)]?e:e+"px"}function V(t){var e,n;return u[t]||(e=s.createElement(t),s.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),u[t]=n),u[t]}function B(t){return"children"in t?o.call(t.children):n.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function H(n,i,r){for(e in i)r&&(Z(i[e])||M(i[e]))?(Z(i[e])&&!Z(n[e])&&(n[e]={}),M(i[e])&&!M(n[e])&&(n[e]=[]),H(n[e],i[e],r)):i[e]!==t&&(n[e]=i[e])}function I(t,e){return null==e?n(t):n(t).filter(e)}function X(t,e,n,i){return A(e)?e.call(t,n,i):e}function U(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function Y(e,n){var i=e.className||"",r=i&&i.baseVal!==t;return n===t?r?i.baseVal:i:void(r?i.baseVal=n:e.className=n)}function J(t){var e;try{return t?"true"==t||("false"==t?!1:"null"==t?null:/^0/.test(t)||isNaN(e=Number(t))?/^[\[\{]/.test(t)?n.parseJSON(t):t:e):t}catch(i){return t}}function G(t,e){e(t);for(var n=0,i=t.childNodes.length;i>n;n++)G(t.childNodes[n],e)}var t,e,n,i,P,C,r=[],o=r.slice,a=r.filter,s=window.document,u={},f={},c={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,h=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,p=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,d=/^(?:body|html)$/i,m=/([A-Z])/g,g=["val","css","html","text","data","width","height","offset"],v=["after","prepend","before","append"],y=s.createElement("table"),b=s.createElement("tr"),w={tr:s.createElement("tbody"),tbody:y,thead:y,tfoot:y,td:b,th:b,"*":s.createElement("div")},x=/complete|loaded|interactive/,T=/^[\w-]*$/,E={},S=E.toString,j={},O=s.createElement("div"),N={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},M=Array.isArray||function(t){return t instanceof Array};return j.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var i,r=t.parentNode,o=!r;return o&&(r=O).appendChild(t),i=~j.qsa(r,e).indexOf(t),o&&O.removeChild(t),i},P=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},C=function(t){return a.call(t,function(e,n){return t.indexOf(e)==n})},j.fragment=function(e,i,r){var a,u,f;return h.test(e)&&(a=n(s.createElement(RegExp.$1))),a||(e.replace&&(e=e.replace(p,"<$1>")),i===t&&(i=l.test(e)&&RegExp.$1),i in w||(i="*"),f=w[i],f.innerHTML=""+e,a=n.each(o.call(f.childNodes),function(){f.removeChild(this)})),Z(r)&&(u=n(a),n.each(r,function(t,e){g.indexOf(t)>-1?u[t](e):u.attr(t,e)})),a},j.Z=function(t,e){return t=t||[],t.__proto__=n.fn,t.selector=e||"",t},j.isZ=function(t){return t instanceof j.Z},j.init=function(e,i){var r;if(!e)return j.Z();if("string"==typeof e)if(e=e.trim(),"<"==e[0]&&l.test(e))r=j.fragment(e,RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=j.qsa(s,e)}else{if(A(e))return n(s).ready(e);if(j.isZ(e))return e;if(M(e))r=F(e);else if(_(e))r=[e],e=null;else if(l.test(e))r=j.fragment(e.trim(),RegExp.$1,i),e=null;else{if(i!==t)return n(i).find(e);r=j.qsa(s,e)}}return j.Z(r,e)},n=function(t,e){return j.init(t,e)},n.extend=function(t){var e,n=o.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){H(t,n,e)}),t},j.qsa=function(t,e){var n,i="#"==e[0],r=!i&&"."==e[0],a=i||r?e.slice(1):e,s=T.test(a);return L(t)&&s&&i?(n=t.getElementById(a))?[n]:[]:1!==t.nodeType&&9!==t.nodeType?[]:o.call(s&&!i?r?t.getElementsByClassName(a):t.getElementsByTagName(e):t.querySelectorAll(e))},n.contains=s.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},n.type=k,n.isFunction=A,n.isWindow=D,n.isArray=M,n.isPlainObject=Z,n.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},n.inArray=function(t,e,n){return r.indexOf.call(e,t,n)},n.camelCase=P,n.trim=function(t){return null==t?"":String.prototype.trim.call(t)},n.uuid=0,n.support={},n.expr={},n.map=function(t,e){var n,r,o,i=[];if($(t))for(r=0;r=0?e:e+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return r.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return A(t)?this.not(this.not(t)):n(a.call(this,function(e){return j.matches(e,t)}))},add:function(t,e){return n(C(this.concat(n(t,e))))},is:function(t){return this.length>0&&j.matches(this[0],t)},not:function(e){var i=[];if(A(e)&&e.call!==t)this.each(function(t){e.call(this,t)||i.push(this)});else{var r="string"==typeof e?this.filter(e):$(e)&&A(e.item)?o.call(e):n(e);this.forEach(function(t){r.indexOf(t)<0&&i.push(t)})}return n(i)},has:function(t){return this.filter(function(){return _(t)?n.contains(this,t):n(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!_(t)?t:n(t)},last:function(){var t=this[this.length-1];return t&&!_(t)?t:n(t)},find:function(t){var e,i=this;return e=t?"object"==typeof t?n(t).filter(function(){var t=this;return r.some.call(i,function(e){return n.contains(e,t)})}):1==this.length?n(j.qsa(this[0],t)):this.map(function(){return j.qsa(this,t)}):[]},closest:function(t,e){var i=this[0],r=!1;for("object"==typeof t&&(r=n(t));i&&!(r?r.indexOf(i)>=0:j.matches(i,t));)i=i!==e&&!L(i)&&i.parentNode;return n(i)},parents:function(t){for(var e=[],i=this;i.length>0;)i=n.map(i,function(t){return(t=t.parentNode)&&!L(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return I(e,t)},parent:function(t){return I(C(this.pluck("parentNode")),t)},children:function(t){return I(this.map(function(){return B(this)}),t)},contents:function(){return this.map(function(){return o.call(this.childNodes)})},siblings:function(t){return I(this.map(function(t,e){return a.call(B(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return n.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=V(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=A(t);if(this[0]&&!e)var i=n(t).get(0),r=i.parentNode||this.length>1;return this.each(function(o){n(this).wrapAll(e?t.call(this,o):r?i.cloneNode(!0):i)})},wrapAll:function(t){if(this[0]){n(this[0]).before(t=n(t));for(var e;(e=t.children()).length;)t=e.first();n(t).append(this)}return this},wrapInner:function(t){var e=A(t);return this.each(function(i){var r=n(this),o=r.contents(),a=e?t.call(this,i):t;o.length?o.wrapAll(a):r.append(a)})},unwrap:function(){return this.parent().each(function(){n(this).replaceWith(n(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(e){return this.each(function(){var i=n(this);(e===t?"none"==i.css("display"):e)?i.show():i.hide()})},prev:function(t){return n(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return n(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var i=this.innerHTML;n(this).empty().append(X(this,t,e,i))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=X(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this[0].textContent:null},attr:function(n,i){var r;return"string"!=typeof n||1 in arguments?this.each(function(t){if(1===this.nodeType)if(_(n))for(e in n)U(this,e,n[e]);else U(this,n,X(this,i,t,this.getAttribute(n)))}):this.length&&1===this[0].nodeType?!(r=this[0].getAttribute(n))&&n in this[0]?this[0][n]:r:t},removeAttr:function(t){return this.each(function(){1===this.nodeType&&U(this,t)})},prop:function(t,e){return t=N[t]||t,1 in arguments?this.each(function(n){this[t]=X(this,e,n,this[t])}):this[0]&&this[0][t]},data:function(e,n){var i="data-"+e.replace(m,"-$1").toLowerCase(),r=1 in arguments?this.attr(i,n):this.attr(i);return null!==r?J(r):t},val:function(t){return 0 in arguments?this.each(function(e){this.value=X(this,t,e,this.value)}):this[0]&&(this[0].multiple?n(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var i=n(this),r=X(this,t,e,i.offset()),o=i.offsetParent().offset(),a={top:r.top-o.top,left:r.left-o.left};"static"==i.css("position")&&(a.position="relative"),i.css(a)});if(!this.length)return null;var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(t,i){if(arguments.length<2){var r=this[0],o=getComputedStyle(r,"");if(!r)return;if("string"==typeof t)return r.style[P(t)]||o.getPropertyValue(t);if(M(t)){var a={};return n.each(t,function(t,e){a[e]=r.style[P(e)]||o.getPropertyValue(e)}),a}}var s="";if("string"==k(t))i||0===i?s=W(t)+":"+q(t,i):this.each(function(){this.style.removeProperty(W(t))});else for(e in t)t[e]||0===t[e]?s+=W(e)+":"+q(e,t[e])+";":this.each(function(){this.style.removeProperty(W(e))});return this.each(function(){this.style.cssText+=";"+s})},index:function(t){return t?this.indexOf(n(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?r.some.call(this,function(t){return this.test(Y(t))},z(t)):!1},addClass:function(t){return t?this.each(function(e){if("className"in this){i=[];var r=Y(this),o=X(this,t,e,r);o.split(/\s+/g).forEach(function(t){n(this).hasClass(t)||i.push(t)},this),i.length&&Y(this,r+(r?" ":"")+i.join(" "))}}):this},removeClass:function(e){return this.each(function(n){if("className"in this){if(e===t)return Y(this,"");i=Y(this),X(this,e,n,i).split(/\s+/g).forEach(function(t){i=i.replace(z(t)," ")}),Y(this,i.trim())}})},toggleClass:function(e,i){return e?this.each(function(r){var o=n(this),a=X(this,e,r,Y(this));a.split(/\s+/g).forEach(function(e){(i===t?!o.hasClass(e):i)?o.addClass(e):o.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var n="scrollTop"in this[0];return e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var n="scrollLeft"in this[0];return e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),i=this.offset(),r=d.test(e[0].nodeName)?{top:0,left:0}:e.offset();return i.top-=parseFloat(n(t).css("margin-top"))||0,i.left-=parseFloat(n(t).css("margin-left"))||0,r.top+=parseFloat(n(e[0]).css("border-top-width"))||0,r.left+=parseFloat(n(e[0]).css("border-left-width"))||0,{top:i.top-r.top,left:i.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||s.body;t&&!d.test(t.nodeName)&&"static"==n(t).css("position");)t=t.offsetParent;return t})}},n.fn.detach=n.fn.remove,["width","height"].forEach(function(e){var i=e.replace(/./,function(t){return t[0].toUpperCase()});n.fn[e]=function(r){var o,a=this[0];return r===t?D(a)?a["inner"+i]:L(a)?a.documentElement["scroll"+i]:(o=this.offset())&&o[e]:this.each(function(t){a=n(this),a.css(e,X(this,r,t,a[e]()))})}}),v.forEach(function(t,e){var i=e%2;n.fn[t]=function(){var t,o,r=n.map(arguments,function(e){return t=k(e),"object"==t||"array"==t||null==e?e:j.fragment(e)}),a=this.length>1;return r.length<1?this:this.each(function(t,u){o=i?u:u.parentNode,u=0==e?u.nextSibling:1==e?u.firstChild:2==e?u:null;var f=n.contains(s.documentElement,o);r.forEach(function(t){if(a)t=t.cloneNode(!0);else if(!o)return n(t).remove();o.insertBefore(t,u),f&&G(t,function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},n.fn[i?t+"To":"insert"+(e?"Before":"After")]=function(e){return n(e)[t](this),this}}),j.Z.prototype=n.fn,j.uniq=C,j.deserializeValue=J,n.zepto=j,n}();window.Zepto=Zepto,void 0===window.$&&(window.$=Zepto),function(t){function l(t){return t._zid||(t._zid=e++)}function h(t,e,n,i){if(e=p(e),e.ns)var r=d(e.ns);return(a[l(t)]||[]).filter(function(t){return!(!t||e.e&&t.e!=e.e||e.ns&&!r.test(t.ns)||n&&l(t.fn)!==l(n)||i&&t.sel!=i)})}function p(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function d(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function m(t,e){return t.del&&!u&&t.e in f||!!e}function g(t){return c[t]||u&&f[t]||t}function v(e,i,r,o,s,u,f){var h=l(e),d=a[h]||(a[h]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return t(document).ready(r);var a=p(i);a.fn=r,a.sel=s,a.e in c&&(r=function(e){var n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?a.fn.apply(this,arguments):void 0}),a.del=u;var l=u||r;a.proxy=function(t){if(t=E(t),!t.isImmediatePropagationStopped()){t.data=o;var i=l.apply(e,t._args==n?[t]:[t].concat(t._args));return i===!1&&(t.preventDefault(),t.stopPropagation()),i}},a.i=d.length,d.push(a),"addEventListener"in e&&e.addEventListener(g(a.e),a.proxy,m(a,f))})}function y(t,e,n,i,r){var o=l(t);(e||"").split(/\s/).forEach(function(e){h(t,e,n,i).forEach(function(e){delete a[o][e.i],"removeEventListener"in t&&t.removeEventListener(g(e.e),e.proxy,m(e,r))})})}function E(e,i){return(i||!e.isDefaultPrevented)&&(i||(i=e),t.each(T,function(t,n){var r=i[t];e[t]=function(){return this[n]=b,r&&r.apply(i,arguments)},e[n]=w}),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?i.returnValue===!1:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=b)),e}function S(t){var e,i={originalEvent:t};for(e in t)x.test(e)||t[e]===n||(i[e]=t[e]);return E(i,t)}var n,e=1,i=Array.prototype.slice,r=t.isFunction,o=function(t){return"string"==typeof t},a={},s={},u="onfocusin"in window,f={focus:"focusin",blur:"focusout"},c={mouseenter:"mouseover",mouseleave:"mouseout"};s.click=s.mousedown=s.mouseup=s.mousemove="MouseEvents",t.event={add:v,remove:y},t.proxy=function(e,n){var a=2 in arguments&&i.call(arguments,2);if(r(e)){var s=function(){return e.apply(n,a?a.concat(i.call(arguments)):arguments)};return s._zid=l(e),s}if(o(n))return a?(a.unshift(e[n],e),t.proxy.apply(null,a)):t.proxy(e[n],e);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var b=function(){return!0},w=function(){return!1},x=/^([A-Z]|returnValue$|layer[XY]$)/,T={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,a,s,u,f){var c,l,h=this;return e&&!o(e)?(t.each(e,function(t,e){h.on(t,a,s,e,f)}),h):(o(a)||r(u)||u===!1||(u=s,s=a,a=n),(r(s)||s===!1)&&(u=s,s=n),u===!1&&(u=w),h.each(function(n,r){f&&(c=function(t){return y(r,t.type,u),u.apply(this,arguments)}),a&&(l=function(e){var n,o=t(e.target).closest(a,r).get(0);return o&&o!==r?(n=t.extend(S(e),{currentTarget:o,liveFired:r}),(c||u).apply(o,[n].concat(i.call(arguments,1)))):void 0}),v(r,e,u,s,a,l||c)}))},t.fn.off=function(e,i,a){var s=this;return e&&!o(e)?(t.each(e,function(t,e){s.off(t,i,e)}),s):(o(i)||r(a)||a===!1||(a=i,i=n),a===!1&&(a=w),s.each(function(){y(this,e,a,i)}))},t.fn.trigger=function(e,n){return e=o(e)||t.isPlainObject(e)?t.Event(e):E(e),e._args=n,this.each(function(){"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var i,r;return this.each(function(a,s){i=S(o(e)?t.Event(e):e),i._args=n,i.target=s,t.each(h(s,e.type||e),function(t,e){return r=e.proxy(i),i.isImmediatePropagationStopped()?!1:void 0})}),r},"focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return t?this.bind(e,t):this.trigger(e)}}),["focus","blur"].forEach(function(e){t.fn[e]=function(t){return t?this.bind(e,t):this.each(function(){try{this[e]()}catch(t){}}),this}}),t.Event=function(t,e){o(t)||(e=t,t=e.type);var n=document.createEvent(s[t]||"Events"),i=!0;if(e)for(var r in e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return n.initEvent(t,i,!0),E(n)}}(Zepto),function(t){function l(e,n,i){var r=t.Event(n);return t(e).trigger(r,i),!r.isDefaultPrevented()}function h(t,e,i,r){return t.global?l(e||n,i,r):void 0}function p(e){e.global&&0===t.active++&&h(e,null,"ajaxStart")}function d(e){e.global&&!--t.active&&h(e,null,"ajaxStop")}function m(t,e){var n=e.context;return e.beforeSend.call(n,t,e)===!1||h(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void h(e,n,"ajaxSend",[t,e])}function g(t,e,n,i){var r=n.context,o="success";n.success.call(r,t,o,e),i&&i.resolveWith(r,[t,o,e]),h(n,r,"ajaxSuccess",[e,n,t]),y(o,e,n)}function v(t,e,n,i,r){var o=i.context;i.error.call(o,n,e,t),r&&r.rejectWith(o,[n,e,t]),h(i,o,"ajaxError",[n,i,t||e]),y(e,n,i)}function y(t,e,n){var i=n.context;n.complete.call(i,e,t),h(n,i,"ajaxComplete",[e,n]),d(n)}function b(){}function w(t){return t&&(t=t.split(";",2)[0]),t&&(t==f?"html":t==u?"json":a.test(t)?"script":s.test(t)&&"xml")||"text"}function x(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function T(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=x(e.url,e.data),e.data=void 0)}function E(e,n,i,r){return t.isFunction(n)&&(r=i,i=n,n=void 0),t.isFunction(i)||(r=i,i=void 0),{url:e,data:n,success:i,dataType:r}}function j(e,n,i,r){var o,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),r&&(n=i?r:r+"["+(s||"object"==o||"array"==o?n:"")+"]"),!r&&a?e.add(u.name,u.value):"array"==o||!i&&"object"==o?j(e,u,i,n):e.add(n,u)})}var i,r,e=0,n=window.document,o=/)<[^<]*)*<\/script>/gi,a=/^(?:text|application)\/javascript/i,s=/^(?:text|application)\/xml/i,u="application/json",f="text/html",c=/^\s*$/;t.active=0,t.ajaxJSONP=function(i,r){if(!("type"in i))return t.ajax(i);var f,h,o=i.jsonpCallback,a=(t.isFunction(o)?o():o)||"jsonp"+ ++e,s=n.createElement("script"),u=window[a],c=function(e){t(s).triggerHandler("error",e||"abort")},l={abort:c};return r&&r.promise(l),t(s).on("load error",function(e,n){clearTimeout(h),t(s).off().remove(),"error"!=e.type&&f?g(f[0],l,i,r):v(null,n||"error",l,i,r),window[a]=u,f&&t.isFunction(u)&&u(f[0]),u=f=void 0}),m(l,i)===!1?(c("abort"),l):(window[a]=function(){f=arguments},s.src=i.url.replace(/\?(.+)=\?/,"?$1="+a),n.head.appendChild(s),i.timeout>0&&(h=setTimeout(function(){c("timeout")},i.timeout)),l)},t.ajaxSettings={type:"GET",beforeSend:b,success:b,error:b,complete:b,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript, application/x-javascript",json:u,xml:"application/xml, text/xml",html:f,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0},t.ajax=function(e){var n=t.extend({},e||{}),o=t.Deferred&&t.Deferred();for(i in t.ajaxSettings)void 0===n[i]&&(n[i]=t.ajaxSettings[i]);p(n),n.crossDomain||(n.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(n.url)&&RegExp.$2!=window.location.host),n.url||(n.url=window.location.toString()),T(n);var a=n.dataType,s=/\?.+=\?/.test(n.url);if(s&&(a="jsonp"),n.cache!==!1&&(e&&e.cache===!0||"script"!=a&&"jsonp"!=a)||(n.url=x(n.url,"_="+Date.now())),"jsonp"==a)return s||(n.url=x(n.url,n.jsonp?n.jsonp+"=?":n.jsonp===!1?"":"callback=?")),t.ajaxJSONP(n,o);var E,u=n.accepts[a],f={},l=function(t,e){f[t.toLowerCase()]=[t,e]},h=/^([\w-]+:)\/\//.test(n.url)?RegExp.$1:window.location.protocol,d=n.xhr(),y=d.setRequestHeader;if(o&&o.promise(d),n.crossDomain||l("X-Requested-With","XMLHttpRequest"),l("Accept",u||"*/*"),(u=n.mimeType||u)&&(u.indexOf(",")>-1&&(u=u.split(",",2)[0]),d.overrideMimeType&&d.overrideMimeType(u)),(n.contentType||n.contentType!==!1&&n.data&&"GET"!=n.type.toUpperCase())&&l("Content-Type",n.contentType||"application/x-www-form-urlencoded"),n.headers)for(r in n.headers)l(r,n.headers[r]);if(d.setRequestHeader=l,d.onreadystatechange=function(){if(4==d.readyState){d.onreadystatechange=b,clearTimeout(E);var e,i=!1;if(d.status>=200&&d.status<300||304==d.status||0==d.status&&"file:"==h){a=a||w(n.mimeType||d.getResponseHeader("content-type")),e=d.responseText;try{"script"==a?(1,eval)(e):"xml"==a?e=d.responseXML:"json"==a&&(e=c.test(e)?null:t.parseJSON(e))}catch(r){i=r}i?v(i,"parsererror",d,n,o):g(e,d,n,o)}else v(d.statusText||null,d.status?"error":"abort",d,n,o)}},m(d,n)===!1)return d.abort(),v(null,"abort",d,n,o),d;if(n.xhrFields)for(r in n.xhrFields)d[r]=n.xhrFields[r];var S="async"in n?n.async:!0;d.open(n.type,n.url,S,n.username,n.password);for(r in f)y.apply(d,f[r]);return n.timeout>0&&(E=setTimeout(function(){d.onreadystatechange=b,d.abort(),v(null,"timeout",d,n,o)},n.timeout)),d.send(n.data?n.data:null),d},t.get=function(){return t.ajax(E.apply(null,arguments))},t.post=function(){var e=E.apply(null,arguments);return e.type="POST",t.ajax(e)},t.getJSON=function(){var e=E.apply(null,arguments);return e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,i){if(!this.length)return this;var s,r=this,a=e.split(/\s/),u=E(e,n,i),f=u.success;return a.length>1&&(u.url=a[0],s=a[1]),u.success=function(e){r.html(s?t("
").html(e.replace(o,"")).find(s):e),f&&f.apply(r,arguments)},t.ajax(u),this};var S=encodeURIComponent;t.param=function(t,e){var n=[];return n.add=function(t,e){this.push(S(t)+"="+S(e))},j(n,t,e),n.join("&").replace(/%20/g,"+")}}(Zepto),function(t){function e(t){var e=this.os={},n=this.browser={},i=t.match(/Web[kK]it[\/]{0,1}([\d.]+)/),r=t.match(/(Android);?[\s\/]+([\d.]+)?/),o=!!t.match(/\(Macintosh\; Intel /),a=t.match(/(iPad).*OS\s([\d_]+)/),s=t.match(/(iPod)(.*OS\s([\d_]+))?/),u=!a&&t.match(/(iPhone\sOS)\s([\d_]+)/),f=t.match(/(webOS|hpwOS)[\s\/]([\d.]+)/),c=t.match(/Windows Phone ([\d.]+)/),l=f&&t.match(/TouchPad/),h=t.match(/Kindle\/([\d.]+)/),p=t.match(/Silk\/([\d._]+)/),d=t.match(/(BlackBerry).*Version\/([\d.]+)/),m=t.match(/(BB10).*Version\/([\d.]+)/),g=t.match(/(RIM\sTablet\sOS)\s([\d.]+)/),v=t.match(/PlayBook/),y=t.match(/Chrome\/([\d.]+)/)||t.match(/CriOS\/([\d.]+)/),b=t.match(/Firefox\/([\d.]+)/),w=t.match(/MSIE\s([\d.]+)/)||t.match(/Trident\/[\d](?=[^\?]+).*rv:([0-9.].)/),x=!y&&t.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/),T=x||t.match(/Version\/([\d.]+)([^S](Safari)|[^M]*(Mobile)[^S]*(Safari))/);(n.webkit=!!i)&&(n.version=i[1]),r&&(e.android=!0,e.version=r[2]),u&&!s&&(e.ios=e.iphone=!0,e.version=u[2].replace(/_/g,".")),a&&(e.ios=e.ipad=!0,e.version=a[2].replace(/_/g,".")),s&&(e.ios=e.ipod=!0,e.version=s[3]?s[3].replace(/_/g,"."):null),c&&(e.wp=!0,e.version=c[1]),f&&(e.webos=!0,e.version=f[2]),l&&(e.touchpad=!0),d&&(e.blackberry=!0,e.version=d[2]),m&&(e.bb10=!0,e.version=m[2]),g&&(e.rimtabletos=!0,e.version=g[2]),v&&(n.playbook=!0),h&&(e.kindle=!0,e.version=h[1]),p&&(n.silk=!0,n.version=p[1]),!p&&e.android&&t.match(/Kindle Fire/)&&(n.silk=!0),y&&(n.chrome=!0,n.version=y[1]),b&&(n.firefox=!0,n.version=b[1]),w&&(n.ie=!0,n.version=w[1]),T&&(o||e.ios)&&(n.safari=!0,o&&(n.version=T[1])),x&&(n.webview=!0),e.tablet=!!(a||v||r&&!t.match(/Mobile/)||b&&t.match(/Tablet/)||w&&!t.match(/Phone/)&&t.match(/Touch/)),e.phone=!(e.tablet||e.ipod||!(r||u||f||d||m||y&&t.match(/Android/)||y&&t.match(/CriOS\/([\d.]+)/)||b&&t.match(/Mobile/)||w&&t.match(/Touch/)))}e.call(t,navigator.userAgent),t.__detect=e}(Zepto),function(t){function a(o,a){var u=o[r],f=u&&e[u];if(void 0===a)return f||s(o);if(f){if(a in f)return f[a];var c=i(a);if(c in f)return f[c]}return n.call(t(o),a)}function s(n,o,a){var s=n[r]||(n[r]=++t.uuid),f=e[s]||(e[s]=u(n));return void 0!==o&&(f[i(o)]=a),f}function u(e){var n={};return t.each(e.attributes||o,function(e,r){0==r.name.indexOf("data-")&&(n[i(r.name.replace("data-",""))]=t.zepto.deserializeValue(r.value))}),n}var e={},n=t.fn.data,i=t.camelCase,r=t.expando="Zepto"+ +new Date,o=[];t.fn.data=function(e,n){return void 0===n?t.isPlainObject(e)?this.each(function(n,i){t.each(e,function(t,e){s(i,t,e)})}):0 in this?a(this[0],e):void 0:this.each(function(){s(this,e,n)})},t.fn.removeData=function(n){return"string"==typeof n&&(n=n.split(/\s+/)),this.each(function(){var o=this[r],a=o&&e[o];a&&t.each(n||a,function(t){delete a[n?i(this):t]})})},["remove","empty"].forEach(function(e){var n=t.fn[e];t.fn[e]=function(){var t=this.find("*");return"remove"===e&&(t=t.add(this)),t.removeData(),n.call(this)}})}(Zepto),function(t){function u(t,e,n,i){return Math.abs(t-e)>=Math.abs(n-i)?t-e>0?"Left":"Right":n-i>0?"Up":"Down"}function f(){o=null,e.last&&(e.el.trigger("longTap"),e={})}function c(){o&&clearTimeout(o),o=null}function l(){n&&clearTimeout(n),i&&clearTimeout(i),r&&clearTimeout(r),o&&clearTimeout(o),n=i=r=o=null,e={}}function h(t){return("touch"==t.pointerType||t.pointerType==t.MSPOINTER_TYPE_TOUCH)&&t.isPrimary}function p(t,e){return t.type=="pointer"+e||t.type.toLowerCase()=="mspointer"+e}var n,i,r,o,s,e={},a=750;t(document).ready(function(){var d,m,y,b,g=0,v=0;"MSGesture"in window&&(s=new MSGesture,s.target=document.body),t(document).bind("MSGestureEnd",function(t){var n=t.velocityX>1?"Right":t.velocityX<-1?"Left":t.velocityY>1?"Down":t.velocityY<-1?"Up":null;n&&(e.el.trigger("swipe"),e.el.trigger("swipe"+n))}).on("touchstart MSPointerDown pointerdown",function(i){(!(b=p(i,"down"))||h(i))&&(y=b?i:i.touches[0],i.touches&&1===i.touches.length&&e.x2&&(e.x2=void 0,e.y2=void 0),d=Date.now(),m=d-(e.last||d),e.el=t("tagName"in y.target?y.target:y.target.parentNode),n&&clearTimeout(n),e.x1=y.pageX,e.y1=y.pageY,m>0&&250>=m&&(e.isDoubleTap=!0),e.last=d,o=setTimeout(f,a),s&&b&&s.addPointer(i.pointerId))}).on("touchmove MSPointerMove pointermove",function(t){(!(b=p(t,"move"))||h(t))&&(y=b?t:t.touches[0],c(),e.x2=y.pageX,e.y2=y.pageY,g+=Math.abs(e.x1-e.x2),v+=Math.abs(e.y1-e.y2))}).on("touchend MSPointerUp pointerup",function(o){(!(b=p(o,"up"))||h(o))&&(c(),e.x2&&Math.abs(e.x1-e.x2)>30||e.y2&&Math.abs(e.y1-e.y2)>30?r=setTimeout(function(){e.el.trigger("swipe"),e.el.trigger("swipe"+u(e.x1,e.x2,e.y1,e.y2)),e={}},0):"last"in e&&(30>g&&30>v?i=setTimeout(function(){var i=t.Event("tap");i.cancelTouch=l,e.el.trigger(i),e.isDoubleTap?(e.el&&e.el.trigger("doubleTap"),e={}):n=setTimeout(function(){n=null,e.el&&e.el.trigger("singleTap"),e={}},250)},0):e={}),g=v=0)}).on("touchcancel MSPointerCancel pointercancel",l),t(window).on("scroll",l)}),["swipe","swipeLeft","swipeRight","swipeUp","swipeDown","doubleTap","tap","singleTap","longTap"].forEach(function(e){t.fn[e]=function(t){return this.on(e,t)}})}(Zepto),function(t){t.Callbacks=function(e){e=t.extend({},e);var n,i,r,o,a,s,u=[],f=!e.once&&[],c=function(t){for(n=e.memory&&t,i=!0,s=o||0,o=0,a=u.length,r=!0;u&&a>s;++s)if(u[s].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}r=!1,u&&(f?f.length&&c(f.shift()):n?u.length=0:l.disable())},l={add:function(){if(u){var i=u.length,s=function(n){t.each(n,function(t,n){"function"==typeof n?e.unique&&l.has(n)||u.push(n):n&&n.length&&"string"!=typeof n&&s(n)})};s(arguments),r?a=u.length:n&&(o=i,c(n))}return this},remove:function(){return u&&t.each(arguments,function(e,n){for(var i;(i=t.inArray(n,u,i))>-1;)u.splice(i,1),r&&(a>=i&&--a,s>=i&&--s)}),this},has:function(e){return!(!u||!(e?t.inArray(e,u)>-1:u.length))},empty:function(){return a=u.length=0,this},disable:function(){return u=f=n=void 0,this},disabled:function(){return!u},lock:function(){return f=void 0,n||l.disable(),this},locked:function(){return!f},fireWith:function(t,e){return!u||i&&!f||(e=e||[],e=[t,e.slice?e.slice():e],r?f.push(e):c(e)),this},fire:function(){return l.fireWith(this,arguments)},fired:function(){return!!i}};return l}}(Zepto),function(t){function n(e){var i=[["resolve","done",t.Callbacks({once:1,memory:1}),"resolved"],["reject","fail",t.Callbacks({once:1,memory:1}),"rejected"],["notify","progress",t.Callbacks({memory:1})]],r="pending",o={state:function(){return r},always:function(){return a.done(arguments).fail(arguments),this},then:function(){var e=arguments;return n(function(n){t.each(i,function(i,r){var s=t.isFunction(e[i])&&e[i];a[r[1]](function(){var e=s&&s.apply(this,arguments);if(e&&t.isFunction(e.promise))e.promise().done(n.resolve).fail(n.reject).progress(n.notify);else{var i=this===o?n.promise():this,a=s?[e]:arguments;n[r[0]+"With"](i,a)}})}),e=null}).promise()},promise:function(e){return null!=e?t.extend(e,o):o}},a={};return t.each(i,function(t,e){var n=e[2],s=e[3];o[e[1]]=n.add,s&&n.add(function(){r=s},i[1^t][2].disable,i[2][2].lock),a[e[0]]=function(){return a[e[0]+"With"](this===a?o:this,arguments),this},a[e[0]+"With"]=n.fireWith}),o.promise(a),e&&e.call(a,a),a}var e=Array.prototype.slice;t.when=function(i){var f,c,l,r=e.call(arguments),o=r.length,a=0,s=1!==o||i&&t.isFunction(i.promise)?o:0,u=1===s?i:n(),h=function(t,n,i){return function(r){n[t]=this,i[t]=arguments.length>1?e.call(arguments):r,i===f?u.notifyWith(n,i):--s||u.resolveWith(n,i)}};if(o>1)for(f=new Array(o),c=new Array(o),l=new Array(o);o>a;++a)r[a]&&t.isFunction(r[a].promise)?r[a].promise().done(h(a,l,r)).fail(u.reject).progress(h(a,c,f)):--s;return s||u.resolveWith(l,r),u.promise()},t.Deferred=n}(Zepto),function(t,e){function w(t){return t.replace(/([a-z])([A-Z])/,"$1-$2").toLowerCase()}function x(t){return i?i+t:t.toLowerCase()}var i,c,l,h,p,d,m,g,v,y,n="",a={Webkit:"webkit",Moz:"",O:"o"},s=window.document,u=s.createElement("div"),f=/^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,b={};t.each(a,function(t,r){return u.style[t+"TransitionProperty"]!==e?(n="-"+t.toLowerCase()+"-",i=r,!1):void 0 3 | }),c=n+"transform",b[l=n+"transition-property"]=b[h=n+"transition-duration"]=b[d=n+"transition-delay"]=b[p=n+"transition-timing-function"]=b[m=n+"animation-name"]=b[g=n+"animation-duration"]=b[y=n+"animation-delay"]=b[v=n+"animation-timing-function"]="",t.fx={off:i===e&&u.style.transitionProperty===e,speeds:{_default:400,fast:200,slow:600},cssPrefix:n,transitionEnd:x("TransitionEnd"),animationEnd:x("AnimationEnd")},t.fn.animate=function(n,i,r,o,a){return t.isFunction(i)&&(o=i,r=e,i=e),t.isFunction(r)&&(o=r,r=e),t.isPlainObject(i)&&(r=i.easing,o=i.complete,a=i.delay,i=i.duration),i&&(i=("number"==typeof i?i:t.fx.speeds[i]||t.fx.speeds._default)/1e3),a&&(a=parseFloat(a)/1e3),this.anim(n,i,r,o,a)},t.fn.anim=function(n,i,r,o,a){var s,x,S,u={},T="",E=this,j=t.fx.transitionEnd,P=!1;if(i===e&&(i=t.fx.speeds._default/1e3),a===e&&(a=0),t.fx.off&&(i=0),"string"==typeof n)u[m]=n,u[g]=i+"s",u[y]=a+"s",u[v]=r||"linear",j=t.fx.animationEnd;else{x=[];for(s in n)f.test(s)?T+=s+"("+n[s]+") ":(u[s]=n[s],x.push(w(s)));T&&(u[c]=T,x.push(c)),i>0&&"object"==typeof n&&(u[l]=x.join(", "),u[h]=i+"s",u[d]=a+"s",u[p]=r||"linear")}return S=function(e){if("undefined"!=typeof e){if(e.target!==e.currentTarget)return;t(e.target).unbind(j,S)}else t(this).unbind(j,S);P=!0,t(this).css(b),o&&o.call(this)},i>0&&(this.bind(j,S),setTimeout(function(){P||S.call(E)},1e3*i+25)),this.size()&&this.get(0).clientLeft,this.css(u),0>=i&&setTimeout(function(){E.each(function(){S.call(this)})},0),this},u=null}(Zepto); -------------------------------------------------------------------------------- /demo/styles/css/demo.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 62.5%; 3 | } 4 | body { 5 | margin:0; 6 | padding:0; 7 | background-color: rgb(249, 249, 249); 8 | -webkit-font-smoothing: antialiased; 9 | font-family: "RobotoDraft","Roboto",sans-serif; 10 | font-size: 1.4rem; 11 | color: rgb(33, 33, 33); 12 | } 13 | a { 14 | text-decoration:none; 15 | font-family:arial,sans-serif; 16 | font-size:1.6rem; 17 | line-height:2rem; 18 | } 19 | .demo-block a { 20 | border-left: 5px solid; 21 | border-left-color: rgb(178, 235, 242); 22 | padding-left: 20px; 23 | color: #00BCD4; 24 | } 25 | a.no-border { 26 | border:none; 27 | padding-left: 0; 28 | } 29 | 30 | .nav-bar { 31 | position:fixed; 32 | top:0; 33 | left:0; 34 | z-index: 100; 35 | 36 | height:60px; 37 | width:100%; 38 | line-height:2.2; 39 | 40 | background-color: #02BBD3; 41 | color: #fff; 42 | font-size: 2.8rem; 43 | } 44 | .nav-wrapper { 45 | max-width:800px; 46 | margin: 0 auto; 47 | } 48 | .nav-bar .title { 49 | margin-left: 2rem; 50 | } 51 | .rotate-btn { 52 | display: inline-block; 53 | cursor:pointer; 54 | width: 25px; 55 | height: 25px; 56 | box-sizing: border-box; 57 | border-width: 3px; 58 | border-style: solid; 59 | border-radius: 999px; 60 | margin-top: 10px; 61 | transform: rotate(-100deg); 62 | -webkit-transform: rotate(-100deg); 63 | border-color: #fff #fff rgba(0,0,0,0); 64 | } 65 | .rotate-btn:before { 66 | content: ''; 67 | display: block; 68 | position: relative; 69 | top: 15px; 70 | left: 6px; 71 | width: 0; 72 | height: 0; 73 | border-width: 6px 6px; 74 | border-style: solid; 75 | transform: rotate(54deg); 76 | -webkit-transform: rotate(54deg); 77 | border-color: #fff #02BBD3 #02BBD3 #02BBD3; 78 | } 79 | #doRotate { 80 | position: relative; 81 | float: right; 82 | width: 6rem; 83 | height: 100%; 84 | text-align: center; 85 | cursor:pointer; 86 | } 87 | .rotate-btn-wrapper { 88 | background-color: #02BBD3; 89 | color: white; 90 | text-align:center; 91 | height: 40px; 92 | width: 150px; 93 | line-height:40px; 94 | border-radius: 10px; 95 | cursor:pointer; 96 | font-size: 1.6rem; 97 | } 98 | .demo-rotate-btn { 99 | position:relative; 100 | } 101 | .contents { 102 | position: relative; 103 | top: 50px; 104 | left: 0; 105 | margin: 0 auto; 106 | max-width: 800px; 107 | 108 | } 109 | @media screen and (max-width:700px){ 110 | .contents { 111 | position: absolute; 112 | top: 60px; 113 | left: 0; 114 | right: 0; 115 | bottom: 0; 116 | /* -webkit-overflow-scrolling: touch; */ 117 | overflow-y: scroll; 118 | } 119 | } 120 | .demo-block { 121 | margin: 0 2rem; 122 | line-height: 1.5; 123 | } 124 | .demo-block h2 { 125 | color: rgb(0, 188, 212); 126 | font-size: 3rem; 127 | font-weight: 300; 128 | line-height: 40px; 129 | } 130 | .demo-block.intro p { 131 | font-size:1.8rem; 132 | } 133 | .demo-block h3 { 134 | /* border-left: 5px solid; */ 135 | /* border-left-color: rgb(178, 235, 242); */ 136 | /* padding-left: 20px; */ 137 | color: rgb(0, 188, 212); 138 | font-size: 2rem; 139 | font-weight: 300; 140 | line-height: 40px; 141 | 142 | } 143 | .demo-block h4 { 144 | font-size: 1.8rem; 145 | font-weight: 300; 146 | } 147 | .demo-block img { 148 | width: 100%; 149 | max-width: 688px; 150 | } 151 | .demo-block .half-img { 152 | max-width: 340px; 153 | } 154 | .demo-block .qr-img { 155 | display: block; 156 | margin:0 auto; 157 | width: 300px; 158 | height: 300px; 159 | max-width:100%; 160 | } 161 | @media screen and (min-width:600px){ 162 | .demo-block .half-img { 163 | width: 49%; 164 | max-width: 340px; 165 | } 166 | } 167 | 168 | .demo-button { 169 | width: 50px; 170 | line-height: 25px; 171 | height: 25px; 172 | display: inline-block; 173 | background: #FFA500; 174 | text-align: center; 175 | color: #FFF; 176 | font-size: 1.7rem; 177 | border-radius: 3px; 178 | margin: 15px 12px 5px 0; 179 | cursor: pointer; 180 | } 181 | .demo-button:hover { 182 | background:#00BCD4; 183 | } 184 | .demo-button.btn-big { 185 | width:100px; 186 | } 187 | .demo-bar { 188 | margin: 10px 0; 189 | width:100%; 190 | max-width: 690px; 191 | height:5px; 192 | } 193 | .demo-code { 194 | background-color: #EBECEA; 195 | padding: 20px 0 20px 10px; 196 | max-width: 678px; 197 | color: #000; 198 | } 199 | 200 | .demo-code.short { 201 | padding: 0.4rem 1.3rem; 202 | line-height: 1.8; 203 | } 204 | .demo-code .indent { 205 | padding-left:25px; 206 | margin: 0; 207 | } 208 | 209 | .demo-footer { 210 | margin-bottom: 50px; 211 | } 212 | 213 | /* fork github*/ 214 | #forkongithub a{ 215 | position:relative; 216 | top: 15px; 217 | left: 20px; 218 | padding:5px 40px; 219 | 220 | background:#FFA500; 221 | color:#fff; 222 | text-align:center; 223 | font-weight:bold; 224 | transition:0.5s; 225 | } 226 | #forkongithub a:hover{ 227 | background:#00BCD4; 228 | color:#fff; 229 | } 230 | #forkongithub a::before, 231 | #forkongithub a::after { 232 | content:""; 233 | width:100%; 234 | display:block; 235 | position:absolute; 236 | top:1px; 237 | left:0; 238 | height:1px; 239 | background:#fff; 240 | } 241 | #forkongithub a::after{ 242 | bottom:1px; 243 | top:auto; 244 | } 245 | @media screen and (min-width:800px){ 246 | #forkongithub{ 247 | position:fixed; 248 | display:block !important; 249 | top:0; 250 | right:0; 251 | width:200px; 252 | overflow:hidden; 253 | height:200px; 254 | z-index:9999; 255 | } 256 | #forkongithub a{ 257 | width:200px; 258 | position:absolute; 259 | top:60px; 260 | right:-60px; 261 | left:0; 262 | transform:rotate(45deg); 263 | -webkit-transform:rotate(45deg); 264 | -ms-transform:rotate(45deg); 265 | -moz-transform:rotate(45deg); 266 | -o-transform:rotate(45deg); 267 | box-shadow:4px 4px 10px rgba(0,0,0,0.5); 268 | } 269 | } 270 | 271 | -------------------------------------------------------------------------------- /demo/styles/images/above.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningtgc/material-refresh/6b1be0046d58230ecc00dc929557ad4b3459e304/demo/styles/images/above.gif -------------------------------------------------------------------------------- /demo/styles/images/below-color.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningtgc/material-refresh/6b1be0046d58230ecc00dc929557ad4b3459e304/demo/styles/images/below-color.gif -------------------------------------------------------------------------------- /demo/styles/images/below.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningtgc/material-refresh/6b1be0046d58230ecc00dc929557ad4b3459e304/demo/styles/images/below.gif -------------------------------------------------------------------------------- /demo/styles/images/button-action.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningtgc/material-refresh/6b1be0046d58230ecc00dc929557ad4b3459e304/demo/styles/images/button-action.gif -------------------------------------------------------------------------------- /demo/styles/images/qr-above.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningtgc/material-refresh/6b1be0046d58230ecc00dc929557ad4b3459e304/demo/styles/images/qr-above.png -------------------------------------------------------------------------------- /demo/styles/images/qr-below.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningtgc/material-refresh/6b1be0046d58230ecc00dc929557ad4b3459e304/demo/styles/images/qr-below.png -------------------------------------------------------------------------------- /demo/test-jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Material Design swipe to refresh 8 | 9 | 10 | 11 | 12 | 20 | 21 |
22 | Fork me on GitHub 23 |
24 |

Intro

26 |

Google Material Design swipe (pull) to refresh.

27 |

It uses CSS3 and JavaScript depend on Zepto or jQuery.

28 |

Mobile Only and High Performance.

29 |

You can use 3 types of this Material Design refresher.

30 |
31 |
32 |

Type1: Above surface (default)

33 | Above surface 34 | 35 |

Source code:

36 |

37 |

38 | mRefresh(); 39 |
40 |

41 |

You can try it in mobile browser or webview by scanning the following QR code:

42 | 43 | QR 44 |

45 | URL: http://lightningtgc.github.io/material-refresh/type1.html 46 |

47 |
48 |
49 |

Type2: Below surface

50 | Below surface 51 | 52 |

Source code:

53 |
54 | mRefresh({ 55 |

56 | nav: '#navMain' 57 |

58 | }); 59 |
60 | 61 |

Actually, this page is the demo for Type2.

62 |

You can try it in chrome device mode or other mobile emulator.

63 | 64 |

Similarly, try it in mobile browser or webview by scanning the following QR code:

65 | 66 | QR 67 |

68 | URL: http://lightningtgc.github.io/material-refresh/index.html 69 |

70 | 71 |
72 |
73 |

Type3: Button action

74 | Button action 75 | 76 |

Source code:

77 |
78 | $('#buttonAction').on('tap', function(){ 79 |

80 | mRefresh.refresh(); 81 |

82 | }); 83 |
84 | 85 |

Click this button ↓↓ to try it and notice the top of page ↑↑

86 |
87 |
88 |
89 |
90 | 91 | 98 | 99 | 100 | 101 | 102 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /demo/type1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Material Design swipe to refresh 8 | 9 | 10 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |

Type1 Demo

30 |

Google Material Design swipe (pull) to refresh.

31 |

Open this site in mobile browser or webview, then use your finger to pull down the page.

32 |

You can also click some button ↓↓ to refresh the page.

33 |
34 | Click me! 35 |
36 |

37 | Maybe you can try the Type2:Below surface 38 |

39 | 40 |

Three types of this Material Design refresher.

41 |
42 |
43 |

Type1: Above surface (default)

44 | Above surface 45 | 46 |

Source code:

47 |

48 |

49 | mRefresh(); 50 |
51 |

52 | 53 |

You can try it in chrome device mode or other mobile emulator.

54 |

Similarly, try it in mobile browser or webview by scanning the following QR code:

55 | 56 | QR 57 | 58 |

59 | URL: http://lightningtgc.github.io/material-refresh/type1.html 60 |

61 | 62 |
63 |
64 |

Type2: Below surface

65 | Below surface 66 | 67 |

Source code:

68 |
69 | mRefresh({ 70 |

71 | nav: '#navMain' 72 |

73 | }); 74 |
75 | 76 |

Try it in mobile browser or webview by scanning the following QR code:

77 | 78 | QR 79 |

80 | URL: http://lightningtgc.github.io/material-refresh/index.html 81 |

82 |
83 |
84 |

Type3: Button action

85 | Button action 86 | 87 |

Source code:

88 |
89 | $('#buttonAction').on('tap', function(){ 90 |

91 | mRefresh.refresh(); 92 |

93 | }); 94 |
95 | 96 |

Click this button ↓↓ to try it and notice the top of page ↑↑

97 |
98 |
99 |
100 |
101 | 102 | 109 | 110 | 111 | 112 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gUglify = require('gulp-uglify'); 3 | var gRename = require('gulp-rename'); 4 | var gStylus = require('gulp-stylus'); 5 | var gMinifyCSS = require('gulp-minify-css'); 6 | var gTranspile = require('gulp-es6-module-transpiler'); 7 | var del = require('del'); 8 | 9 | var projectName = 'material-refresh'; 10 | 11 | var jsName = projectName + '.js'; 12 | var jsMinName = projectName + '.min.js'; 13 | var cssName = projectName + '.css'; 14 | var cssMinName = projectName + '.min.css'; 15 | 16 | var devPath = './dev'; 17 | var buildPath = './build'; 18 | 19 | var paths = { 20 | srcCSS: './src/css/*.styl', 21 | srcJS: './src/js/main.js', 22 | srcBase: './src/js', 23 | destJS: devPath + '/js', 24 | destCSS: devPath + '/css' 25 | } 26 | 27 | // clean 28 | gulp.task('clean', function(cb){ 29 | del([devPath], cb); 30 | }); 31 | 32 | // Handle CSS 33 | gulp.task('stylus', ['clean'], function(){ 34 | 35 | gulp.src(paths.srcCSS) 36 | .pipe(gStylus()) 37 | .pipe(gulp.dest(paths.destCSS)) 38 | .pipe(gMinifyCSS()) 39 | .pipe(gRename(cssMinName)) 40 | .pipe(gulp.dest(paths.destCSS)); 41 | }); 42 | 43 | // Handle JS 44 | gulp.task('es6module', ['clean'], function(){ 45 | return gulp.src(paths.srcJS) 46 | .pipe(gTranspile({ 47 | formatter: 'bundle', 48 | basePath: paths.srcBase 49 | })) 50 | .pipe(gRename(jsName)) 51 | .pipe(gulp.dest(paths.destJS)) 52 | .pipe(gUglify()) 53 | .pipe(gRename(jsMinName)) 54 | .pipe(gulp.dest(paths.destJS)); 55 | }); 56 | 57 | // watch change 58 | gulp.task('watch', function(){ 59 | gulp.watch(paths.srcCSS, ['stylus']); 60 | }); 61 | 62 | gulp.task('default', [ 'stylus', 'es6module']); 63 | 64 | gulp.task('build', ['stylus', 'es6module'], function(){ 65 | return gulp.src(devPath + '/*/*') 66 | .pipe(gulp.dest(buildPath)); 67 | }); 68 | 69 | gulp.task('publish', ['stylus', 'es6module'], function(){ 70 | gulp.src([devPath + '/css/' + cssMinName, devPath + '/js/' + jsMinName]) 71 | .pipe(gulp.dest('./')); 72 | }); 73 | -------------------------------------------------------------------------------- /material-refresh.css: -------------------------------------------------------------------------------- 1 | .mui-refresh-main { 2 | position: absolute; 3 | left: 50%; 4 | margin-left: -25px; 5 | padding: 9px; 6 | z-index: 10001; 7 | background-color: #fff; 8 | overflow: hidden; 9 | border-radius: 999px; 10 | box-shadow: 0 3px 8px 0 rgba(0,0,0,0.19), 0 6px 13px 0 rgba(0,0,0,0.24); 11 | -webkit-box-shadow: 0 3px 8px 0 rgba(0,0,0,0.19), 0 6px 13px 0 rgba(0,0,0,0.24); 12 | } 13 | .mui-refresh-main { 14 | position: fixed; 15 | top: 0; 16 | -webkit-perspective: 1000; 17 | -webkit-backface-visibility: hidden; 18 | opacity: 0; 19 | -webkit-transform: scale(0); 20 | } 21 | /*transition*/ 22 | .mui-refresh-main-animat { 23 | /* transition: box-shadow 0.38s cubic-bezier(0.4, 0, 0.2, 1) */ 24 | /* -webkit-transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1) */ 25 | /* transition: all 0.43s cubic-bezier(.08,.55,.81,1.8) */ 26 | -webkit-transition: all 0.43s cubic-bezier(0.08, 0.55, 0.81, 1.8); 27 | } 28 | /* below nav bar */ 29 | .mui-refresh-nav { 30 | -webkit-transform: scale(1); 31 | } 32 | .mui-refresh-noshow { 33 | opacity: 0; 34 | -webkit-transform: scale(0.01) !important; 35 | -webkit-transition: all 0.25s ease-in-out !important; 36 | } 37 | .mui-refresh-wrapper { 38 | width: 25px; 39 | height: 25px; 40 | } 41 | .mui-arrow-wrapper { 42 | -webkit-transition: all 0.2s ease; 43 | } 44 | .mui-half-circle, 45 | .mui-arrow-main { 46 | position: absolute; 47 | top: 0; 48 | width: 25px; 49 | height: 25px; 50 | box-sizing: border-box; 51 | border-width: 3px; 52 | border-style: solid; 53 | border-color: #000 #000 transparent; 54 | border-radius: 999px; 55 | } 56 | .mui-arrow-main { 57 | margin-top: 10px; 58 | /* transform: rotate(-37deg) */ 59 | -webkit-transform: rotate(-37deg); 60 | /* arrow */ 61 | } 62 | .mui-arrow-main:before { 63 | content: ''; 64 | display: block; 65 | position: relative; 66 | top: 14px; 67 | left: 0px; 68 | width: 0; 69 | height: 0; 70 | border-width: 6px 6px; 71 | border-style: solid; 72 | transform: rotate(-56deg); 73 | -webkit-transform: rotate(-56deg); 74 | } 75 | .mui-spinner-main { 76 | width: 25px; 77 | height: 25px; 78 | position: relative; 79 | } 80 | .mui-spinner-main .mui-spinner-left, 81 | .mui-spinner-main .mui-spinner-right { 82 | position: absolute; 83 | top: 0; 84 | height: 25px; 85 | width: 13px; 86 | overflow: hidden; 87 | } 88 | .mui-spinner-main .mui-spinner-left { 89 | left: 0; 90 | } 91 | .mui-spinner-main .mui-spinner-left .mui-half-circle { 92 | left: 0; 93 | border-right-color: transparent; 94 | } 95 | .mui-spinner-main .mui-spinner-right { 96 | right: 0; 97 | } 98 | .mui-spinner-main .mui-spinner-right .mui-half-circle { 99 | right: 0; 100 | border-left-color: transparent; 101 | } 102 | /* Blue theme */ 103 | .mui-blue-theme .mui-arrow-main { 104 | border-color: #2196f3 #2196f3 rgba(0,0,0,0); 105 | } 106 | .mui-blue-theme .mui-arrow-main:before { 107 | border-color: #2196f3 #fff #fff #fff; 108 | } 109 | .mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle, 110 | .mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle { 111 | border-top-color: #2196f3; 112 | } 113 | .mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle { 114 | border-left-color: #2196f3; 115 | } 116 | .mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle { 117 | border-right-color: #2196f3; 118 | } 119 | /* Animation */ 120 | .mui-spinner-wrapper { 121 | -webkit-animation: outer-rotate 2.91667s linear infinite; 122 | animation: outer-rotate 2.91667s linear infinite; 123 | } 124 | .mui-spinner-main { 125 | -webkit-animation: sporadic-rotate 5.25s cubic-bezier(0.35, 0, 0.25, 1) infinite; 126 | animation: sporadic-rotate 5.25s cubic-bezier(0.35, 0, 0.25, 1) infinite; 127 | } 128 | .mui-spinner-main .mui-spinner-left .mui-half-circle, 129 | .mui-spinner-main .mui-spinner-right .mui-half-circle { 130 | -webkit-animation-iteration-count: infinite; 131 | animation-iteration-count: infinite; 132 | -webkit-animation-duration: 1.3125s; 133 | animation-duration: 1.3125s; 134 | -webkit-animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1); 135 | animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1); 136 | } 137 | .mui-spinner-main .mui-spinner-left .mui-half-circle { 138 | -webkit-animation-name: left-wobble; 139 | animation-name: left-wobble; 140 | } 141 | .mui-spinner-main .mui-spinner-right .mui-half-circle { 142 | -webkit-animation-name: right-wobble; 143 | animation-name: right-wobble; 144 | } 145 | @-webkit-keyframes outer-rotate { 146 | 100% { 147 | -webkit-transform: rotate(360deg); 148 | transform: rotate(360deg); 149 | } 150 | } 151 | @-webkit-keyframes left-wobble { 152 | 0%, 100% { 153 | -webkit-transform: rotate(130deg); 154 | transform: rotate(130deg); 155 | } 156 | 50% { 157 | -webkit-transform: rotate(-5deg); 158 | transform: rotate(-5deg); 159 | } 160 | } 161 | @-webkit-keyframes right-wobble { 162 | 0%, 100% { 163 | -webkit-transform: rotate(-130deg); 164 | transform: rotate(-130deg); 165 | } 166 | 50% { 167 | -webkit-transform: rotate(5deg); 168 | transform: rotate(5deg); 169 | } 170 | } 171 | @-webkit-keyframes sporadic-rotate { 172 | 12.5% { 173 | -webkit-transform: rotate(135deg); 174 | transform: rotate(135deg); 175 | } 176 | 25% { 177 | -webkit-transform: rotate(270deg); 178 | transform: rotate(270deg); 179 | } 180 | 37.5% { 181 | -webkit-transform: rotate(405deg); 182 | transform: rotate(405deg); 183 | } 184 | 50% { 185 | -webkit-transform: rotate(540deg); 186 | transform: rotate(540deg); 187 | } 188 | 62.5% { 189 | -webkit-transform: rotate(675deg); 190 | transform: rotate(675deg); 191 | } 192 | 75% { 193 | -webkit-transform: rotate(810deg); 194 | transform: rotate(810deg); 195 | } 196 | 87.5% { 197 | -webkit-transform: rotate(945deg); 198 | transform: rotate(945deg); 199 | } 200 | 100% { 201 | -webkit-transform: rotate(1080deg); 202 | transform: rotate(1080deg); 203 | } 204 | } 205 | @-moz-keyframes outer-rotate { 206 | 100% { 207 | -webkit-transform: rotate(360deg); 208 | transform: rotate(360deg); 209 | } 210 | } 211 | @-webkit-keyframes outer-rotate { 212 | 100% { 213 | -webkit-transform: rotate(360deg); 214 | transform: rotate(360deg); 215 | } 216 | } 217 | @-o-keyframes outer-rotate { 218 | 100% { 219 | -webkit-transform: rotate(360deg); 220 | transform: rotate(360deg); 221 | } 222 | } 223 | @keyframes outer-rotate { 224 | 100% { 225 | -webkit-transform: rotate(360deg); 226 | transform: rotate(360deg); 227 | } 228 | } 229 | @-moz-keyframes left-wobble { 230 | 0%, 100% { 231 | -webkit-transform: rotate(130deg); 232 | transform: rotate(130deg); 233 | } 234 | 50% { 235 | -webkit-transform: rotate(-5deg); 236 | transform: rotate(-5deg); 237 | } 238 | } 239 | @-webkit-keyframes left-wobble { 240 | 0%, 100% { 241 | -webkit-transform: rotate(130deg); 242 | transform: rotate(130deg); 243 | } 244 | 50% { 245 | -webkit-transform: rotate(-5deg); 246 | transform: rotate(-5deg); 247 | } 248 | } 249 | @-o-keyframes left-wobble { 250 | 0%, 100% { 251 | -webkit-transform: rotate(130deg); 252 | transform: rotate(130deg); 253 | } 254 | 50% { 255 | -webkit-transform: rotate(-5deg); 256 | transform: rotate(-5deg); 257 | } 258 | } 259 | @keyframes left-wobble { 260 | 0%, 100% { 261 | -webkit-transform: rotate(130deg); 262 | transform: rotate(130deg); 263 | } 264 | 50% { 265 | -webkit-transform: rotate(-5deg); 266 | transform: rotate(-5deg); 267 | } 268 | } 269 | @-moz-keyframes right-wobble { 270 | 0%, 100% { 271 | -webkit-transform: rotate(-130deg); 272 | transform: rotate(-130deg); 273 | } 274 | 50% { 275 | -webkit-transform: rotate(5deg); 276 | transform: rotate(5deg); 277 | } 278 | } 279 | @-webkit-keyframes right-wobble { 280 | 0%, 100% { 281 | -webkit-transform: rotate(-130deg); 282 | transform: rotate(-130deg); 283 | } 284 | 50% { 285 | -webkit-transform: rotate(5deg); 286 | transform: rotate(5deg); 287 | } 288 | } 289 | @-o-keyframes right-wobble { 290 | 0%, 100% { 291 | -webkit-transform: rotate(-130deg); 292 | transform: rotate(-130deg); 293 | } 294 | 50% { 295 | -webkit-transform: rotate(5deg); 296 | transform: rotate(5deg); 297 | } 298 | } 299 | @keyframes right-wobble { 300 | 0%, 100% { 301 | -webkit-transform: rotate(-130deg); 302 | transform: rotate(-130deg); 303 | } 304 | 50% { 305 | -webkit-transform: rotate(5deg); 306 | transform: rotate(5deg); 307 | } 308 | } 309 | @-moz-keyframes sporadic-rotate { 310 | 12.5% { 311 | -webkit-transform: rotate(135deg); 312 | transform: rotate(135deg); 313 | } 314 | 25% { 315 | -webkit-transform: rotate(270deg); 316 | transform: rotate(270deg); 317 | } 318 | 37.5% { 319 | -webkit-transform: rotate(405deg); 320 | transform: rotate(405deg); 321 | } 322 | 50% { 323 | -webkit-transform: rotate(540deg); 324 | transform: rotate(540deg); 325 | } 326 | 62.5% { 327 | -webkit-transform: rotate(675deg); 328 | transform: rotate(675deg); 329 | } 330 | 75% { 331 | -webkit-transform: rotate(810deg); 332 | transform: rotate(810deg); 333 | } 334 | 87.5% { 335 | -webkit-transform: rotate(945deg); 336 | transform: rotate(945deg); 337 | } 338 | 100% { 339 | -webkit-transform: rotate(1080deg); 340 | transform: rotate(1080deg); 341 | } 342 | } 343 | @-webkit-keyframes sporadic-rotate { 344 | 12.5% { 345 | -webkit-transform: rotate(135deg); 346 | transform: rotate(135deg); 347 | } 348 | 25% { 349 | -webkit-transform: rotate(270deg); 350 | transform: rotate(270deg); 351 | } 352 | 37.5% { 353 | -webkit-transform: rotate(405deg); 354 | transform: rotate(405deg); 355 | } 356 | 50% { 357 | -webkit-transform: rotate(540deg); 358 | transform: rotate(540deg); 359 | } 360 | 62.5% { 361 | -webkit-transform: rotate(675deg); 362 | transform: rotate(675deg); 363 | } 364 | 75% { 365 | -webkit-transform: rotate(810deg); 366 | transform: rotate(810deg); 367 | } 368 | 87.5% { 369 | -webkit-transform: rotate(945deg); 370 | transform: rotate(945deg); 371 | } 372 | 100% { 373 | -webkit-transform: rotate(1080deg); 374 | transform: rotate(1080deg); 375 | } 376 | } 377 | @-o-keyframes sporadic-rotate { 378 | 12.5% { 379 | -webkit-transform: rotate(135deg); 380 | transform: rotate(135deg); 381 | } 382 | 25% { 383 | -webkit-transform: rotate(270deg); 384 | transform: rotate(270deg); 385 | } 386 | 37.5% { 387 | -webkit-transform: rotate(405deg); 388 | transform: rotate(405deg); 389 | } 390 | 50% { 391 | -webkit-transform: rotate(540deg); 392 | transform: rotate(540deg); 393 | } 394 | 62.5% { 395 | -webkit-transform: rotate(675deg); 396 | transform: rotate(675deg); 397 | } 398 | 75% { 399 | -webkit-transform: rotate(810deg); 400 | transform: rotate(810deg); 401 | } 402 | 87.5% { 403 | -webkit-transform: rotate(945deg); 404 | transform: rotate(945deg); 405 | } 406 | 100% { 407 | -webkit-transform: rotate(1080deg); 408 | transform: rotate(1080deg); 409 | } 410 | } 411 | @keyframes sporadic-rotate { 412 | 12.5% { 413 | -webkit-transform: rotate(135deg); 414 | transform: rotate(135deg); 415 | } 416 | 25% { 417 | -webkit-transform: rotate(270deg); 418 | transform: rotate(270deg); 419 | } 420 | 37.5% { 421 | -webkit-transform: rotate(405deg); 422 | transform: rotate(405deg); 423 | } 424 | 50% { 425 | -webkit-transform: rotate(540deg); 426 | transform: rotate(540deg); 427 | } 428 | 62.5% { 429 | -webkit-transform: rotate(675deg); 430 | transform: rotate(675deg); 431 | } 432 | 75% { 433 | -webkit-transform: rotate(810deg); 434 | transform: rotate(810deg); 435 | } 436 | 87.5% { 437 | -webkit-transform: rotate(945deg); 438 | transform: rotate(945deg); 439 | } 440 | 100% { 441 | -webkit-transform: rotate(1080deg); 442 | transform: rotate(1080deg); 443 | } 444 | } 445 | 446 | -------------------------------------------------------------------------------- /material-refresh.min.css: -------------------------------------------------------------------------------- 1 | .mui-refresh-main{left:50%;margin-left:-25px;padding:9px;z-index:10001;background-color:#fff;overflow:hidden;border-radius:999px;box-shadow:0 3px 8px 0 rgba(0,0,0,.19),0 6px 13px 0 rgba(0,0,0,.24);-webkit-box-shadow:0 3px 8px 0 rgba(0,0,0,.19),0 6px 13px 0 rgba(0,0,0,.24);position:fixed;top:0;-webkit-perspective:1000;-webkit-backface-visibility:hidden;opacity:0;-webkit-transform:scale(0)}.mui-refresh-main-animat{-webkit-transition:all .43s cubic-bezier(.08,.55,.81,1.8)}.mui-refresh-nav{-webkit-transform:scale(1)}.mui-refresh-noshow{opacity:0;-webkit-transform:scale(.01)!important;-webkit-transition:all .25s ease-in-out!important}.mui-refresh-wrapper{width:25px;height:25px}.mui-arrow-wrapper{-webkit-transition:all .2s ease}.mui-arrow-main,.mui-half-circle{position:absolute;top:0;width:25px;height:25px;box-sizing:border-box;border-width:3px;border-style:solid;border-color:#000 #000 transparent;border-radius:999px}.mui-arrow-main{margin-top:10px;-webkit-transform:rotate(-37deg)}.mui-arrow-main:before{content:'';display:block;position:relative;top:14px;left:0;width:0;height:0;border-width:6px;border-style:solid;transform:rotate(-56deg);-webkit-transform:rotate(-56deg)}.mui-spinner-main{width:25px;height:25px;position:relative}.mui-spinner-main .mui-spinner-left,.mui-spinner-main .mui-spinner-right{position:absolute;top:0;height:25px;width:13px;overflow:hidden}.mui-spinner-main .mui-spinner-left{left:0}.mui-spinner-main .mui-spinner-left .mui-half-circle{left:0;border-right-color:transparent}.mui-spinner-main .mui-spinner-right{right:0}.mui-spinner-main .mui-spinner-right .mui-half-circle{right:0;border-left-color:transparent}.mui-blue-theme .mui-arrow-main{border-color:#2196f3 #2196f3 transparent}.mui-blue-theme .mui-arrow-main:before{border-color:#2196f3 #fff #fff}.mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle,.mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle{border-top-color:#2196f3}.mui-blue-theme .mui-spinner-main .mui-spinner-left .mui-half-circle{border-left-color:#2196f3}.mui-blue-theme .mui-spinner-main .mui-spinner-right .mui-half-circle{border-right-color:#2196f3}.mui-spinner-wrapper{-webkit-animation:outer-rotate 2.91667s linear infinite;animation:outer-rotate 2.91667s linear infinite}.mui-spinner-main{-webkit-animation:sporadic-rotate 5.25s cubic-bezier(.35,0,.25,1) infinite;animation:sporadic-rotate 5.25s cubic-bezier(.35,0,.25,1) infinite}.mui-spinner-main .mui-spinner-left .mui-half-circle,.mui-spinner-main .mui-spinner-right .mui-half-circle{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-duration:1.3125s;animation-duration:1.3125s;-webkit-animation-timing-function:cubic-bezier(.35,0,.25,1);animation-timing-function:cubic-bezier(.35,0,.25,1)}.mui-spinner-main .mui-spinner-left .mui-half-circle{-webkit-animation-name:left-wobble;animation-name:left-wobble}.mui-spinner-main .mui-spinner-right .mui-half-circle{-webkit-animation-name:right-wobble;animation-name:right-wobble}@-moz-keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes outer-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@-webkit-keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@-o-keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@keyframes left-wobble{0%,100%{-webkit-transform:rotate(130deg);transform:rotate(130deg)}50%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}}@-moz-keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@-webkit-keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@-o-keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@keyframes right-wobble{0%,100%{-webkit-transform:rotate(-130deg);transform:rotate(-130deg)}50%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}}@-moz-keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-webkit-keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@-o-keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}}@keyframes sporadic-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg)}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg)}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg)}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg)}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg)}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg)}100%{-webkit-transform:rotate(1080deg);transform:rotate(1080deg)}} -------------------------------------------------------------------------------- /material-refresh.min.js: -------------------------------------------------------------------------------- 1 | (function(){"use strict";!function(e){function n(n){if(n=n||{},g=n.scrollEl?n.scrollEl:Q?g:document,y=e(g),U=n.onBegin,H=n.onEnd,M=n.maxTime||M,O=n.nav||O,0===e("#muirefresh").length&&o(),m=e("#muiRefresh"),p=e(".mui-spinner-wrapper",m),h=e(".mui-arrow-wrapper",m),v=e(".mui-arrow-main",m),!d()){m.addClass("mui-refresh-nav"),S=e(O).height()+20,e(O).offset()&&(A=e(O).offset().top,"fixed"!==e(O).css("position")&&(S+=A),m.css("top",A+"px"));var t=e(O).css("z-index");m.css("z-index",t-1)}n.index&&m.css("z-index",~~n.index),n.top&&m.css("top",n.top),m.addClass(n.theme?n.theme:T),m.addClass(b),n.freeze||f()}function o(){document.body.insertAdjacentHTML("beforeend",Z)}function t(e){F.top=Q&&g==document.body?window.scrollY:g!=document?document.querySelector(g).scrollTop:(document.documentElement||document.body.parentNode||document.body).scrollTop,F.top>0||E||(w=S+z,m.show(),e=e.originalEvent||e,e.touches[0]&&(F.x1=e.touches[0].pageX,j=F.y1=e.touches[0].pageY))}function i(e){var n,o,t=(new Date).getTime();if(e=e.originalEvent||e,!(F.top>0||E)&&e.touches&&1===e.touches.length){if(n=e.touches[0],F.x2=n.pageX,F.y2=n.pageY,o=F.y2-F.y1,F.y2-j+B>0){if(e.preventDefault(),90>t-P)return;if(!(S-A+Y>w))return void u();w+=o,r(w)}F.y1=n.pageY,P=t}}function s(e){F.top>0||E||(e.preventDefault(),w>S-A+R?u():c())}function c(){var e=S+z;d()?(m.css("top",e+"px"),m.css("-webkit-transform","scale(0)")):m.css("top",A+"px"),setTimeout(function(){E||(m.css("opacity",0),m.hide())},300)}function r(e){var n=40,o=e/n>1?1:0>e/n?0:e/n,t=S+z+e;d()&&m.css("-webkit-transform","scale("+o+")"),m.css("opacity",o),m.css("top",t+"px"),v.css("-webkit-transform","rotate("+-(3*e)+"deg)")}function u(){if(E=!0,k&&"function"==typeof q?q():"function"==typeof U&&U(),m.css("opacity",1),k)m.addClass(b),m.css("-webkit-transform","scale(1)");else{var e=S+D-20;d()||(e+=X),m.css("top",e+"px")}h.hide(),p.show(),N=setTimeout(a,M)}function a(){C=!0,m.addClass(x),p.hide(),setTimeout(function(){m.removeClass(x),m.hide(),c(),h.show(),E=!1,C=!1,k&&"function"==typeof L?L():"function"==typeof H&&H(),k=!1},500)}function d(){return 0===e(O).length}function f(){y.on("touchstart",t),y.on("touchmove",i),y.on("touchend",s)}function l(){y.off("touchstart",t),y.off("touchmove",i),y.off("touchend",s)}var m,p,h,v,w,y=e(document.body),g=document.body,x="mui-refresh-noshow",b="mui-refresh-main-animat",T="mui-blue-theme",E=!1,C=!1,k=!1,z=-85,D=0,Y=65,R=-25,X=20,j=0,A=0,B=2,M=6e3,S=60,U=null,q=null,H=null,L=null,N=null,O="",P=(new Date).getTime(),Q=!!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),Z='
',F={top:0,x1:0,x2:0,y1:0,y2:0};n.resolve=function(){!C&&N&&(clearTimeout(N),N=null,a())},n.destroy=function(){l(),m.remove()},n.refresh=function(e){if(!E){var n=S+D-20;E=!0,k=!0,e=e||{},q=e.onBegin,L=e.onEnd,d()||(n+=X),m.show(),m.removeClass(b),m.css("top",n+"px"),m.css("-webkit-transform","scale(0.01)"),setTimeout(u,60)}},n.unbindEvents=function(){l()},n.bindEvents=function(){f()},window.mRefresh=n}(window.Zepto||window.jQuery)}).call(this); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "material-refresh", 3 | "description": "Material Design pull to refresh, by using CSS3 and JavaScript", 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/lightningtgc/material-refresh", 6 | "author": "gctang ", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/lightningtgc/material-refresh.git" 10 | }, 11 | "devDependencies": { 12 | "gulp": "^3.8.10", 13 | "del": "^1.1.1", 14 | "gulp-uglify": "^1.1.0", 15 | "gulp-rename": "^1.2.0", 16 | "gulp-es6-module-transpiler": "^0.2.1", 17 | "gulp-stylus": "^2.0.0", 18 | "gulp-minify-css": "^0.4.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/css/material-refresh.styl: -------------------------------------------------------------------------------- 1 | blueSpinColor = #2196F3 2 | 3 | .mui-refresh-main 4 | position: absolute 5 | left:50% 6 | margin-left: -25px 7 | padding: 9px 8 | z-index: 10001 9 | 10 | background-color: #FFF 11 | overflow: hidden 12 | border-radius: 999px /* hack android 2.3*/ 13 | 14 | box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.19), 0 6px 13px 0 rgba(0, 0, 0, 0.24) 15 | -webkit-box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.19), 0 6px 13px 0 rgba(0, 0, 0, 0.24) 16 | 17 | .mui-refresh-main { 18 | position: fixed 19 | 20 | top: 0 21 | -webkit-perspective: 1000 22 | // some error in stylus 23 | -webkit-backface-visibility: hidden 24 | opacity: 0 25 | -webkit-transform: scale(0) 26 | } 27 | 28 | /*transition*/ 29 | .mui-refresh-main-animat { 30 | /* transition: box-shadow 0.38s cubic-bezier(0.4, 0, 0.2, 1) */ 31 | /* -webkit-transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1) */ 32 | /* transition: all 0.43s cubic-bezier(.08,.55,.81,1.8) */ 33 | -webkit-transition: all 0.43s cubic-bezier(.08,.55,.81,1.8) 34 | } 35 | 36 | /* below nav bar */ 37 | .mui-refresh-nav 38 | -webkit-transform: scale(1) 39 | 40 | .mui-refresh-noshow 41 | opacity: 0 42 | -webkit-transform: scale(0.01) !important 43 | -webkit-transition: all 0.25s ease-in-out !important 44 | 45 | .mui-refresh-wrapper 46 | width:25px 47 | height:25px 48 | 49 | .mui-arrow-wrapper 50 | -webkit-transition: all 0.20s ease 51 | 52 | .mui-half-circle, 53 | .mui-arrow-main 54 | position: absolute 55 | top: 0 56 | width: 25px 57 | height: 25px 58 | box-sizing: border-box 59 | border-width: 3px 60 | border-style: solid 61 | border-color: black black transparent 62 | border-radius: 999px /* hack android 2.3*/ 63 | 64 | .mui-arrow-main 65 | margin-top: 10px 66 | /* transform: rotate(-37deg) */ 67 | -webkit-transform: rotate(-37deg) 68 | 69 | /* arrow */ 70 | &:before 71 | content: '' 72 | display: block 73 | position: relative 74 | top: 14px 75 | left: 0px 76 | width: 0 77 | height: 0 78 | border-width: 6px 6px 79 | border-style: solid 80 | 81 | transform: rotate(-56deg) 82 | -webkit-transform: rotate(-56deg) 83 | 84 | 85 | .mui-spinner-main 86 | width: 25px 87 | height: 25px 88 | position: relative 89 | 90 | .mui-spinner-left, 91 | .mui-spinner-right 92 | position: absolute 93 | top: 0 94 | height: 25px 95 | width: 13px 96 | overflow: hidden 97 | 98 | .mui-spinner-left 99 | left: 0 100 | .mui-half-circle 101 | left: 0 102 | border-right-color: transparent 103 | 104 | .mui-spinner-right 105 | right: 0 106 | .mui-half-circle 107 | right: 0 108 | border-left-color: transparent 109 | 110 | /* Blue theme */ 111 | .mui-blue-theme 112 | .mui-arrow-main 113 | border-color: blueSpinColor blueSpinColor rgba(0, 0, 0, 0) 114 | &:before 115 | border-color: blueSpinColor #FFF #FFF #FFF 116 | 117 | .mui-spinner-main 118 | .mui-spinner-left .mui-half-circle, 119 | .mui-spinner-right .mui-half-circle 120 | border-top-color: blueSpinColor 121 | 122 | .mui-spinner-left .mui-half-circle 123 | border-left-color: blueSpinColor 124 | 125 | .mui-spinner-right .mui-half-circle 126 | border-right-color: blueSpinColor 127 | 128 | /* Animation */ 129 | .mui-spinner-wrapper 130 | -webkit-animation: outer-rotate 2.91667s linear infinite 131 | animation: outer-rotate 2.91667s linear infinite 132 | 133 | .mui-spinner-main 134 | -webkit-animation: sporadic-rotate 5.25s cubic-bezier(0.35, 0, 0.25, 1) infinite 135 | animation: sporadic-rotate 5.25s cubic-bezier(0.35, 0, 0.25, 1) infinite 136 | 137 | .mui-spinner-left .mui-half-circle, 138 | .mui-spinner-right .mui-half-circle 139 | -webkit-animation-iteration-count: infinite 140 | animation-iteration-count: infinite 141 | -webkit-animation-duration: 1.3125s 142 | animation-duration: 1.3125s 143 | -webkit-animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1) 144 | animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1) 145 | 146 | 147 | .mui-spinner-left .mui-half-circle 148 | -webkit-animation-name: left-wobble 149 | animation-name: left-wobble 150 | 151 | .mui-spinner-right .mui-half-circle 152 | -webkit-animation-name: right-wobble 153 | animation-name: right-wobble 154 | 155 | 156 | @-webkit-keyframes outer-rotate { 157 | 100% { 158 | -webkit-transform: rotate(360deg) 159 | transform: rotate(360deg) } 160 | } 161 | 162 | @keyframes outer-rotate { 163 | 100% { 164 | -webkit-transform: rotate(360deg) 165 | transform: rotate(360deg) } 166 | } 167 | 168 | @-webkit-keyframes left-wobble { 169 | 0%, 100% { 170 | -webkit-transform: rotate(130deg) 171 | transform: rotate(130deg) 172 | } 173 | 174 | 50% { 175 | -webkit-transform: rotate(-5deg) 176 | transform: rotate(-5deg) } 177 | } 178 | 179 | @keyframes left-wobble { 180 | 0%, 100% { 181 | -webkit-transform: rotate(130deg) 182 | transform: rotate(130deg) } 183 | 184 | 50% { 185 | -webkit-transform: rotate(-5deg) 186 | transform: rotate(-5deg) } 187 | } 188 | 189 | @-webkit-keyframes right-wobble { 190 | 0%, 100% { 191 | -webkit-transform: rotate(-130deg) 192 | transform: rotate(-130deg) } 193 | 194 | 50% { 195 | -webkit-transform: rotate(5deg) 196 | transform: rotate(5deg) } 197 | } 198 | 199 | @keyframes right-wobble { 200 | 0%, 100% { 201 | -webkit-transform: rotate(-130deg) 202 | transform: rotate(-130deg) } 203 | 204 | 50% { 205 | -webkit-transform: rotate(5deg) 206 | transform: rotate(5deg) } 207 | } 208 | @-webkit-keyframes sporadic-rotate { 209 | 12.5% { 210 | -webkit-transform: rotate(135deg) 211 | transform: rotate(135deg) } 212 | 213 | 25% { 214 | -webkit-transform: rotate(270deg) 215 | transform: rotate(270deg) } 216 | 217 | 37.5% { 218 | -webkit-transform: rotate(405deg) 219 | transform: rotate(405deg) } 220 | 221 | 50% { 222 | -webkit-transform: rotate(540deg) 223 | transform: rotate(540deg) } 224 | 225 | 62.5% { 226 | -webkit-transform: rotate(675deg) 227 | transform: rotate(675deg) } 228 | 229 | 75% { 230 | -webkit-transform: rotate(810deg) 231 | transform: rotate(810deg) } 232 | 233 | 87.5% { 234 | -webkit-transform: rotate(945deg) 235 | transform: rotate(945deg) } 236 | 237 | 100% { 238 | -webkit-transform: rotate(1080deg) 239 | transform: rotate(1080deg) } 240 | } 241 | 242 | @keyframes sporadic-rotate { 243 | 12.5% { 244 | -webkit-transform: rotate(135deg) 245 | transform: rotate(135deg) } 246 | 247 | 25% { 248 | -webkit-transform: rotate(270deg) 249 | transform: rotate(270deg) } 250 | 251 | 37.5% { 252 | -webkit-transform: rotate(405deg) 253 | transform: rotate(405deg) } 254 | 255 | 50% { 256 | -webkit-transform: rotate(540deg) 257 | transform: rotate(540deg) } 258 | 259 | 62.5% { 260 | -webkit-transform: rotate(675deg) 261 | transform: rotate(675deg) } 262 | 263 | 75% { 264 | -webkit-transform: rotate(810deg) 265 | transform: rotate(810deg) } 266 | 267 | 87.5% { 268 | -webkit-transform: rotate(945deg) 269 | transform: rotate(945deg) } 270 | 271 | 100% { 272 | -webkit-transform: rotate(1080deg) 273 | transform: rotate(1080deg) } 274 | } 275 | 276 | 277 | -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Google Material Design Swipe To Refresh. 3 | * By Gctang(https://github.com/lightningtgc) 4 | * 5 | * Three types of refresh: 6 | * 1. Above or coplanar with another surface 7 | * 2. Below another surface in z-space. 8 | * 3. Button action refresh 9 | * 10 | */ 11 | 12 | ;(function($){ 13 | var $scrollEl = $(document.body); 14 | var $refreshMain, $spinnerWrapper, $arrowWrapper, $arrowMain; 15 | var scrollEl = document.body; 16 | 17 | var noShowClass = 'mui-refresh-noshow'; 18 | var mainAnimatClass = 'mui-refresh-main-animat'; 19 | var blueThemeClass = 'mui-blue-theme'; 20 | 21 | var isShowLoading = false; 22 | var isStoping = false; 23 | var isBtnAction = false; 24 | 25 | var NUM_POS_START_Y = -85; 26 | var NUM_POS_TARGET_Y = 0; // Where to stop 27 | var NUM_POS_MAX_Y = 65; // Max position for the moving distance 28 | var NUM_POS_MIN_Y = -25; // Min position for the moving distance 29 | var NUM_NAV_TARGET_ADDY = 20; // For custom nav bar 30 | 31 | var touchCurrentY; 32 | var touchStartY = 0; 33 | var customNavTop = 0; 34 | var verticalThreshold = 2; 35 | var maxRotateTime = 6000; //Max time to stop rotate 36 | var basePosY = 60; 37 | 38 | var onBegin = null; 39 | var onBtnBegin= null; 40 | var onEnd = null; 41 | var onBtnEnd = null; 42 | var stopAnimatTimeout = null; 43 | 44 | var refreshNav = ''; 45 | 46 | var lastTime = new Date().getTime(); 47 | 48 | var isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); 49 | 50 | var tmpl = '
\ 51 |
\ 52 |
\ 53 |
\ 54 |
\ 55 | \ 65 |
\ 66 |
'; 67 | 68 | // Defined the object to improve performance 69 | var touchPos = { 70 | top: 0, 71 | x1: 0, 72 | x2: 0, 73 | y1: 0, 74 | y2: 0 75 | } 76 | 77 | // Default options 78 | /* var opts = { */ 79 | /* scrollEl: '', //String */ 80 | /* nav: '', //String */ 81 | /* top: '0px', //String */ 82 | /* theme: '', //String */ 83 | /* index: 10001, //Number*/ 84 | /* maxTime: 3000, //Number */ 85 | /* freeze: false, //Boolen */ 86 | /* onBegin: null, //Function */ 87 | /* onEnd: null //Function */ 88 | /* } */ 89 | 90 | 91 | /* Known issue: 92 | * 1. iOS feature when scrolling ,animation will stop 93 | * 2. Animation display issue in anfroid like miui小米 94 | * 95 | * 96 | * TODO list: 97 | * 1. Using translate and scale together to replace top 98 | * 2. Optimize circle rotate animation 99 | */ 100 | 101 | // Main function to init the refresh style 102 | function mRefresh(options) { 103 | options = options || {}; 104 | 105 | scrollEl = options.scrollEl ? options.scrollEl : 106 | isIOS ? scrollEl : document; 107 | $scrollEl = $(scrollEl); 108 | 109 | // extend options 110 | onBegin = options.onBegin; 111 | onEnd = options.onEnd; 112 | maxRotateTime = options.maxTime || maxRotateTime; 113 | refreshNav = options.nav || refreshNav; 114 | 115 | if ($('#muirefresh').length === 0) { 116 | renderTmpl(); 117 | } 118 | 119 | $refreshMain = $('#muiRefresh'); 120 | $spinnerWrapper = $('.mui-spinner-wrapper', $refreshMain); 121 | $arrowWrapper = $('.mui-arrow-wrapper', $refreshMain); 122 | $arrowMain = $('.mui-arrow-main', $refreshMain); 123 | 124 | // Custom nav bar 125 | if (!isDefaultType()) { 126 | $refreshMain.addClass('mui-refresh-nav'); 127 | basePosY = $(refreshNav).height() + 20; 128 | if($(refreshNav).offset()){ 129 | customNavTop = $(refreshNav).offset().top; 130 | // Handle position fix 131 | if($(refreshNav).css('position') !== 'fixed'){ 132 | basePosY += customNavTop; 133 | } 134 | // Set the first Y position 135 | $refreshMain.css('top', customNavTop + 'px'); 136 | } 137 | 138 | //Set z-index to make sure ablow the nav bar 139 | var navIndex = $(refreshNav).css('z-index'); 140 | $refreshMain.css('z-index', navIndex - 1); 141 | } 142 | 143 | //Set custom z-index 144 | if(options.index){ 145 | $refreshMain.css('z-index', ~~options.index); 146 | } 147 | 148 | //Set custom top, to change the position 149 | if(options.top){ 150 | $refreshMain.css('top', options.top); 151 | } 152 | 153 | // Extract theme 154 | if (options.theme) { 155 | $refreshMain.addClass(options.theme); 156 | } else { 157 | $refreshMain.addClass(blueThemeClass); 158 | } 159 | 160 | // Add Animation Class 161 | $refreshMain.addClass(mainAnimatClass); 162 | 163 | if(!options.freeze){ 164 | bindEvents(); 165 | } 166 | } 167 | 168 | // Public Methods 169 | 170 | // Finish loading 171 | mRefresh.resolve = function() { 172 | if(!isStoping && stopAnimatTimeout){ 173 | clearTimeout(stopAnimatTimeout); 174 | stopAnimatTimeout = null; 175 | 176 | recoverRefresh(); 177 | } 178 | } 179 | 180 | // Destory refresh 181 | mRefresh.destroy = function(){ 182 | unbindEvents(); 183 | $refreshMain.remove(); 184 | 185 | } 186 | 187 | // Type3: Button action refresh 188 | mRefresh.refresh = function(opt) { 189 | // Do rotate 190 | if(!isShowLoading){ 191 | var realTargetPos = basePosY + NUM_POS_TARGET_Y - 20; 192 | isShowLoading = true; 193 | isBtnAction = true; 194 | 195 | opt = opt || {}; 196 | onBtnBegin = opt.onBegin; 197 | onBtnEnd = opt.onEnd; 198 | 199 | if (!isDefaultType()) { 200 | realTargetPos = realTargetPos + NUM_NAV_TARGET_ADDY; 201 | } 202 | 203 | // Handle freeze 204 | $refreshMain.show(); 205 | //Romove animat time 206 | $refreshMain.removeClass(mainAnimatClass); 207 | // move to target position 208 | $refreshMain.css('top', realTargetPos + 'px'); 209 | // make it small 210 | $refreshMain.css('-webkit-transform', 'scale(' + 0.01 + ')'); 211 | 212 | setTimeout(doRotate, 60); 213 | } 214 | } 215 | 216 | // Unbind touch events,for freeze type1 and type2 217 | mRefresh.unbindEvents = function(){ 218 | unbindEvents(); 219 | } 220 | 221 | mRefresh.bindEvents = function(){ 222 | bindEvents(); 223 | } 224 | 225 | // Render html template 226 | function renderTmpl(){ 227 | document.body.insertAdjacentHTML('beforeend', tmpl); 228 | } 229 | 230 | 231 | function touchStart(e){ 232 | if(isIOS && scrollEl == document.body){ 233 | touchPos.top = window.scrollY; 234 | }else if(scrollEl != document){ 235 | touchPos.top = document.querySelector(scrollEl).scrollTop; 236 | } else { 237 | touchPos.top = (document.documentElement || document.body.parentNode || document.body).scrollTop; 238 | } 239 | 240 | if (touchPos.top > 0 || isShowLoading) { 241 | return; 242 | } 243 | 244 | touchCurrentY = basePosY + NUM_POS_START_Y; 245 | $refreshMain.show(); 246 | 247 | // Fix jQuery touch event detect 248 | e = e.originalEvent || e; 249 | 250 | if (e.touches[0]) { 251 | touchPos.x1 = e.touches[0].pageX; 252 | touchStartY = touchPos.y1 = e.touches[0].pageY; 253 | } 254 | } 255 | 256 | function touchMove(e){ 257 | var thisTouch, distanceY; 258 | var now = new Date().getTime(); 259 | 260 | e = e.originalEvent || e; 261 | 262 | if (touchPos.top > 0 || isShowLoading || !e.touches || e.touches.length !== 1) { 263 | // Just allow one finger 264 | return; 265 | } 266 | 267 | thisTouch = e.touches[0]; 268 | 269 | touchPos.x2 = thisTouch.pageX; 270 | touchPos.y2 = thisTouch.pageY; 271 | 272 | // Distance for pageY change 273 | distanceY = touchPos.y2 - touchPos.y1; 274 | 275 | if (touchPos.y2 - touchStartY + verticalThreshold > 0) { 276 | e.preventDefault(); 277 | 278 | // Some android phone 279 | // Throttle, aviod jitter 280 | if (now - lastTime < 90) { 281 | return; 282 | } 283 | 284 | if (touchCurrentY < basePosY - customNavTop + NUM_POS_MAX_Y) { 285 | touchCurrentY += distanceY ; 286 | moveCircle(touchCurrentY); 287 | } else { 288 | // Move over the max position will do the rotate 289 | doRotate(); 290 | return; 291 | } 292 | 293 | } 294 | 295 | // y1 always is the current pageY 296 | touchPos.y1 = thisTouch.pageY; 297 | lastTime = now; 298 | } 299 | 300 | function touchEnd(e){ 301 | if (touchPos.top > 0 || isShowLoading) { 302 | return; 303 | } 304 | e.preventDefault(); 305 | 306 | if (touchCurrentY > basePosY - customNavTop + NUM_POS_MIN_Y) { 307 | // Should move over the min position 308 | doRotate(); 309 | } else { 310 | backToStart(); 311 | } 312 | } 313 | 314 | /** 315 | * backToStart 316 | * Return to start position 317 | */ 318 | function backToStart() { 319 | var realStartPos = basePosY + NUM_POS_START_Y; 320 | if ( isDefaultType() ) { 321 | $refreshMain.css('top', realStartPos + 'px'); 322 | $refreshMain.css('-webkit-transform', 'scale(' + 0 + ')'); 323 | } else { 324 | // Distance must greater than NUM_POS_MIN_Y 325 | $refreshMain.css('top', customNavTop + 'px'); 326 | /* $refreshMain.css('-webkit-transform', 'translateY(' + realStartPos + 'px)'); */ 327 | } 328 | setTimeout(function(){ 329 | // Handle button action 330 | if(!isShowLoading){ 331 | $refreshMain.css('opacity', 0); 332 | $refreshMain.hide(); 333 | } 334 | }, 300); 335 | } 336 | 337 | /** 338 | * moveCircle 339 | * touchmove change the circle style 340 | * 341 | * @param {number} y 342 | */ 343 | function moveCircle(y){ 344 | var scaleRate = 40; 345 | var scalePer = y / scaleRate > 1 ? 1 : y / scaleRate < 0 ? 0 : y / scaleRate; 346 | var currMoveY = basePosY + NUM_POS_START_Y + y; 347 | 348 | if (isDefaultType()) { 349 | // Small to Big 350 | $refreshMain.css('-webkit-transform', 'scale(' + scalePer + ')'); 351 | } 352 | /* $refreshMain.css('-webkit-transform', 'translateY('+ y + 'px)'); */ 353 | 354 | $refreshMain.css('opacity', scalePer); 355 | // Change the position 356 | $refreshMain.css('top', currMoveY + 'px'); 357 | $arrowMain.css('-webkit-transform', 'rotate(' + -(y * 3) + 'deg)'); 358 | /* $arrowMain.css('transform', 'rotate(' + -(y * 3) + 'deg)'); */ 359 | 360 | } 361 | 362 | 363 | /** 364 | * doRotate 365 | * Rotate the circle,and you can stop it by `mRefresh.resolve()` 366 | * or it wil stop within the time: `maxRotateTime` 367 | */ 368 | function doRotate(){ 369 | isShowLoading = true; 370 | // Do button action callback 371 | if (isBtnAction && typeof onBtnBegin === 'function') { 372 | onBtnBegin(); 373 | } else if (typeof onBegin === 'function') { 374 | // Do onBegin callback 375 | onBegin(); 376 | } 377 | 378 | // Make sure display entirely 379 | $refreshMain.css('opacity', 1); 380 | 381 | if (!isBtnAction) { 382 | var realTargetPos = basePosY + NUM_POS_TARGET_Y - 20; 383 | if (!isDefaultType()) { 384 | realTargetPos = realTargetPos + NUM_NAV_TARGET_ADDY; 385 | } 386 | $refreshMain.css('top', realTargetPos + 'px'); 387 | /* $refreshMain.css('-webkit-transform', 'translateY(' + realTargetPos + 'px)'); */ 388 | } else { 389 | $refreshMain.addClass(mainAnimatClass); 390 | $refreshMain.css('-webkit-transform', 'scale(' + 1 + ')'); 391 | } 392 | 393 | $arrowWrapper.hide(); 394 | 395 | // Start animation 396 | $spinnerWrapper.show(); 397 | 398 | // Timeout to stop animation 399 | stopAnimatTimeout = setTimeout(recoverRefresh, maxRotateTime); 400 | } 401 | 402 | /** 403 | * Recover Refresh 404 | * Hide the circle 405 | */ 406 | function recoverRefresh(){ 407 | // For aviod resolve 408 | isStoping = true; 409 | 410 | // Stop animation 411 | $refreshMain.addClass(noShowClass); 412 | 413 | $spinnerWrapper.hide(); 414 | 415 | setTimeout(function(){ 416 | $refreshMain.removeClass(noShowClass); 417 | $refreshMain.hide(); 418 | 419 | backToStart(); 420 | 421 | $arrowWrapper.show(); 422 | 423 | isShowLoading = false; 424 | isStoping = false; 425 | 426 | if (isBtnAction && typeof onBtnEnd === 'function') { 427 | onBtnEnd(); 428 | } else if (typeof onEnd === 'function') { 429 | onEnd(); 430 | } 431 | 432 | isBtnAction = false; 433 | 434 | }, 500); 435 | } 436 | 437 | /** 438 | * isDefaultType 439 | * Check is type1: Above surface 440 | * 441 | * @return {Boolen} 442 | */ 443 | function isDefaultType() { 444 | return $(refreshNav).length === 0; 445 | } 446 | 447 | function bindEvents() { 448 | $scrollEl.on('touchstart', touchStart); 449 | $scrollEl.on('touchmove', touchMove); 450 | $scrollEl.on('touchend', touchEnd); 451 | } 452 | 453 | function unbindEvents() { 454 | $scrollEl.off('touchstart', touchStart); 455 | $scrollEl.off('touchmove', touchMove); 456 | $scrollEl.off('touchend', touchEnd); 457 | } 458 | 459 | 460 | window.mRefresh = mRefresh; 461 | 462 | })(window.Zepto || window.jQuery); 463 | 464 | --------------------------------------------------------------------------------