├── img ├── bg.jpg ├── cd.png ├── pre.png ├── bg_top.jpg ├── lrc_bg.png ├── next.png ├── pause.png ├── play.png ├── poster.jpg ├── bg_list.jpg ├── control.png ├── favicon.ico ├── bg_bottom.jpg ├── icon_love.png ├── icon_love_active.png └── poster │ ├── 0-origin.jpg │ ├── 1-origin.jpg │ ├── 10-origin.jpg │ ├── 11-origin.jpg │ ├── 12-origin.jpg │ ├── 13-origin.jpg │ ├── 14-origin.jpg │ ├── 15-origin.jpg │ ├── 16-origin.jpg │ ├── 2-origin.jpg │ ├── 3-origin.jpg │ ├── 4-origin.jpg │ ├── 5-origin.jpg │ ├── 6-origin.jpg │ ├── 7-origin.jpg │ ├── 8-origin.jpg │ ├── 9-origin.jpg │ ├── 0-thumbnail.jpg │ ├── 1-thumbnail.jpg │ ├── 12-origin .jpg │ ├── 12-origin-.jpg │ ├── 2-thumbnail.jpg │ ├── 3-thumbnail.jpg │ ├── 4-thumbnail.jpg │ ├── 5-thumbnail.jpg │ ├── 6-thumbnail.jpg │ ├── 7-thumbnail.jpg │ ├── 8-thumbnail.jpg │ ├── 9-thumbnail.jpg │ ├── 10-thumbnail.jpg │ ├── 11-thumbnail.jpg │ ├── 12-thumbnail.jpg │ ├── 13-thumbnail.jpg │ ├── 14-thumbnail.jpg │ ├── 15-thumbnail.jpg │ └── 16-thumbnail.jpg ├── fonts ├── icomoon.eot ├── icomoon.ttf ├── icomoon.woff └── icomoon.svg ├── LICENSE ├── css ├── common.css ├── normalize.css └── index.css ├── index.html ├── README.md ├── lib ├── jquery.touchSwipe.min.js └── iscroll.js ├── data └── data.json └── js └── main.js /img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/bg.jpg -------------------------------------------------------------------------------- /img/cd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/cd.png -------------------------------------------------------------------------------- /img/pre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/pre.png -------------------------------------------------------------------------------- /img/bg_top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/bg_top.jpg -------------------------------------------------------------------------------- /img/lrc_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/lrc_bg.png -------------------------------------------------------------------------------- /img/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/next.png -------------------------------------------------------------------------------- /img/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/pause.png -------------------------------------------------------------------------------- /img/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/play.png -------------------------------------------------------------------------------- /img/poster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster.jpg -------------------------------------------------------------------------------- /img/bg_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/bg_list.jpg -------------------------------------------------------------------------------- /img/control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/control.png -------------------------------------------------------------------------------- /img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/favicon.ico -------------------------------------------------------------------------------- /fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/fonts/icomoon.eot -------------------------------------------------------------------------------- /fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/fonts/icomoon.ttf -------------------------------------------------------------------------------- /fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/fonts/icomoon.woff -------------------------------------------------------------------------------- /img/bg_bottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/bg_bottom.jpg -------------------------------------------------------------------------------- /img/icon_love.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/icon_love.png -------------------------------------------------------------------------------- /img/icon_love_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/icon_love_active.png -------------------------------------------------------------------------------- /img/poster/0-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/0-origin.jpg -------------------------------------------------------------------------------- /img/poster/1-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/1-origin.jpg -------------------------------------------------------------------------------- /img/poster/10-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/10-origin.jpg -------------------------------------------------------------------------------- /img/poster/11-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/11-origin.jpg -------------------------------------------------------------------------------- /img/poster/12-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/12-origin.jpg -------------------------------------------------------------------------------- /img/poster/13-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/13-origin.jpg -------------------------------------------------------------------------------- /img/poster/14-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/14-origin.jpg -------------------------------------------------------------------------------- /img/poster/15-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/15-origin.jpg -------------------------------------------------------------------------------- /img/poster/16-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/16-origin.jpg -------------------------------------------------------------------------------- /img/poster/2-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/2-origin.jpg -------------------------------------------------------------------------------- /img/poster/3-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/3-origin.jpg -------------------------------------------------------------------------------- /img/poster/4-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/4-origin.jpg -------------------------------------------------------------------------------- /img/poster/5-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/5-origin.jpg -------------------------------------------------------------------------------- /img/poster/6-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/6-origin.jpg -------------------------------------------------------------------------------- /img/poster/7-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/7-origin.jpg -------------------------------------------------------------------------------- /img/poster/8-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/8-origin.jpg -------------------------------------------------------------------------------- /img/poster/9-origin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/9-origin.jpg -------------------------------------------------------------------------------- /img/poster/0-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/0-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/1-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/1-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/12-origin .jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/12-origin .jpg -------------------------------------------------------------------------------- /img/poster/12-origin-.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/12-origin-.jpg -------------------------------------------------------------------------------- /img/poster/2-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/2-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/3-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/3-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/4-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/4-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/5-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/5-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/6-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/6-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/7-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/7-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/8-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/8-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/9-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/9-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/10-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/10-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/11-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/11-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/12-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/12-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/13-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/13-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/14-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/14-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/15-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/15-thumbnail.jpg -------------------------------------------------------------------------------- /img/poster/16-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex1504/jquery-mobile-player/HEAD/img/poster/16-thumbnail.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 alex1504 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /css/common.css: -------------------------------------------------------------------------------- 1 | /* 字体库样式 */ 2 | @font-face { 3 | font-family: 'icomoon'; 4 | src: url('../fonts/icomoon.eot?m26i4l'); 5 | src: url('../fonts/icomoon.eot?m26i4l#iefix') format('embedded-opentype'), 6 | url('../fonts/icomoon.ttf?m26i4l') format('truetype'), 7 | url('../fonts/icomoon.woff?m26i4l') format('woff'), 8 | url('../fonts/icomoon.svg?m26i4l#icomoon') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | [class^="icon-"], [class*=" icon-"] { 14 | /* use !important to prevent issues with browser extensions that change fonts */ 15 | font-family: 'icomoon' !important; 16 | speak: none; 17 | font-style: normal; 18 | font-weight: normal; 19 | font-variant: normal; 20 | text-transform: none; 21 | line-height: 1; 22 | 23 | /* Better Font Rendering =========== */ 24 | -webkit-font-smoothing: antialiased; 25 | -moz-osx-font-smoothing: grayscale; 26 | } 27 | 28 | .icon-user:before { 29 | content: "\e971"; 30 | } 31 | .icon-search:before { 32 | content: "\e986"; 33 | } 34 | .icon-cog:before { 35 | content: "\e994"; 36 | } 37 | .icon-menu:before { 38 | content: "\e9bd"; 39 | } 40 | .icon-cross:before { 41 | content: "\ea0f"; 42 | } 43 | .icon-volume-high:before { 44 | content: "\ea26"; 45 | } 46 | .icon-volume-mute:before { 47 | content: "\ea29"; 48 | } 49 | .icon-loop:before { 50 | content: "\ea2d"; 51 | } 52 | .icon-random:before { 53 | content: "\ea30"; 54 | } 55 | .icon-arrow-left:before { 56 | content: "\ea40"; 57 | } 58 | 59 | 60 | /* END字体库样式 */ 61 | 62 | 63 | /* 公共样式 */ 64 | html, body{ 65 | height: 100%; 66 | background: url(../img/bg.jpg) no-repeat center center; 67 | background-size: cover; 68 | overflow-x: hidden; 69 | } 70 | .page{ 71 | height: 100%; 72 | background: url(../img/bg.jpg) no-repeat center center; 73 | background-size: cover; 74 | color:#fff; 75 | font-size: 0.3rem; 76 | font-family: "Microsoft YaHei","微软雅黑","sans-serif"; 77 | } 78 | .page i{ 79 | text-align: center; 80 | } 81 | .btm-line{ 82 | border-bottom: 1px solid rgba(255, 255, 255, 0.5); 83 | } 84 | .show{ 85 | display: block !important; 86 | } 87 | .hide{ 88 | opacity:0 !important; 89 | } 90 | .disno{ 91 | display: none !important; 92 | } 93 | /* 公共样式 */ 94 | -------------------------------------------------------------------------------- /fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |此项目纯属个人爱好,用于研究H5音频相关API和JS模板渲染及动态加载,其中涉及handlebar模板渲染、下拉动态渲染、H5本地存储、移动端rem自适应布局等,其中仍有很多不完善之处,欢迎交流和指正。
218 |标签的高度将为0,这会影响歌词向上移动距离的不统一。因此下面作出个判断如果为空,则替换为“--------------”。(为空的时候大多数是歌曲中间停顿或过渡的时候) 248 | ```javascript 249 | function renderLyric(songinfo) { 250 | var arrMap = Util.createArrMap(songinfo.lyric); 251 | var tpl = ""; 252 | $.each(arrMap.lyricArr, function(index, lyric) { 253 | var lyricContent = lyric === "" ? "--------------" : lyric; 254 | tpl += "
" + lyricContent + "
"; 255 | }); 256 | Dom.lrcwrap.html(tpl); 257 | } 258 | ``` 259 | 260 | ### 歌词同步 ### 261 | 歌词同步我写在了syncLyric方法中,监听audio元素的timeupdate事件调用。 262 | 这个方法接收两个参数,第一个是当前播放歌曲时间(秒),第二个是转化为秒数的时间点数组。 263 | 如果当前时间>=时间点,那么高亮当前歌词(以lrcHighIndex)存储,并且lrcHighIndex自增1。 264 | 当歌词高亮索引lrcHighIndex>=1即歌词高亮不为第一句时,计算索引并让歌词盒子向上移动。 265 | ```javascript 266 | function syncLyric(curS, formatTimeArr) { 267 | if (Math.floor(curS) >= formatTimeArr[lrcHighIndex]) { 268 | Dom.lrc.eq(lrcHighIndex).addClass('current').siblings().removeClass('current'); 269 | if (lrcHighIndex >= 1) { 270 | lrcMoveIndex = lrcHighIndex - 2; 271 | moveDis += Util.getMoveDis(lrcMoveIndex); 272 | Dom.lrcwrap.animate({ 273 | "top": "-" + moveDis + "px" 274 | }, 100); 275 | lrcMoveIndex++; 276 | } 277 | lrcHighIndex++; 278 | } 279 | } 280 | ``` 281 | 282 | # 后记(于2017-11-9) 283 | 时隔两年,回顾当年所走的路,不仅感概前端的变化远快于自己的成长速度,不过庆幸的是自己并没有选错这条路,因为我发现开发不仅已经成为了自己的工作,更是融入了自己的生活,成为生活的一部分;其次,这是开发者最好的时代,我不仅能从互联网挖掘我所有想要深入的知识,也感受者开源社区带给我的魅力,希望自己能从知识的接收者变成知识的传播者,与您共勉。 284 | -------------------------------------------------------------------------------- /css/index.css: -------------------------------------------------------------------------------- 1 | /* 加载页 */ 2 | .m-loader { 3 | position: fixed; 4 | z-index: 9999; 5 | width: 100%; 6 | height: 100%; 7 | top: 0; 8 | } 9 | .loader_overlay { 10 | width: 150px; 11 | height: 150px; 12 | background: transparent; 13 | box-shadow: 0px 0px 0px 1000px rgba(255, 255, 255, 0.67), 0px 0px 19px 0px rgba(0, 0, 0, 0.16) inset; 14 | border-radius: 100%; 15 | z-index: -1; 16 | position: absolute; 17 | left: 0; 18 | right: 0; 19 | top: 0; 20 | bottom: 0; 21 | margin: auto; 22 | } 23 | .loader_cogs { 24 | z-index: -2; 25 | width: 100px; 26 | height: 100px; 27 | top: -120px !important; 28 | position: absolute; 29 | left: 0; 30 | right: 0; 31 | top: 0; 32 | bottom: 0; 33 | margin: auto; 34 | } 35 | .loader_cogs__top { 36 | position: relative; 37 | width: 100px; 38 | height: 100px; 39 | -webkit-transform-origin: 50px 50px; 40 | transform-origin: 50px 50px; 41 | -webkit-animation: rotate 10s infinite linear; 42 | animation: rotate 10s infinite linear; 43 | } 44 | .loader_cogs__top div:nth-of-type(1) { 45 | -webkit-transform: rotate(30deg); 46 | transform: rotate(30deg); 47 | } 48 | .loader_cogs__top div:nth-of-type(2) { 49 | -webkit-transform: rotate(60deg); 50 | transform: rotate(60deg); 51 | } 52 | .loader_cogs__top div:nth-of-type(3) { 53 | -webkit-transform: rotate(90deg); 54 | transform: rotate(90deg); 55 | } 56 | .loader_cogs__top div.top_part { 57 | width: 100px; 58 | border-radius: 10px; 59 | position: absolute; 60 | height: 100px; 61 | background: #f98db9; 62 | } 63 | .loader_cogs__top div.top_hole { 64 | width: 50px; 65 | height: 50px; 66 | border-radius: 100%; 67 | background: white; 68 | position: absolute; 69 | position: absolute; 70 | left: 0; 71 | right: 0; 72 | top: 0; 73 | bottom: 0; 74 | margin: auto; 75 | } 76 | .loader_cogs__left { 77 | position: relative; 78 | width: 80px; 79 | -webkit-transform: rotate(16deg); 80 | transform: rotate(16deg); 81 | top: 28px; 82 | -webkit-transform-origin: 40px 40px; 83 | transform-origin: 40px 40px; 84 | -webkit-animation: rotate_left 10s .1s infinite reverse linear; 85 | animation: rotate_left 10s .1s infinite reverse linear; 86 | left: -24px; 87 | height: 80px; 88 | } 89 | .loader_cogs__left div:nth-of-type(1) { 90 | -webkit-transform: rotate(30deg); 91 | transform: rotate(30deg); 92 | } 93 | .loader_cogs__left div:nth-of-type(2) { 94 | -webkit-transform: rotate(60deg); 95 | transform: rotate(60deg); 96 | } 97 | .loader_cogs__left div:nth-of-type(3) { 98 | -webkit-transform: rotate(90deg); 99 | transform: rotate(90deg); 100 | } 101 | .loader_cogs__left div.left_part { 102 | width: 80px; 103 | border-radius: 6px; 104 | position: absolute; 105 | height: 80px; 106 | background: #97ddff; 107 | } 108 | .loader_cogs__left div.left_hole { 109 | width: 40px; 110 | height: 40px; 111 | border-radius: 100%; 112 | background: white; 113 | position: absolute; 114 | position: absolute; 115 | left: 0; 116 | right: 0; 117 | top: 0; 118 | bottom: 0; 119 | margin: auto; 120 | } 121 | .loader_cogs__bottom { 122 | position: relative; 123 | width: 60px; 124 | top: -65px; 125 | -webkit-transform-origin: 30px 30px; 126 | transform-origin: 30px 30px; 127 | -webkit-animation: rotate_left 10.2s .4s infinite linear; 128 | animation: rotate_left 10.2s .4s infinite linear; 129 | -webkit-transform: rotate(4deg); 130 | transform: rotate(4deg); 131 | left: 79px; 132 | height: 60px; 133 | } 134 | .loader_cogs__bottom div:nth-of-type(1) { 135 | -webkit-transform: rotate(30deg); 136 | transform: rotate(30deg); 137 | } 138 | .loader_cogs__bottom div:nth-of-type(2) { 139 | -webkit-transform: rotate(60deg); 140 | transform: rotate(60deg); 141 | } 142 | .loader_cogs__bottom div:nth-of-type(3) { 143 | -webkit-transform: rotate(90deg); 144 | transform: rotate(90deg); 145 | } 146 | .loader_cogs__bottom div.bottom_part { 147 | width: 60px; 148 | border-radius: 5px; 149 | position: absolute; 150 | height: 60px; 151 | background: #ffcd66; 152 | } 153 | .loader_cogs__bottom div.bottom_hole { 154 | width: 30px; 155 | height: 30px; 156 | border-radius: 100%; 157 | background: white; 158 | position: absolute; 159 | position: absolute; 160 | left: 0; 161 | right: 0; 162 | top: 0; 163 | bottom: 0; 164 | margin: auto; 165 | } 166 | 167 | /* Animations */ 168 | @-webkit-keyframes rotate { 169 | from { 170 | -webkit-transform: rotate(0deg); 171 | transform: rotate(0deg); 172 | } 173 | to { 174 | -webkit-transform: rotate(360deg); 175 | transform: rotate(360deg); 176 | } 177 | } 178 | @keyframes rotate { 179 | from { 180 | -webkit-transform: rotate(0deg); 181 | transform: rotate(0deg); 182 | } 183 | to { 184 | -webkit-transform: rotate(360deg); 185 | transform: rotate(360deg); 186 | } 187 | } 188 | @-webkit-keyframes rotate_left { 189 | from { 190 | -webkit-transform: rotate(16deg); 191 | transform: rotate(16deg); 192 | } 193 | to { 194 | -webkit-transform: rotate(376deg); 195 | transform: rotate(376deg); 196 | } 197 | } 198 | @keyframes rotate_left { 199 | from { 200 | -webkit-transform: rotate(16deg); 201 | transform: rotate(16deg); 202 | } 203 | to { 204 | -webkit-transform: rotate(376deg); 205 | transform: rotate(376deg); 206 | } 207 | } 208 | @-webkit-keyframes rotate_right { 209 | from { 210 | -webkit-transform: rotate(4deg); 211 | transform: rotate(4deg); 212 | } 213 | to { 214 | -webkit-transform: rotate(364deg); 215 | transform: rotate(364deg); 216 | } 217 | } 218 | @keyframes rotate_right { 219 | from { 220 | -webkit-transform: rotate(4deg); 221 | transform: rotate(4deg); 222 | } 223 | to { 224 | -webkit-transform: rotate(364deg); 225 | transform: rotate(364deg); 226 | } 227 | } 228 | 229 | 230 | /* 主页 */ 231 | .index{ 232 | 233 | } 234 | .index .g-header{ 235 | position: fixed; 236 | width: 100%; 237 | z-index: 9999; 238 | line-height: 0.86rem; 239 | text-align: center; 240 | justify-contents: center; 241 | box-sizing: border-box; 242 | } 243 | .index .m-header{ 244 | height: 100%; 245 | display: flex; 246 | 247 | } 248 | .index .m-header .cog, .index .m-header .user{ 249 | width: 0.96rem; 250 | height: 100%; 251 | font-size: 0.5rem; 252 | } 253 | .index .m-header .cog{ 254 | 255 | } 256 | .index .m-header .title{ 257 | margin: 0 auto; 258 | width: 4.68rem; 259 | height: 100%; 260 | } 261 | 262 | .index .g-search{ 263 | height: 0.94rem; 264 | overflow: hidden; 265 | font-size: 0.24rem; 266 | } 267 | .index .m-search{ 268 | width: 98%; 269 | height: 0.56rem; 270 | line-height: 0.56rem; 271 | margin: 0.16rem auto; 272 | position: relative; 273 | 274 | 275 | } 276 | .index .m-search input{ 277 | padding-left: 0.28rem; 278 | box-sizing: border-box; 279 | width: 100%; 280 | height: 100%; 281 | background-color: rgba(12, 18, 19, 0.39); 282 | border:none; 283 | color:#fff; 284 | } 285 | .index .m-search .icon-search{ 286 | position:absolute; 287 | right: 0.15rem; 288 | top: 0.14rem; 289 | font-size: 0.3rem; 290 | 291 | } 292 | 293 | 294 | .index .g-nav{ 295 | font-size: 0.18rem; 296 | height: 0.94rem; 297 | overflow: hidden; 298 | box-sizing: border-box; 299 | box-shadow: 0 2px 4px #08293f; 300 | 301 | } 302 | .index .m-nav{ 303 | display: flex; 304 | height: 0.6rem; 305 | line-height: 0.6rem; 306 | margin: 0.18rem auto; 307 | text-align: center; 308 | } 309 | .index .m-nav .nav{ 310 | flex-grow: 1; 311 | margin: 0 0.14rem; 312 | text-align: center; 313 | 314 | border: 1px solid #fff; 315 | font-size: 0.3rem; 316 | 317 | } 318 | .index .m-nav .nav.active{ 319 | background-color: #4ba292; 320 | } 321 | .index .m-nav .list{ 322 | 323 | } 324 | .index .m-nav .love{ 325 | 326 | } 327 | .index .m-nav .search{ 328 | 329 | } 330 | 331 | 332 | .index .g-songlist{ 333 | position: relative; 334 | z-index: 9; 335 | top: 2.82rem; 336 | width: 300vw; 337 | 338 | font-size: 0.24rem; 339 | 340 | overflow: hidden; // 外围包裹,设置为overflow:hidden,限制区域以防溢出 341 | -webkit-transition: all .3s ease-out; 342 | transition: all .3s ease-out; 343 | 344 | 345 | } 346 | .index .sliderWrap{ 347 | width: 100vw; 348 | overflow-y: auto; // 滑动容器,定义该属性的容器用于滑动 349 | height: auto; 350 | float: left; 351 | height: calc(100vh - 2.76rem); 352 | -webkit-overflow-scrolling: touch; // 让局部滚动成为全局滚动 353 | } 354 | 355 | .index .m-songlist{ 356 | 357 | width: 100vw; 358 | 359 | height: auto; 360 | float: left; 361 | } 362 | .index .m-songlist-l{ 363 | text-align: center; 364 | } 365 | .index .m-songlist-s{ 366 | text-align: center; 367 | } 368 | 369 | @keyframes fadeIn{ 370 | from{ 371 | opacity:0 372 | }to{ 373 | opacity:1 374 | } 375 | } 376 | .index .m-songlist .song{ 377 | height: 1.80rem; 378 | animation-name: fadeIn; 379 | -webkit-animation-name: fadeIn; 380 | animation-duration: .6s; 381 | -webkit-animation-duration: .6s; 382 | animation-fill-mode: both; 383 | -webkit-animation-fill-mode: both; 384 | } 385 | .index .m-songlist .song.active{ 386 | color: #bfd835; 387 | } 388 | .index .m-songlist .poster, .index .m-songlist .songinfo{ 389 | height: 100%; 390 | box-sizing: border-box; 391 | float: left; 392 | } 393 | .index .m-songlist .poster{ 394 | display: flex; 395 | align: center; 396 | width: 1.74rem; 397 | line-height: 1.80rem; 398 | padding:0.16rem; 399 | text-align: center; 400 | } 401 | .index .m-songlist .songinfo{ 402 | padding-top: 0.5rem; 403 | width: 3.78rem; 404 | padding-left: 0.4rem; 405 | text-align: left; 406 | } 407 | .index .m-songlist .loveflag{ 408 | width: 0.88rem; 409 | float: right; 410 | box-sizing: border-box; 411 | width: 1rem; 412 | justify-content: center; 413 | display: flex; 414 | align-items: center; 415 | height: 100%; 416 | position: relative; 417 | } 418 | .index .m-songlist .icon-love{ 419 | display:block; 420 | width: 0.52rem; 421 | height:0.52rem; 422 | background:url(../img/icon_love.png) no-repeat center center; 423 | background-size: cover; 424 | } 425 | .index .m-songlist .icon-love.active{ 426 | background:url(../img/icon_love_active.png) no-repeat center center; 427 | background-size: cover; 428 | } 429 | 430 | 431 | .index .g-footer{ 432 | position: fixed; 433 | z-index: 99; 434 | background: url(../img/bg_bottom.jpg) no-repeat center center; 435 | background-size: cover; 436 | width: 100%; 437 | bottom: 0; 438 | height: 0.92rem; 439 | line-height: 0.92rem; 440 | text-align: center; 441 | } 442 | .index .m-footer{ 443 | width:4.64rem; 444 | height: 100%; 445 | margin: 0 auto; 446 | } 447 | .index .m-footer .tip{ 448 | height: 100%; 449 | } 450 | .index .m-footer .tip::before{ 451 | content:""; 452 | display:inline-block; 453 | width: 0.36rem; 454 | height: 0.36rem; 455 | background:url(../img/cd.png) no-repeat center center; 456 | background-size: cover; 457 | vertical-align: -4%; 458 | } 459 | /* END主页 */ 460 | 461 | /* 播放页 */ 462 | .play{ 463 | position: fixed; 464 | width: 100%; 465 | height: 100%; 466 | 467 | top: 0; 468 | left: 0; 469 | z-index: 999999; 470 | -webkit-transform: translateY(-100%); 471 | transform: translateY(-100%); 472 | -webkit-transition: all .3s ease-in-out; 473 | transition: all .3s ease-in-out; 474 | -webkit-overflow-scrolling: touch; 475 | overflow-y: scroll; 476 | 477 | } 478 | /* 头部 */ 479 | .play .g-header{ 480 | height: 0.86rem; 481 | line-height: 0.86rem; 482 | text-align: center; 483 | justify-contents: center; 484 | box-sizing: border-box; 485 | } 486 | .play .m-header{ 487 | height: 100%; 488 | display: flex; 489 | 490 | } 491 | .play .m-header .back, .play .m-header .user{ 492 | width: 0.86rem; 493 | height: 100%; 494 | margin: 0 auto; 495 | font-size: 0.5rem; 496 | } 497 | .play .m-header .cog{ 498 | 499 | } 500 | .play .m-header .title{ 501 | width: 4.68rem; 502 | height: 100%; 503 | } 504 | 505 | /* 主体 */ 506 | .play .g-main{ 507 | height: calc(100vh - 0.86rem); 508 | } 509 | 510 | /* 音量控制模块 */ 511 | .play .g-volctrl{ 512 | height: 1.14rem; 513 | line-height: 1.14rem; 514 | } 515 | .play .m-volctrl{ 516 | width: 5.60rem; 517 | height: 100%; 518 | margin: 0 auto; 519 | } 520 | .play .m-volctrl .icon, .play .m-volctrl .m-progress{ 521 | float: left; 522 | } 523 | .play .m-volctrl .icon{ 524 | margin-top: 0.38rem; 525 | } 526 | .play .m-volctrl .icon-volume-high{ 527 | float: right; 528 | } 529 | .play .m-progress{ 530 | width: 4.64rem; 531 | height: 0.08rem; 532 | background-color: #162227; 533 | margin-top: 0.47rem; 534 | margin-left: 0.02rem; 535 | position: relative; 536 | } 537 | .play .m-progress .progress, .play .m-progress .progressbar{ 538 | position: absolute; 539 | } 540 | .play .m-progress .progress{ 541 | top: 0; 542 | height: 100%; 543 | width: 0%; 544 | background-color: #62c6c7; 545 | } 546 | .play .m-progress .progressbar{ 547 | top: 50%; 548 | width: 0.30rem; 549 | height: 0.30rem; 550 | left: 0%; 551 | background-color: #fff; 552 | border-radius: 50%; 553 | margin-top: -0.15rem; 554 | } 555 | .play .m-volctrl .progressbar{ 556 | left: 10%; 557 | } 558 | .play .m-volctrl .progress{ 559 | width: 10%; 560 | } 561 | /* 歌词模块 */ 562 | .play .g-lrc{ 563 | height: 4.60rem; 564 | } 565 | .play .m-lrc{ 566 | width: 4.64rem; 567 | height: 100%; 568 | margin: 0 auto; 569 | background-color: rgba(255, 255, 255, 0.2); 570 | box-shadow: 1px 1px 4px #333; 571 | overflow: hidden; 572 | } 573 | .play .m-lrc .lrc-box{ 574 | position:relative; 575 | width:3.88rem; 576 | height: 3.92rem; 577 | margin: 0.36rem auto 0; 578 | background-color: rgba(119, 140, 155, 0.4); 579 | border: 1px solid #8a9daa; 580 | box-shadow: 0 0 10px 2px rgba(97, 95, 95, 0.71); 581 | } 582 | .play .m-lrc .lrc-no, .play .m-lrc .lrc{ 583 | position: absolute; 584 | top: 0; 585 | width: 100%; 586 | height: 100%; 587 | } 588 | .play .m-lrc .lrc-no{ 589 | display:flex; 590 | justify-content:center; 591 | align-items:center; 592 | } 593 | @-webkit-keyframes rotate{ 594 | from{ 595 | transform: rotate(0deg) 596 | }to{ 597 | transform: rotate(360deg) 598 | } 599 | } 600 | @keyframes rotate{ 601 | from{ 602 | transform: rotate(0deg) 603 | }to{ 604 | transform: rotate(360deg) 605 | } 606 | } 607 | .play .m-lrc .lrc-no .lrc-bg{ 608 | -webkit-animation: rotate 40s linear infinite; 609 | animation: rotate 40s linear infinite; 610 | background-repeat: no-repeat; 611 | background-position: center center; 612 | background-size: cover; 613 | width: 3rem; 614 | height: 3rem; 615 | } 616 | .play .m-lrc .lrc{ 617 | overflow:hidden; 618 | top: 0; 619 | } 620 | .play .m-lrc .lrc-wrap{ 621 | position: absolute; 622 | width: 100%; 623 | height: 100%; 624 | top: 0; 625 | } 626 | .play .m-lrc .lrc-wrap p{ 627 | text-align: center; 628 | margin: 0.20rem 0.2rem; 629 | font-size: 0.24rem; 630 | } 631 | .play .m-lrc .lrc-wrap p.current{ 632 | color: #bfd835; 633 | font-size: 0.26rem; 634 | } 635 | 636 | 637 | /* 歌曲信息模块 */ 638 | .play .g-songinfo{ 639 | height: 1.50rem; 640 | } 641 | .play .m-songinfo{ 642 | width: 4.64rem; 643 | height: 100%; 644 | margin: 0 auto; 645 | overflow: hidden; 646 | } 647 | .play .m-songinfo .songname,.play .m-songinfo .singer{ 648 | text-align: center; 649 | font-weight: normal; 650 | margin-bottom: 0; 651 | } 652 | .play .m-songinfo .songname{ 653 | font-size: 0.34rem; 654 | margin-top: 0.44rem; 655 | 656 | } 657 | .play .m-songinfo .singer{ 658 | font-size: 0.20rem; 659 | } 660 | 661 | 662 | /* 音乐控制模块 */ 663 | .play .g-panel{ 664 | height: 3.34rem; 665 | } 666 | .play .g-panel .m-progress{ 667 | margin: 0 auto; 668 | height: 0.08rem; 669 | } 670 | .play .g-panel .m-progress .songtime{ 671 | font-size: 0.18rem; 672 | position:relative; 673 | top: 0.20rem; 674 | } 675 | .play .g-panel .m-progress .start{ 676 | float:left; 677 | } 678 | .play .g-panel .m-progress .end{ 679 | float:right; 680 | } 681 | 682 | .play .m-playcontrol{ 683 | width: 3.9rem; 684 | margin: 0.34rem auto 0; 685 | text-align: center; 686 | } 687 | .play .m-playcontrol .btn{ 688 | display: inline-block; 689 | background-size: cover; 690 | 691 | } 692 | .play .m-playcontrol .btn-prev{ 693 | width: 0.87rem; 694 | height:0.75rem; 695 | background: url(../img/pre.png) no-repeat center center; 696 | background-size: cover; 697 | } 698 | 699 | .play .m-playcontrol .btn-next{ 700 | width: 0.87rem; 701 | height:0.75rem; 702 | background: url(../img/next.png) no-repeat center center; 703 | background-size: cover; 704 | } 705 | .play .m-playcontrol .btn-play, .play .m-playcontrol .btn-pause{ 706 | width: 1.37rem; 707 | height: 1.27rem; 708 | } 709 | .play .m-playcontrol .btn-play{ 710 | background: url(../img/play.png) no-repeat center center; 711 | background-size: cover; 712 | } 713 | .play .m-playcontrol .btn-pause{ 714 | background: url(../img/pause.png) no-repeat center center; 715 | background-size: cover; 716 | } 717 | .play .m-playoptions{ 718 | height: 1.14rem; 719 | display:flex; 720 | width: 100%; 721 | bottom: 0; 722 | } 723 | .play .m-playoptions a{ 724 | display:flex; 725 | justify-content: center; 726 | align-items:center; 727 | flex: 1; 728 | color: #0e202c; 729 | } 730 | .play .m-playoptions a.active{ 731 | color:#fff; 732 | } 733 | 734 | /* 歌曲列表模块 */ 735 | .play .g-list{ 736 | position: fixed; 737 | width: 100%; 738 | height:8.62rem; 739 | bottom:1.14rem; 740 | background:url(../img/bg_list.jpg) no-repeat center center; 741 | background-size: cover; 742 | } 743 | .play .m-list{ 744 | margin-bottom: 1.0rem; 745 | width: 4.64rem; 746 | margin: 0 auto; 747 | height: 7.58rem; 748 | overflow-y: scroll; 749 | -webkit-overflow-scrolling: touch; 750 | } 751 | 752 | .play .m-list::-webkit-scrollbar{ 753 | opacity: 0; 754 | } 755 | 756 | .play .m-list .list{ 757 | 758 | } 759 | .play .m-list .list .song{ 760 | position:relative; 761 | padding: 0.24rem 0; 762 | border-bottom: 1px solid #fff; 763 | } 764 | .play .m-list .song .songinfo{ 765 | 766 | } 767 | .play .m-list .song .songinfo h1{ 768 | font-size: 0.40rem; 769 | margin: 0; 770 | font-weight: normal; 771 | } 772 | .play .m-list .song .songinfo h2{ 773 | font-size: 0.30rem; 774 | font-weight: normal; 775 | } 776 | .play .m-list .song .icon-cross{ 777 | position:absolute; 778 | top: 50%; 779 | margin-top: -0.14rem; 780 | right: 0; 781 | } 782 | 783 | /* END主体 */ 784 | /* END播放页 */ 785 | 786 | /* 用户页 */ 787 | .g-user{ 788 | position: fixed; 789 | z-index: 99999; 790 | top:0; 791 | display: flex; 792 | align-items: center; 793 | width: 100%; 794 | height: 100%; 795 | background: url(../img/bg.jpg) no-repeat center center; 796 | background-size: cover; 797 | transform: translateX(100%); 798 | transition: all .6s; 799 | } 800 | .m-user{ 801 | color: #fefefe; 802 | text-align: justify; 803 | } 804 | .m-user h2{ 805 | text-align: center; 806 | margin-top: 0.2rem; 807 | font-size: 0.6rem; 808 | } 809 | .m-user p{ 810 | padding: 0 0.8rem; 811 | font-size: 0.4rem; 812 | } 813 | /* END用户页 */ -------------------------------------------------------------------------------- /lib/jquery.touchSwipe.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @fileOverview TouchSwipe - jQuery Plugin 3 | * @version 1.6.18 4 | * 5 | * @author Matt Bryson http://www.github.com/mattbryson 6 | * @see https://github.com/mattbryson/TouchSwipe-Jquery-Plugin 7 | * @see http://labs.rampinteractive.co.uk/touchSwipe/ 8 | * @see http://plugins.jquery.com/project/touchSwipe 9 | * @license 10 | * Copyright (c) 2010-2015 Matt Bryson 11 | * Dual licensed under the MIT or GPL Version 2 licenses. 12 | * 13 | */ 14 | !function(factory){"function"==typeof define&&define.amd&&define.amd.jQuery?define(["jquery"],factory):factory("undefined"!=typeof module&&module.exports?require("jquery"):jQuery)}(function($){"use strict";function init(options){return!options||void 0!==options.allowPageScroll||void 0===options.swipe&&void 0===options.swipeStatus||(options.allowPageScroll=NONE),void 0!==options.click&&void 0===options.tap&&(options.tap=options.click),options||(options={}),options=$.extend({},$.fn.swipe.defaults,options),this.each(function(){var $this=$(this),plugin=$this.data(PLUGIN_NS);plugin||(plugin=new TouchSwipe(this,options),$this.data(PLUGIN_NS,plugin))})}function TouchSwipe(element,options){function touchStart(jqEvent){if(!(getTouchInProgress()||$(jqEvent.target).closest(options.excludedElements,$element).length>0)){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent;if(!event.pointerType||"mouse"!=event.pointerType||0!=options.fallbackToMouseEvents){var ret,touches=event.touches,evt=touches?touches[0]:event;return phase=PHASE_START,touches?fingerCount=touches.length:options.preventDefaultEvents!==!1&&jqEvent.preventDefault(),distance=0,direction=null,currentDirection=null,pinchDirection=null,duration=0,startTouchesDistance=0,endTouchesDistance=0,pinchZoom=1,pinchDistance=0,maximumsMap=createMaximumsData(),cancelMultiFingerRelease(),createFingerData(0,evt),!touches||fingerCount===options.fingers||options.fingers===ALL_FINGERS||hasPinches()?(startTime=getTimeStamp(),2==fingerCount&&(createFingerData(1,touches[1]),startTouchesDistance=endTouchesDistance=calculateTouchesDistance(fingerData[0].start,fingerData[1].start)),(options.swipeStatus||options.pinchStatus)&&(ret=triggerHandler(event,phase))):ret=!1,ret===!1?(phase=PHASE_CANCEL,triggerHandler(event,phase),ret):(options.hold&&(holdTimeout=setTimeout($.proxy(function(){$element.trigger("hold",[event.target]),options.hold&&(ret=options.hold.call($element,event,event.target))},this),options.longTapThreshold)),setTouchInProgress(!0),null)}}}function touchMove(jqEvent){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent;if(phase!==PHASE_END&&phase!==PHASE_CANCEL&&!inMultiFingerRelease()){var ret,touches=event.touches,evt=touches?touches[0]:event,currentFinger=updateFingerData(evt);if(endTime=getTimeStamp(),touches&&(fingerCount=touches.length),options.hold&&clearTimeout(holdTimeout),phase=PHASE_MOVE,2==fingerCount&&(0==startTouchesDistance?(createFingerData(1,touches[1]),startTouchesDistance=endTouchesDistance=calculateTouchesDistance(fingerData[0].start,fingerData[1].start)):(updateFingerData(touches[1]),endTouchesDistance=calculateTouchesDistance(fingerData[0].end,fingerData[1].end),pinchDirection=calculatePinchDirection(fingerData[0].end,fingerData[1].end)),pinchZoom=calculatePinchZoom(startTouchesDistance,endTouchesDistance),pinchDistance=Math.abs(startTouchesDistance-endTouchesDistance)),fingerCount===options.fingers||options.fingers===ALL_FINGERS||!touches||hasPinches()){if(direction=calculateDirection(currentFinger.start,currentFinger.end),currentDirection=calculateDirection(currentFinger.last,currentFinger.end),validateDefaultEvent(jqEvent,currentDirection),distance=calculateDistance(currentFinger.start,currentFinger.end),duration=calculateDuration(),setMaxDistance(direction,distance),ret=triggerHandler(event,phase),!options.triggerOnTouchEnd||options.triggerOnTouchLeave){var inBounds=!0;if(options.triggerOnTouchLeave){var bounds=getbounds(this);inBounds=isInBounds(currentFinger.end,bounds)}!options.triggerOnTouchEnd&&inBounds?phase=getNextPhase(PHASE_MOVE):options.triggerOnTouchLeave&&!inBounds&&(phase=getNextPhase(PHASE_END)),phase!=PHASE_CANCEL&&phase!=PHASE_END||triggerHandler(event,phase)}}else phase=PHASE_CANCEL,triggerHandler(event,phase);ret===!1&&(phase=PHASE_CANCEL,triggerHandler(event,phase))}}function touchEnd(jqEvent){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent,touches=event.touches;if(touches){if(touches.length&&!inMultiFingerRelease())return startMultiFingerRelease(event),!0;if(touches.length&&inMultiFingerRelease())return!0}return inMultiFingerRelease()&&(fingerCount=fingerCountAtRelease),endTime=getTimeStamp(),duration=calculateDuration(),didSwipeBackToCancel()||!validateSwipeDistance()?(phase=PHASE_CANCEL,triggerHandler(event,phase)):options.triggerOnTouchEnd||options.triggerOnTouchEnd===!1&&phase===PHASE_MOVE?(options.preventDefaultEvents!==!1&&jqEvent.preventDefault(),phase=PHASE_END,triggerHandler(event,phase)):!options.triggerOnTouchEnd&&hasTap()?(phase=PHASE_END,triggerHandlerForGesture(event,phase,TAP)):phase===PHASE_MOVE&&(phase=PHASE_CANCEL,triggerHandler(event,phase)),setTouchInProgress(!1),null}function touchCancel(){fingerCount=0,endTime=0,startTime=0,startTouchesDistance=0,endTouchesDistance=0,pinchZoom=1,cancelMultiFingerRelease(),setTouchInProgress(!1)}function touchLeave(jqEvent){var event=jqEvent.originalEvent?jqEvent.originalEvent:jqEvent;options.triggerOnTouchLeave&&(phase=getNextPhase(PHASE_END),triggerHandler(event,phase))}function removeListeners(){$element.unbind(START_EV,touchStart),$element.unbind(CANCEL_EV,touchCancel),$element.unbind(MOVE_EV,touchMove),$element.unbind(END_EV,touchEnd),LEAVE_EV&&$element.unbind(LEAVE_EV,touchLeave),setTouchInProgress(!1)}function getNextPhase(currentPhase){var nextPhase=currentPhase,validTime=validateSwipeTime(),validDistance=validateSwipeDistance(),didCancel=didSwipeBackToCancel();return!validTime||didCancel?nextPhase=PHASE_CANCEL:!validDistance||currentPhase!=PHASE_MOVE||options.triggerOnTouchEnd&&!options.triggerOnTouchLeave?!validDistance&¤tPhase==PHASE_END&&options.triggerOnTouchLeave&&(nextPhase=PHASE_CANCEL):nextPhase=PHASE_END,nextPhase}function triggerHandler(event,phase){var ret,touches=event.touches;return(didSwipe()||hasSwipes())&&(ret=triggerHandlerForGesture(event,phase,SWIPE)),(didPinch()||hasPinches())&&ret!==!1&&(ret=triggerHandlerForGesture(event,phase,PINCH)),didDoubleTap()&&ret!==!1?ret=triggerHandlerForGesture(event,phase,DOUBLE_TAP):didLongTap()&&ret!==!1?ret=triggerHandlerForGesture(event,phase,LONG_TAP):didTap()&&ret!==!1&&(ret=triggerHandlerForGesture(event,phase,TAP)),phase===PHASE_CANCEL&&touchCancel(event),phase===PHASE_END&&(touches?touches.length||touchCancel(event):touchCancel(event)),ret}function triggerHandlerForGesture(event,phase,gesture){var ret;if(gesture==SWIPE){if($element.trigger("swipeStatus",[phase,direction||null,distance||0,duration||0,fingerCount,fingerData,currentDirection]),options.swipeStatus&&(ret=options.swipeStatus.call($element,event,phase,direction||null,distance||0,duration||0,fingerCount,fingerData,currentDirection),ret===!1))return!1;if(phase==PHASE_END&&validateSwipe()){if(clearTimeout(singleTapTimeout),clearTimeout(holdTimeout),$element.trigger("swipe",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipe&&(ret=options.swipe.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection),ret===!1))return!1;switch(direction){case LEFT:$element.trigger("swipeLeft",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeLeft&&(ret=options.swipeLeft.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection));break;case RIGHT:$element.trigger("swipeRight",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeRight&&(ret=options.swipeRight.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection));break;case UP:$element.trigger("swipeUp",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeUp&&(ret=options.swipeUp.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection));break;case DOWN:$element.trigger("swipeDown",[direction,distance,duration,fingerCount,fingerData,currentDirection]),options.swipeDown&&(ret=options.swipeDown.call($element,event,direction,distance,duration,fingerCount,fingerData,currentDirection))}}}if(gesture==PINCH){if($element.trigger("pinchStatus",[phase,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData]),options.pinchStatus&&(ret=options.pinchStatus.call($element,event,phase,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData),ret===!1))return!1;if(phase==PHASE_END&&validatePinch())switch(pinchDirection){case IN:$element.trigger("pinchIn",[pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData]),options.pinchIn&&(ret=options.pinchIn.call($element,event,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData));break;case OUT:$element.trigger("pinchOut",[pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData]),options.pinchOut&&(ret=options.pinchOut.call($element,event,pinchDirection||null,pinchDistance||0,duration||0,fingerCount,pinchZoom,fingerData))}}return gesture==TAP?phase!==PHASE_CANCEL&&phase!==PHASE_END||(clearTimeout(singleTapTimeout),clearTimeout(holdTimeout),hasDoubleTap()&&!inDoubleTap()?(doubleTapStartTime=getTimeStamp(),singleTapTimeout=setTimeout($.proxy(function(){doubleTapStartTime=null,$element.trigger("tap",[event.target]),options.tap&&(ret=options.tap.call($element,event,event.target))},this),options.doubleTapThreshold)):(doubleTapStartTime=null,$element.trigger("tap",[event.target]),options.tap&&(ret=options.tap.call($element,event,event.target)))):gesture==DOUBLE_TAP?phase!==PHASE_CANCEL&&phase!==PHASE_END||(clearTimeout(singleTapTimeout),clearTimeout(holdTimeout),doubleTapStartTime=null,$element.trigger("doubletap",[event.target]),options.doubleTap&&(ret=options.doubleTap.call($element,event,event.target))):gesture==LONG_TAP&&(phase!==PHASE_CANCEL&&phase!==PHASE_END||(clearTimeout(singleTapTimeout),doubleTapStartTime=null,$element.trigger("longtap",[event.target]),options.longTap&&(ret=options.longTap.call($element,event,event.target)))),ret}function validateSwipeDistance(){var valid=!0;return null!==options.threshold&&(valid=distance>=options.threshold),valid}function didSwipeBackToCancel(){var cancelled=!1;return null!==options.cancelThreshold&&null!==direction&&(cancelled=getMaxDistance(direction)-distance>=options.cancelThreshold),cancelled}function validatePinchDistance(){return null!==options.pinchThreshold?pinchDistance>=options.pinchThreshold:!0}function validateSwipeTime(){var result;return result=options.maxTimeThreshold?!(duration>=options.maxTimeThreshold):!0}function validateDefaultEvent(jqEvent,direction){if(options.preventDefaultEvents!==!1)if(options.allowPageScroll===NONE)jqEvent.preventDefault();else{var auto=options.allowPageScroll===AUTO;switch(direction){case LEFT:(options.swipeLeft&&auto||!auto&&options.allowPageScroll!=HORIZONTAL)&&jqEvent.preventDefault();break;case RIGHT:(options.swipeRight&&auto||!auto&&options.allowPageScroll!=HORIZONTAL)&&jqEvent.preventDefault();break;case UP:(options.swipeUp&&auto||!auto&&options.allowPageScroll!=VERTICAL)&&jqEvent.preventDefault();break;case DOWN:(options.swipeDown&&auto||!auto&&options.allowPageScroll!=VERTICAL)&&jqEvent.preventDefault();break;case NONE:}}}function validatePinch(){var hasCorrectFingerCount=validateFingers(),hasEndPoint=validateEndPoint(),hasCorrectDistance=validatePinchDistance();return hasCorrectFingerCount&&hasEndPoint&&hasCorrectDistance}function hasPinches(){return!!(options.pinchStatus||options.pinchIn||options.pinchOut)}function didPinch(){return!(!validatePinch()||!hasPinches())}function validateSwipe(){var hasValidTime=validateSwipeTime(),hasValidDistance=validateSwipeDistance(),hasCorrectFingerCount=validateFingers(),hasEndPoint=validateEndPoint(),didCancel=didSwipeBackToCancel(),valid=!didCancel&&hasEndPoint&&hasCorrectFingerCount&&hasValidDistance&&hasValidTime;return valid}function hasSwipes(){return!!(options.swipe||options.swipeStatus||options.swipeLeft||options.swipeRight||options.swipeUp||options.swipeDown)}function didSwipe(){return!(!validateSwipe()||!hasSwipes())}function validateFingers(){return fingerCount===options.fingers||options.fingers===ALL_FINGERS||!SUPPORTS_TOUCH}function validateEndPoint(){return 0!==fingerData[0].end.x}function hasTap(){return!!options.tap}function hasDoubleTap(){return!!options.doubleTap}function hasLongTap(){return!!options.longTap}function validateDoubleTap(){if(null==doubleTapStartTime)return!1;var now=getTimeStamp();return hasDoubleTap()&&now-doubleTapStartTime<=options.doubleTapThreshold}function inDoubleTap(){return validateDoubleTap()}function validateTap(){return(1===fingerCount||!SUPPORTS_TOUCH)&&(isNaN(distance)||distance" + lyricContent + "
"; 456 | }); 457 | Dom.lrcwrap.html(tpl); 458 | return; 459 | } 460 | var preTpl = Handlebars.compile(lyricTpl); 461 | Dom.lrcwrap.html(preTpl(arrMap)); 462 | } 463 | 464 | // 歌词同步 465 | function syncLyric(curS, formatTimeArr) { 466 | // console.log(Math.floor(curS) + "|" + lrcHighIndex); 467 | if (Math.floor(curS) >= formatTimeArr[lrcHighIndex]) { 468 | Dom.lrc.eq(lrcHighIndex).addClass('current').siblings().removeClass('current'); 469 | if (lrcHighIndex >= 1) { 470 | lrcMoveIndex = lrcHighIndex - 2; 471 | moveDis += Util.getMoveDis(lrcMoveIndex); 472 | Dom.lrcwrap.animate({ 473 | "top": "-" + moveDis + "px" 474 | }, 100); 475 | lrcMoveIndex++; 476 | } 477 | lrcHighIndex++; 478 | } 479 | } 480 | 481 | function updateLrcView(curS, formatTimeArr) { 482 | moveDis = 0; 483 | lrcHighIndex = Util.getHighLightIndex(curS, formatTimeArr); 484 | lrcMoveIndex = lrcHighIndex - 2; 485 | Dom.lrc.eq(lrcHighIndex).addClass('current').siblings().removeClass('current'); 486 | for (var i = 0; i < lrcHighIndex - 1; i++) { 487 | moveDis += Util.getMoveDis(i); 488 | } 489 | Dom.lrcwrap.animate({ 490 | "top": "-" + moveDis + "px" 491 | }, 100); 492 | 493 | } 494 | return { 495 | renderList: renderList, 496 | updateList: updateList, 497 | updateSongInfo: updateSongInfo, 498 | syncLyric: syncLyric, 499 | updateLrcView: updateLrcView, 500 | }; 501 | } 502 | 503 | // =================================================================事件绑定模块 504 | function EventHandler() { 505 | 506 | // 根据手机屏幕调整布局 507 | $(window).on("resize", function () { 508 | Util.rescale(); 509 | }); 510 | 511 | // 滚动加载 512 | Dom.sliderWrap.eq(0).on("scroll", function (e) { 513 | var updateSongModel = PlayerModel(); 514 | var updateSongListUI = ModelUIFrame(Dom.songListContainer); 515 | var scrT = $(this).scrollTop(); 516 | var listBoxWinH = Dom.songContainerWrap.height(); 517 | 518 | if (Util.isScrollToBottom($(this), listBoxWinH, scrT)) { 519 | updateSongModel.getSongList("data/data.json", function (data) { 520 | // 生成歌曲列表 521 | var beUpdSongNum = songNum; // 记录更新前歌曲的总数 522 | updateSongListUI.renderList(data, 0, null, function () { 523 | updateSongListUI.updateList(); 524 | }); 525 | Util.addAnimationDelay(Dom.song, beUpdSongNum); 526 | // 保存歌词数据 527 | updateSongModel.saveLyric(data); 528 | }); 529 | } 530 | }); 531 | 532 | // 歌曲播放完毕事件 533 | Dom.audio.onended = function () { 534 | // 触发点击下一首歌的事件 535 | $(Dom.next).trigger("click"); 536 | }; 537 | 538 | // 监听歌曲播放时间发生变化事件 539 | $(Dom.audio).on("timeupdate", function () { 540 | var curS = Dom.audio.currentTime; 541 | var curPercent = Util.timeToPercent(curS, duration); 542 | 543 | // 歌词同步 544 | songModelUI.syncLyric(curS, formatTimeArr); 545 | 546 | // 更新歌曲时间,进度条 547 | Util.updataTime(Dom.start, curS); 548 | Util.updateProgress(Dom.sProgress, curPercent); 549 | Util.updateBarPos(Dom.songbar, curPercent); 550 | }); 551 | 552 | // 点击一首歌曲 553 | $(".g-songlist").on("click", ".poster", function (e) { 554 | e.preventDefault(); 555 | e.stopPropagation(); 556 | 557 | // 列表视图相关 558 | var listType = Dom.navBox.find(".nav.active").index(); 559 | if (listType === 0) { 560 | Dom.songContainerWrap.find(".m-songlist").eq(1).find(".song").removeClass('active'); 561 | } else if (listType === 1) { 562 | Dom.songContainerWrap.find(".m-songlist").eq(0).find(".song").removeClass('active'); 563 | } 564 | 565 | $(this).parents(".song").addClass("active").siblings().removeClass("active"); 566 | Dom.index.addClass('hide'); 567 | 568 | Dom.playPage.css({ 569 | transform: "translateY(0%)" 570 | }); 571 | 572 | // 音频相关 573 | if (Dom.audio.src == rootPath + $(this).parents(".song").data("src")) { 574 | return; 575 | } 576 | Dom.audio.src = $(this).parents(".song").data("src"); 577 | Dom.audio.play(); 578 | Dom.btnPlay.removeClass("btn-play").addClass("btn-pause"); 579 | 580 | Dom.audio.setAttribute("index", $(this).parents(".song").data("index")); 581 | index = $(this).parents(".song").data("index"); 582 | 583 | Dom.audio.oncanplay = function () { 584 | duration = this.duration; 585 | formatDuration = Util.formatTime(duration); 586 | Dom.end.html(formatDuration); 587 | 588 | }; 589 | 590 | // 播放视图相关 591 | songInfo = { 592 | songname: $(this).parents(".song").find(".lsongname").text(), 593 | singer: $(this).parents(".song").find(".lsinger").text(), 594 | lrcimg: "img/poster/" + $(this).parents(".song").data("index") + "-origin.jpg", 595 | lyric: Util.getItem("lyric" + $(this).parents(".song").data("index")) 596 | }; 597 | timeArr = Util.createArrMap(songInfo.lyric).timeArr; 598 | formatTimeArr = Util.formatLyricTime(timeArr); 599 | songModelUI = ModelUIFrame(); 600 | songModelUI.updateSongInfo(songInfo); 601 | 602 | lrcMoveIndex = 0; 603 | lrcHighIndex = 0; 604 | moveDis = 0; 605 | }); 606 | 607 | // 点击后退按钮 608 | Dom.btnBack.on("click", function (e) { 609 | e.preventDefault(); 610 | e.stopPropagation(); 611 | Dom.index.removeClass('hide'); 612 | Dom.playPage.css({ 613 | transform: "translateY(-100%)" 614 | }); 615 | }); 616 | 617 | function saveLoveSong() { 618 | var cIndex = $(this).parents(".song").data("index"); // 当前点击歌曲索引 619 | var loveFlag = $(this).find(".icon-love").hasClass('active'); 620 | var lsongArr = Util.getItem('lsonglist') === null ? [] : Util.getItem('lsonglist'); // 记录喜爱歌曲索引的数组 621 | 622 | if (loveFlag) { 623 | if (lsongArr.length === 0) { 624 | return; 625 | } else { 626 | lsongArr = JSON.parse(lsongArr); 627 | $.each(lsongArr, function (index, value) { 628 | if (value === cIndex) { 629 | lsongArr.splice(index, 1); 630 | Util.setItem("lsonglist", lsongArr); 631 | return; 632 | } 633 | }); 634 | } 635 | } else { 636 | if (lsongArr.length === 0) { 637 | lsongArr.push(cIndex); 638 | } else { 639 | lsongArr = JSON.parse(lsongArr); 640 | lsongArr.push(cIndex); 641 | } 642 | Util.setItem("lsonglist", lsongArr); 643 | } 644 | } 645 | // 点击喜爱按钮 646 | $(".g-songlist").on("click", ".loveflag", function (e) { 647 | e.preventDefault(); 648 | e.stopPropagation(); 649 | 650 | saveLoveSong.call(this); 651 | $(this).find(".icon-love").toggleClass('active'); 652 | }); 653 | 654 | // 播放或暂停按钮 655 | Dom.btnControl.on("click", function () { 656 | if ($(this).hasClass("btn-play")) { 657 | Dom.audio.play(); 658 | $(this).removeClass('btn-play').addClass('btn-pause'); 659 | } else { 660 | Dom.audio.pause(); 661 | $(this).removeClass('btn-pause').addClass('btn-play'); 662 | } 663 | }); 664 | /** 665 | * @param {[Number]} listType [0:歌曲列表,1:我的最爱] 666 | * @param {[String]} 点击按钮类型 ["prev":上一首 "next":下一首] 667 | * @param {[String]} 播放模式 668 | * @return {[Number]} [返回下一首播放的index] 669 | */ 670 | function getNextIndex(listType, clickType, playmode) { 671 | var activeIndex; 672 | var nextIndex; 673 | // 单击类型:下一首 674 | if (clickType === "prev") { 675 | if (listType === 0) { 676 | switch (playmode) { 677 | case 0: 678 | nextIndex = (index - 1) < 0 ? songNum - 1 : index - 1; 679 | break; 680 | case 1: 681 | nextIndex = Util.makeRandom(index, songNum); 682 | break; 683 | case 2: 684 | nextIndex = index; 685 | break; 686 | } 687 | } else if (listType === 1) { 688 | activeIndex = Dom.lSongListContainer.find(".song.active").index(); 689 | switch (playmode) { 690 | case 0: 691 | nextIndex = (activeIndex - 1) < 0 ? songNum - 1 : activeIndex - 1; 692 | break; 693 | case 1: 694 | nextIndex = Util.makeRandom(activeIndex, songNum); 695 | break; 696 | case 2: 697 | nextIndex = activeIndex; 698 | break; 699 | } 700 | } 701 | 702 | } else if (clickType === "next") { // 单击类型:下一首 703 | if (listType === 0) { 704 | switch (playmode) { 705 | case 0: 706 | nextIndex = (index + 1) > songNum - 1 ? 0 : index + 1; 707 | break; 708 | case 1: 709 | nextIndex = Util.makeRandom(index, songNum); 710 | break; 711 | case 2: 712 | nextIndex = index; 713 | break; 714 | } 715 | } else if (listType === 1) { 716 | 717 | activeIndex = Dom.lSongListContainer.find(".song.active").index(); 718 | 719 | switch (playmode) { 720 | case 0: 721 | nextIndex = (activeIndex + 1) > songNum - 1 ? 0 : activeIndex + 1; 722 | break; 723 | case 1: 724 | nextIndex = Util.makeRandom(activeIndex, songNum); 725 | break; 726 | case 2: 727 | nextIndex = activeIndex; 728 | break; 729 | } 730 | } 731 | } 732 | return nextIndex; 733 | } 734 | 735 | // 上一首 736 | var src = ""; // 记录歌曲地址变量 737 | var lrcIndex; // 歌词本地查找索引:歌曲列表中lrcIndex = index;我的最爱中lrcIndex可能和index不相等 738 | var listType; // 当前所处列表类型: 0为歌曲列表 1为我的最爱 739 | Dom.prev.on("click", function () { 740 | listType = Dom.navBox.find(".nav.active").index(); 741 | index = getNextIndex(listType, "prev", Settings.playmode); 742 | 743 | if (listType === 0) { 744 | Dom.song.eq(index).addClass("active").siblings().removeClass('active'); 745 | src = Dom.song.eq(index).data("src"); 746 | lrcIndex = Dom.song.eq(index).data("index"); 747 | } else if (listType === 1) { 748 | Dom.lsong.eq(index).addClass("active").siblings().removeClass('active'); 749 | src = Dom.lsong.eq(index).data("src"); 750 | lrcIndex = Dom.lsong.eq(index).data("index"); 751 | } 752 | Dom.audio.src = src; 753 | Dom.audio.play(); 754 | 755 | // 播放视图相关 756 | songInfo = { 757 | songname: Dom.song.eq(lrcIndex).find(".lsongname").text() || Dom.lsong.eq(index).find(".lsongname").text(), // 当播放的是“歌曲列表”未加载的歌曲时,使用“我的最爱”列表中的歌曲信息 758 | singer: Dom.song.eq(lrcIndex).find(".lsinger").text() || Dom.lsong.eq(index).find(".lsinger").text(), 759 | lrcimg: rootPath + "img/poster/" + lrcIndex + "-origin.jpg", 760 | lyric: Util.getItem("lyric" + lrcIndex) 761 | }; 762 | lrcMoveIndex = 0; 763 | lrcHighIndex = 0; 764 | moveDis = 0; 765 | 766 | timeArr = Util.createArrMap(songInfo.lyric).timeArr; 767 | formatTimeArr = Util.formatLyricTime(timeArr); 768 | songModelUI = ModelUIFrame(); 769 | songModelUI.updateSongInfo(songInfo); 770 | 771 | }); 772 | 773 | //下一首 774 | Dom.next.on("click", function () { 775 | 776 | listType = Dom.navBox.find(".nav.active").index(); 777 | index = getNextIndex(listType, "next", Settings.playmode); 778 | 779 | if (listType === 0) { 780 | Dom.song.eq(index).addClass("active").siblings().removeClass('active'); 781 | src = Dom.song.eq(index).data("src"); 782 | lrcIndex = Dom.song.eq(index).data("index"); 783 | 784 | } else if (listType === 1) { 785 | Dom.lsong.eq(index).addClass("active").siblings().removeClass('active'); 786 | src = Dom.lsong.eq(index).data("src"); 787 | lrcIndex = Dom.lsong.eq(index).data("index"); 788 | } 789 | Dom.audio.src = src; 790 | Dom.audio.play(); 791 | 792 | // 播放视图相关 793 | songInfo = { 794 | songname: Dom.song.eq(lrcIndex).find(".lsongname").text() || Dom.lsong.eq(index).find(".lsongname").text(), // 当播放的是“歌曲列表”未加载的歌曲时,使用“我的最爱”列表中的歌曲信息 795 | singer: Dom.song.eq(lrcIndex).find(".lsinger").text() || Dom.lsong.eq(index).find(".lsinger").text(), 796 | lrcimg: rootPath + "img/poster/" + lrcIndex + "-origin.jpg", 797 | lyric: Util.getItem("lyric" + lrcIndex) 798 | }; 799 | lrcMoveIndex = 0; 800 | lrcHighIndex = 0; 801 | moveDis = 0; 802 | 803 | timeArr = Util.createArrMap(songInfo.lyric).timeArr; 804 | formatTimeArr = Util.formatLyricTime(timeArr); 805 | songModelUI = ModelUIFrame(); 806 | songModelUI.updateSongInfo(songInfo); 807 | }); 808 | 809 | //随机播放 810 | Dom.random.on("click", function () { 811 | $(this).addClass("active").siblings().removeClass("active"); 812 | Settings.playmode = 1; 813 | }); 814 | 815 | //列表循环 816 | Dom.menu.on("click", function () { 817 | $(this).addClass("active").siblings().removeClass("active"); 818 | Settings.playmode = 0; 819 | }); 820 | 821 | //单曲循环播放 822 | Dom.loop.on("click", function () { 823 | $(this).addClass("active").siblings().removeClass("active"); 824 | Settings.playmode = 2; 825 | }); 826 | 827 | //歌曲进度条滑块滑动 828 | Dom.songbar.on("touchstart", function (e) { 829 | e.preventDefault(); 830 | e.stopPropagation(); 831 | 832 | $(Dom.audio).off("timeupdate"); 833 | Dom.audio.pause(); 834 | 835 | var totalW = $(Dom.msProgress).width(); 836 | var leftDis = $(Dom.sProgress).offset().left; 837 | var curS = 0; 838 | var curPercent = 0; 839 | var percent = ""; 840 | var touchMove = e.originalEvent.changedTouches[0].clientX; 841 | var dis = e.originalEvent.changedTouches[0].clientX - leftDis; 842 | Dom.songbar.on("touchmove", function (e) { 843 | e.preventDefault(); 844 | e.stopPropagation(); 845 | touchMove = e.originalEvent.targetTouches[0].clientX; 846 | dis = touchMove - leftDis > totalW ? totalW : touchMove - leftDis; 847 | dis = touchMove - leftDis < 0 ? 0 : dis; 848 | percent = Math.floor(dis / totalW * 100) + "%"; 849 | Util.updateProgress(Dom.sProgress, percent); 850 | Util.updateBarPos(Dom.songbar, percent); 851 | 852 | }); 853 | Dom.songbar.on("touchend", function (e) { 854 | e.preventDefault(); 855 | e.stopPropagation(); 856 | 857 | if (Dom.audio.paused) { 858 | Dom.audio.play(); 859 | } 860 | 861 | percent = Math.floor(dis / totalW * 100) + "%"; 862 | Util.updateProgress(Dom.sProgress, percent); 863 | Util.updateBarPos(Dom.songbar, percent); 864 | curS = duration * parseInt(percent.replace("%", "")) / 100; 865 | Dom.audio.currentTime = curS; 866 | 867 | // 播放视图相关 868 | songModelUI.updateLrcView(curS, formatTimeArr); 869 | Dom.audio.ontimeupdate = function () { 870 | var curS = Dom.audio.currentTime; 871 | var curPercent = Util.timeToPercent(curS, duration); 872 | 873 | // 歌词同步 874 | songModelUI.syncLyric(curS, formatTimeArr); 875 | 876 | // 更新歌曲时间,进度条 877 | Util.updataTime(Dom.start, curS); 878 | Util.updateProgress(Dom.sProgress, curPercent); 879 | Util.updateBarPos(Dom.songbar, curPercent); 880 | }; 881 | 882 | Dom.songbar.off("touchmove touchend"); 883 | }); 884 | }); 885 | 886 | // 歌曲进度条点击 887 | Dom.msProgress.on("mousedown", function (e) { 888 | $(Dom.audio).off("timeupdate"); 889 | 890 | var totalW = $(Dom.msProgress).width(); 891 | var leftDis = $(Dom.sProgress).offset().left; 892 | var curS = 0; 893 | var curPercent = 0; 894 | var dis = e.pageX - leftDis > totalW ? totalW : e.pageX - leftDis; 895 | percent = Math.floor(dis / totalW * 100) + "%"; 896 | 897 | Dom.msProgress.on("mouseup", function (e) { 898 | Util.updateProgress(Dom.sProgress, percent); 899 | Util.updateBarPos(Dom.songbar, percent); 900 | curS = duration * parseInt(percent.replace("%", "")) / 100; 901 | Dom.audio.currentTime = curS; 902 | 903 | // 播放视图相关 904 | songModelUI.updateLrcView(curS, formatTimeArr); 905 | Dom.audio.ontimeupdate = function () { 906 | var curS = Dom.audio.currentTime; 907 | var curPercent = Util.timeToPercent(curS, duration); 908 | 909 | // 歌词同步 910 | songModelUI.syncLyric(curS, formatTimeArr); 911 | 912 | // 更新歌曲时间,进度条 913 | Util.updataTime(Dom.start, curS); 914 | Util.updateProgress(Dom.sProgress, curPercent); 915 | Util.updateBarPos(Dom.songbar, curPercent); 916 | }; 917 | 918 | Dom.msProgress.off("mouseup"); 919 | }); 920 | 921 | }); 922 | 923 | //音量控制 924 | Dom.vbar.on("touchstart", function (e) { 925 | var totalW = $(Dom.mvProgress).width(); 926 | var leftDis = $(Dom.sProgress).offset().left; 927 | var percent = ""; 928 | e.preventDefault(); 929 | e.stopPropagation(); 930 | Dom.vbar.on("touchmove", function (e) { 931 | e.preventDefault(); 932 | e.stopPropagation(); 933 | var touchMove = e.originalEvent.targetTouches[0].clientX; 934 | var dis = touchMove - leftDis > totalW ? totalW : touchMove - leftDis; 935 | dis = touchMove - leftDis < 0 ? 0 : dis; 936 | percent = Math.floor(dis / totalW * 100) + "%"; 937 | Dom.audio.volume = (dis / totalW).toFixed(1); 938 | Util.updateProgress(Dom.vProgress, percent); 939 | Util.updateBarPos(Dom.vbar, percent); 940 | }); 941 | Dom.vbar.on("touchend", function (e) { 942 | e.preventDefault(); 943 | e.stopPropagation(); 944 | Dom.vbar.off("touchmove touchend"); 945 | }); 946 | }); 947 | 948 | // 歌词海报切换 949 | Dom.lrcbox.on("click", function () { 950 | $(this).find(".lrc-no").toggleClass("hide"); 951 | $(this).find(".lrc").toggleClass("hide"); 952 | }); 953 | 954 | // 歌曲列表向上或向下滑动 955 | var top = 0; 956 | Dom.sliderWrap.each(function () { 957 | $(this).on("scroll", function () { 958 | var tempTop = $(this).scrollTop(); 959 | if (tempTop - top > 10) { //防止弹性滚动造成的不是预期的效果 960 | // 向下 961 | Dom.footer.stop(true).animate({ 962 | bottom: "-100%" 963 | }, 1000); 964 | } else if (tempTop - top < -10) { 965 | // 向上 966 | Dom.footer.stop(true).animate({ 967 | bottom: "0%" 968 | }, 1000); 969 | } 970 | top = tempTop; 971 | }); 972 | }); 973 | 974 | // 列表切换(点击) 975 | $(".nav").on("click", function () { 976 | var navIndex = $(this).index(); 977 | songNum = Dom.songContainerWrap.find(".m-songlist").eq(navIndex).find(".song").length; 978 | // 切换 979 | $(this).addClass('active').siblings().removeClass('active'); 980 | Dom.songContainerWrap.css({ 981 | "transform": "translateX(" + (-navIndex * 1 / 3) * 100 + "%)" 982 | }); 983 | 984 | // 更新歌曲数据 985 | Dom.songCount.text(songNum); 986 | }); 987 | 988 | // 点击用户头像 989 | $(".icon-user").on("click", function () { 990 | $(".g-user").css({ 991 | transform: "translateX(0%)" 992 | }); 993 | }); 994 | 995 | // 点击用户头像页面 996 | $(".g-user").on("click", function () { 997 | $(".g-user").css({ 998 | transform: "translateX(100%)" 999 | }); 1000 | }); 1001 | 1002 | // 阻止默认滚动事件 1003 | $(window).on('scroll', function (e) { 1004 | console.log("window"); 1005 | e.preventDefault(); 1006 | e.stopPropagation(); 1007 | }); 1008 | $("body").on('scroll', function (e) { 1009 | e.preventDefault(); 1010 | e.stopPropagation(); 1011 | }); 1012 | $(".index").on('scroll', function (e) { 1013 | e.preventDefault(); 1014 | e.stopPropagation(); 1015 | }); 1016 | $(".g-header").on('scroll', function (e) { 1017 | console.log("g-header"); 1018 | e.preventDefault(); 1019 | e.stopPropagation(); 1020 | var navIndex = Dom.navBox.find(".nav.active").index(); 1021 | Dom.sliderWrap.eq(navIndex).trigger('scroll'); 1022 | }); 1023 | 1024 | 1025 | } 1026 | main(); 1027 | 1028 | }); -------------------------------------------------------------------------------- /lib/iscroll.js: -------------------------------------------------------------------------------- 1 | /*! iScroll v5.2.0 ~ (c) 2008-2016 Matteo Spinelli ~ http://cubiq.org/license */ 2 | (function (window, document, Math) { 3 | var rAF = window.requestAnimationFrame || 4 | window.webkitRequestAnimationFrame || 5 | window.mozRequestAnimationFrame || 6 | window.oRequestAnimationFrame || 7 | window.msRequestAnimationFrame || 8 | function (callback) { window.setTimeout(callback, 1000 / 60); }; 9 | 10 | var utils = (function () { 11 | var me = {}; 12 | 13 | var _elementStyle = document.createElement('div').style; 14 | var _vendor = (function () { 15 | var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'], 16 | transform, 17 | i = 0, 18 | l = vendors.length; 19 | 20 | for ( ; i < l; i++ ) { 21 | transform = vendors[i] + 'ransform'; 22 | if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1); 23 | } 24 | 25 | return false; 26 | })(); 27 | 28 | function _prefixStyle (style) { 29 | if ( _vendor === false ) return false; 30 | if ( _vendor === '' ) return style; 31 | return _vendor + style.charAt(0).toUpperCase() + style.substr(1); 32 | } 33 | 34 | me.getTime = Date.now || function getTime () { return new Date().getTime(); }; 35 | 36 | me.extend = function (target, obj) { 37 | for ( var i in obj ) { 38 | target[i] = obj[i]; 39 | } 40 | }; 41 | 42 | me.addEvent = function (el, type, fn, capture) { 43 | el.addEventListener(type, fn, !!capture); 44 | }; 45 | 46 | me.removeEvent = function (el, type, fn, capture) { 47 | el.removeEventListener(type, fn, !!capture); 48 | }; 49 | 50 | me.prefixPointerEvent = function (pointerEvent) { 51 | return window.MSPointerEvent ? 52 | 'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8): 53 | pointerEvent; 54 | }; 55 | 56 | me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) { 57 | var distance = current - start, 58 | speed = Math.abs(distance) / time, 59 | destination, 60 | duration; 61 | 62 | deceleration = deceleration === undefined ? 0.0006 : deceleration; 63 | 64 | destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 ); 65 | duration = speed / deceleration; 66 | 67 | if ( destination < lowerMargin ) { 68 | destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin; 69 | distance = Math.abs(destination - current); 70 | duration = distance / speed; 71 | } else if ( destination > 0 ) { 72 | destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0; 73 | distance = Math.abs(current) + destination; 74 | duration = distance / speed; 75 | } 76 | 77 | return { 78 | destination: Math.round(destination), 79 | duration: duration 80 | }; 81 | }; 82 | 83 | var _transform = _prefixStyle('transform'); 84 | 85 | me.extend(me, { 86 | hasTransform: _transform !== false, 87 | hasPerspective: _prefixStyle('perspective') in _elementStyle, 88 | hasTouch: 'ontouchstart' in window, 89 | hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed 90 | hasTransition: _prefixStyle('transition') in _elementStyle 91 | }); 92 | 93 | /* 94 | This should find all Android browsers lower than build 535.19 (both stock browser and webview) 95 | - galaxy S2 is ok 96 | - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1` 97 | - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30` 98 | - galaxy S3 is badAndroid (stock brower, webview) 99 | `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30` 100 | - galaxy S4 is badAndroid (stock brower, webview) 101 | `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30` 102 | - galaxy S5 is OK 103 | `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)` 104 | - galaxy S6 is OK 105 | `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)` 106 | */ 107 | me.isBadAndroid = (function() { 108 | var appVersion = window.navigator.appVersion; 109 | // Android browser is not a chrome browser. 110 | if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) { 111 | var safariVersion = appVersion.match(/Safari\/(\d+.\d)/); 112 | if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) { 113 | return parseFloat(safariVersion[1]) < 535.19; 114 | } else { 115 | return true; 116 | } 117 | } else { 118 | return false; 119 | } 120 | })(); 121 | 122 | me.extend(me.style = {}, { 123 | transform: _transform, 124 | transitionTimingFunction: _prefixStyle('transitionTimingFunction'), 125 | transitionDuration: _prefixStyle('transitionDuration'), 126 | transitionDelay: _prefixStyle('transitionDelay'), 127 | transformOrigin: _prefixStyle('transformOrigin') 128 | }); 129 | 130 | me.hasClass = function (e, c) { 131 | var re = new RegExp("(^|\\s)" + c + "(\\s|$)"); 132 | return re.test(e.className); 133 | }; 134 | 135 | me.addClass = function (e, c) { 136 | if ( me.hasClass(e, c) ) { 137 | return; 138 | } 139 | 140 | var newclass = e.className.split(' '); 141 | newclass.push(c); 142 | e.className = newclass.join(' '); 143 | }; 144 | 145 | me.removeClass = function (e, c) { 146 | if ( !me.hasClass(e, c) ) { 147 | return; 148 | } 149 | 150 | var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g'); 151 | e.className = e.className.replace(re, ' '); 152 | }; 153 | 154 | me.offset = function (el) { 155 | var left = -el.offsetLeft, 156 | top = -el.offsetTop; 157 | 158 | // jshint -W084 159 | while (el = el.offsetParent) { 160 | left -= el.offsetLeft; 161 | top -= el.offsetTop; 162 | } 163 | // jshint +W084 164 | 165 | return { 166 | left: left, 167 | top: top 168 | }; 169 | }; 170 | 171 | me.preventDefaultException = function (el, exceptions) { 172 | for ( var i in exceptions ) { 173 | if ( exceptions[i].test(el[i]) ) { 174 | return true; 175 | } 176 | } 177 | 178 | return false; 179 | }; 180 | 181 | me.extend(me.eventType = {}, { 182 | touchstart: 1, 183 | touchmove: 1, 184 | touchend: 1, 185 | 186 | mousedown: 2, 187 | mousemove: 2, 188 | mouseup: 2, 189 | 190 | pointerdown: 3, 191 | pointermove: 3, 192 | pointerup: 3, 193 | 194 | MSPointerDown: 3, 195 | MSPointerMove: 3, 196 | MSPointerUp: 3 197 | }); 198 | 199 | me.extend(me.ease = {}, { 200 | quadratic: { 201 | style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', 202 | fn: function (k) { 203 | return k * ( 2 - k ); 204 | } 205 | }, 206 | circular: { 207 | style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1) 208 | fn: function (k) { 209 | return Math.sqrt( 1 - ( --k * k ) ); 210 | } 211 | }, 212 | back: { 213 | style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)', 214 | fn: function (k) { 215 | var b = 4; 216 | return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1; 217 | } 218 | }, 219 | bounce: { 220 | style: '', 221 | fn: function (k) { 222 | if ( ( k /= 1 ) < ( 1 / 2.75 ) ) { 223 | return 7.5625 * k * k; 224 | } else if ( k < ( 2 / 2.75 ) ) { 225 | return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; 226 | } else if ( k < ( 2.5 / 2.75 ) ) { 227 | return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; 228 | } else { 229 | return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; 230 | } 231 | } 232 | }, 233 | elastic: { 234 | style: '', 235 | fn: function (k) { 236 | var f = 0.22, 237 | e = 0.4; 238 | 239 | if ( k === 0 ) { return 0; } 240 | if ( k == 1 ) { return 1; } 241 | 242 | return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 ); 243 | } 244 | } 245 | }); 246 | 247 | me.tap = function (e, eventName) { 248 | var ev = document.createEvent('Event'); 249 | ev.initEvent(eventName, true, true); 250 | ev.pageX = e.pageX; 251 | ev.pageY = e.pageY; 252 | e.target.dispatchEvent(ev); 253 | }; 254 | 255 | me.click = function (e) { 256 | var target = e.target, 257 | ev; 258 | 259 | if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) { 260 | // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent 261 | // initMouseEvent is deprecated. 262 | ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event'); 263 | ev.initEvent('click', true, true); 264 | ev.view = e.view || window; 265 | ev.detail = 1; 266 | ev.screenX = target.screenX || 0; 267 | ev.screenY = target.screenY || 0; 268 | ev.clientX = target.clientX || 0; 269 | ev.clientY = target.clientY || 0; 270 | ev.ctrlKey = !!e.ctrlKey; 271 | ev.altKey = !!e.altKey; 272 | ev.shiftKey = !!e.shiftKey; 273 | ev.metaKey = !!e.metaKey; 274 | ev.button = 0; 275 | ev.relatedTarget = null; 276 | ev._constructed = true; 277 | target.dispatchEvent(ev); 278 | } 279 | }; 280 | 281 | return me; 282 | })(); 283 | function IScroll (el, options) { 284 | this.wrapper = typeof el == 'string' ? document.querySelector(el) : el; 285 | this.scroller = this.wrapper.children[0]; 286 | this.scrollerStyle = this.scroller.style; // cache style for better performance 287 | 288 | this.options = { 289 | 290 | resizeScrollbars: true, 291 | 292 | mouseWheelSpeed: 20, 293 | 294 | snapThreshold: 0.334, 295 | 296 | // INSERT POINT: OPTIONS 297 | disablePointer : !utils.hasPointer, 298 | disableTouch : utils.hasPointer || !utils.hasTouch, 299 | disableMouse : utils.hasPointer || utils.hasTouch, 300 | startX: 0, 301 | startY: 0, 302 | scrollY: true, 303 | directionLockThreshold: 5, 304 | momentum: true, 305 | 306 | bounce: true, 307 | bounceTime: 600, 308 | bounceEasing: '', 309 | 310 | preventDefault: true, 311 | preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ }, 312 | 313 | HWCompositing: true, 314 | useTransition: true, 315 | useTransform: true, 316 | bindToWrapper: typeof window.onmousedown === "undefined" 317 | }; 318 | 319 | for ( var i in options ) { 320 | this.options[i] = options[i]; 321 | } 322 | 323 | // Normalize options 324 | this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : ''; 325 | 326 | this.options.useTransition = utils.hasTransition && this.options.useTransition; 327 | this.options.useTransform = utils.hasTransform && this.options.useTransform; 328 | 329 | this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough; 330 | this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault; 331 | 332 | // If you want eventPassthrough I have to lock one of the axes 333 | this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY; 334 | this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX; 335 | 336 | // With eventPassthrough we also need lockDirection mechanism 337 | this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough; 338 | this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold; 339 | 340 | this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing; 341 | 342 | this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling; 343 | 344 | if ( this.options.tap === true ) { 345 | this.options.tap = 'tap'; 346 | } 347 | 348 | // https://github.com/cubiq/iscroll/issues/1029 349 | if (!this.options.useTransition && !this.options.useTransform) { 350 | if(!(/relative|absolute/i).test(this.scrollerStyle.position)) { 351 | this.scrollerStyle.position = "relative"; 352 | } 353 | } 354 | 355 | if ( this.options.shrinkScrollbars == 'scale' ) { 356 | this.options.useTransition = false; 357 | } 358 | 359 | this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1; 360 | 361 | // INSERT POINT: NORMALIZATION 362 | 363 | // Some defaults 364 | this.x = 0; 365 | this.y = 0; 366 | this.directionX = 0; 367 | this.directionY = 0; 368 | this._events = {}; 369 | 370 | // INSERT POINT: DEFAULTS 371 | 372 | this._init(); 373 | this.refresh(); 374 | 375 | this.scrollTo(this.options.startX, this.options.startY); 376 | this.enable(); 377 | } 378 | 379 | IScroll.prototype = { 380 | version: '5.2.0', 381 | 382 | _init: function () { 383 | this._initEvents(); 384 | 385 | if ( this.options.scrollbars || this.options.indicators ) { 386 | this._initIndicators(); 387 | } 388 | 389 | if ( this.options.mouseWheel ) { 390 | this._initWheel(); 391 | } 392 | 393 | if ( this.options.snap ) { 394 | this._initSnap(); 395 | } 396 | 397 | if ( this.options.keyBindings ) { 398 | this._initKeys(); 399 | } 400 | 401 | // INSERT POINT: _init 402 | 403 | }, 404 | 405 | destroy: function () { 406 | this._initEvents(true); 407 | clearTimeout(this.resizeTimeout); 408 | this.resizeTimeout = null; 409 | this._execEvent('destroy'); 410 | }, 411 | 412 | _transitionEnd: function (e) { 413 | if ( e.target != this.scroller || !this.isInTransition ) { 414 | return; 415 | } 416 | 417 | this._transitionTime(); 418 | if ( !this.resetPosition(this.options.bounceTime) ) { 419 | this.isInTransition = false; 420 | this._execEvent('scrollEnd'); 421 | } 422 | }, 423 | 424 | _start: function (e) { 425 | // React to left mouse button only 426 | if ( utils.eventType[e.type] != 1 ) { 427 | // for button property 428 | // http://unixpapa.com/js/mouse.html 429 | var button; 430 | if (!e.which) { 431 | /* IE case */ 432 | button = (e.button < 2) ? 0 : 433 | ((e.button == 4) ? 1 : 2); 434 | } else { 435 | /* All others */ 436 | button = e.button; 437 | } 438 | if ( button !== 0 ) { 439 | return; 440 | } 441 | } 442 | 443 | if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) { 444 | return; 445 | } 446 | 447 | if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) { 448 | e.preventDefault(); 449 | } 450 | 451 | var point = e.touches ? e.touches[0] : e, 452 | pos; 453 | 454 | this.initiated = utils.eventType[e.type]; 455 | this.moved = false; 456 | this.distX = 0; 457 | this.distY = 0; 458 | this.directionX = 0; 459 | this.directionY = 0; 460 | this.directionLocked = 0; 461 | 462 | this.startTime = utils.getTime(); 463 | 464 | if ( this.options.useTransition && this.isInTransition ) { 465 | this._transitionTime(); 466 | this.isInTransition = false; 467 | pos = this.getComputedPosition(); 468 | this._translate(Math.round(pos.x), Math.round(pos.y)); 469 | this._execEvent('scrollEnd'); 470 | } else if ( !this.options.useTransition && this.isAnimating ) { 471 | this.isAnimating = false; 472 | this._execEvent('scrollEnd'); 473 | } 474 | 475 | this.startX = this.x; 476 | this.startY = this.y; 477 | this.absStartX = this.x; 478 | this.absStartY = this.y; 479 | this.pointX = point.pageX; 480 | this.pointY = point.pageY; 481 | 482 | this._execEvent('beforeScrollStart'); 483 | }, 484 | 485 | _move: function (e) { 486 | if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) { 487 | return; 488 | } 489 | 490 | if ( this.options.preventDefault ) { // increases performance on Android? TODO: check! 491 | e.preventDefault(); 492 | } 493 | 494 | var point = e.touches ? e.touches[0] : e, 495 | deltaX = point.pageX - this.pointX, 496 | deltaY = point.pageY - this.pointY, 497 | timestamp = utils.getTime(), 498 | newX, newY, 499 | absDistX, absDistY; 500 | 501 | this.pointX = point.pageX; 502 | this.pointY = point.pageY; 503 | 504 | this.distX += deltaX; 505 | this.distY += deltaY; 506 | absDistX = Math.abs(this.distX); 507 | absDistY = Math.abs(this.distY); 508 | 509 | // We need to move at least 10 pixels for the scrolling to initiate 510 | if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) { 511 | return; 512 | } 513 | 514 | // If you are scrolling in one direction lock the other 515 | if ( !this.directionLocked && !this.options.freeScroll ) { 516 | if ( absDistX > absDistY + this.options.directionLockThreshold ) { 517 | this.directionLocked = 'h'; // lock horizontally 518 | } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) { 519 | this.directionLocked = 'v'; // lock vertically 520 | } else { 521 | this.directionLocked = 'n'; // no lock 522 | } 523 | } 524 | 525 | if ( this.directionLocked == 'h' ) { 526 | if ( this.options.eventPassthrough == 'vertical' ) { 527 | e.preventDefault(); 528 | } else if ( this.options.eventPassthrough == 'horizontal' ) { 529 | this.initiated = false; 530 | return; 531 | } 532 | 533 | deltaY = 0; 534 | } else if ( this.directionLocked == 'v' ) { 535 | if ( this.options.eventPassthrough == 'horizontal' ) { 536 | e.preventDefault(); 537 | } else if ( this.options.eventPassthrough == 'vertical' ) { 538 | this.initiated = false; 539 | return; 540 | } 541 | 542 | deltaX = 0; 543 | } 544 | 545 | deltaX = this.hasHorizontalScroll ? deltaX : 0; 546 | deltaY = this.hasVerticalScroll ? deltaY : 0; 547 | 548 | newX = this.x + deltaX; 549 | newY = this.y + deltaY; 550 | 551 | // Slow down if outside of the boundaries 552 | if ( newX > 0 || newX < this.maxScrollX ) { 553 | newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX; 554 | } 555 | if ( newY > 0 || newY < this.maxScrollY ) { 556 | newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY; 557 | } 558 | 559 | this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; 560 | this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; 561 | 562 | if ( !this.moved ) { 563 | this._execEvent('scrollStart'); 564 | } 565 | 566 | this.moved = true; 567 | 568 | this._translate(newX, newY); 569 | 570 | /* REPLACE START: _move */ 571 | 572 | if ( timestamp - this.startTime > 300 ) { 573 | this.startTime = timestamp; 574 | this.startX = this.x; 575 | this.startY = this.y; 576 | } 577 | 578 | /* REPLACE END: _move */ 579 | 580 | }, 581 | 582 | _end: function (e) { 583 | if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) { 584 | return; 585 | } 586 | 587 | if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) { 588 | e.preventDefault(); 589 | } 590 | 591 | var point = e.changedTouches ? e.changedTouches[0] : e, 592 | momentumX, 593 | momentumY, 594 | duration = utils.getTime() - this.startTime, 595 | newX = Math.round(this.x), 596 | newY = Math.round(this.y), 597 | distanceX = Math.abs(newX - this.startX), 598 | distanceY = Math.abs(newY - this.startY), 599 | time = 0, 600 | easing = ''; 601 | 602 | this.isInTransition = 0; 603 | this.initiated = 0; 604 | this.endTime = utils.getTime(); 605 | 606 | // reset if we are outside of the boundaries 607 | if ( this.resetPosition(this.options.bounceTime) ) { 608 | return; 609 | } 610 | 611 | this.scrollTo(newX, newY); // ensures that the last position is rounded 612 | 613 | // we scrolled less than 10 pixels 614 | if ( !this.moved ) { 615 | if ( this.options.tap ) { 616 | utils.tap(e, this.options.tap); 617 | } 618 | 619 | if ( this.options.click ) { 620 | utils.click(e); 621 | } 622 | 623 | this._execEvent('scrollCancel'); 624 | return; 625 | } 626 | 627 | if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) { 628 | this._execEvent('flick'); 629 | return; 630 | } 631 | 632 | // start momentum animation if needed 633 | if ( this.options.momentum && duration < 300 ) { 634 | momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration) : { destination: newX, duration: 0 }; 635 | momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration) : { destination: newY, duration: 0 }; 636 | newX = momentumX.destination; 637 | newY = momentumY.destination; 638 | time = Math.max(momentumX.duration, momentumY.duration); 639 | this.isInTransition = 1; 640 | } 641 | 642 | 643 | if ( this.options.snap ) { 644 | var snap = this._nearestSnap(newX, newY); 645 | this.currentPage = snap; 646 | time = this.options.snapSpeed || Math.max( 647 | Math.max( 648 | Math.min(Math.abs(newX - snap.x), 1000), 649 | Math.min(Math.abs(newY - snap.y), 1000) 650 | ), 300); 651 | newX = snap.x; 652 | newY = snap.y; 653 | 654 | this.directionX = 0; 655 | this.directionY = 0; 656 | easing = this.options.bounceEasing; 657 | } 658 | 659 | // INSERT POINT: _end 660 | 661 | if ( newX != this.x || newY != this.y ) { 662 | // change easing function when scroller goes out of the boundaries 663 | if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) { 664 | easing = utils.ease.quadratic; 665 | } 666 | 667 | this.scrollTo(newX, newY, time, easing); 668 | return; 669 | } 670 | 671 | this._execEvent('scrollEnd'); 672 | }, 673 | 674 | _resize: function () { 675 | var that = this; 676 | 677 | clearTimeout(this.resizeTimeout); 678 | 679 | this.resizeTimeout = setTimeout(function () { 680 | that.refresh(); 681 | }, this.options.resizePolling); 682 | }, 683 | 684 | resetPosition: function (time) { 685 | var x = this.x, 686 | y = this.y; 687 | 688 | time = time || 0; 689 | 690 | if ( !this.hasHorizontalScroll || this.x > 0 ) { 691 | x = 0; 692 | } else if ( this.x < this.maxScrollX ) { 693 | x = this.maxScrollX; 694 | } 695 | 696 | if ( !this.hasVerticalScroll || this.y > 0 ) { 697 | y = 0; 698 | } else if ( this.y < this.maxScrollY ) { 699 | y = this.maxScrollY; 700 | } 701 | 702 | if ( x == this.x && y == this.y ) { 703 | return false; 704 | } 705 | 706 | this.scrollTo(x, y, time, this.options.bounceEasing); 707 | 708 | return true; 709 | }, 710 | 711 | disable: function () { 712 | this.enabled = false; 713 | }, 714 | 715 | enable: function () { 716 | this.enabled = true; 717 | }, 718 | 719 | refresh: function () { 720 | var rf = this.wrapper.offsetHeight; // Force reflow 721 | 722 | this.wrapperWidth = this.wrapper.clientWidth; 723 | this.wrapperHeight = this.wrapper.clientHeight; 724 | 725 | /* REPLACE START: refresh */ 726 | 727 | this.scrollerWidth = this.scroller.offsetWidth; 728 | this.scrollerHeight = this.scroller.offsetHeight; 729 | 730 | this.maxScrollX = this.wrapperWidth - this.scrollerWidth; 731 | this.maxScrollY = this.wrapperHeight - this.scrollerHeight; 732 | 733 | /* REPLACE END: refresh */ 734 | 735 | this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0; 736 | this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0; 737 | 738 | if ( !this.hasHorizontalScroll ) { 739 | this.maxScrollX = 0; 740 | this.scrollerWidth = this.wrapperWidth; 741 | } 742 | 743 | if ( !this.hasVerticalScroll ) { 744 | this.maxScrollY = 0; 745 | this.scrollerHeight = this.wrapperHeight; 746 | } 747 | 748 | this.endTime = 0; 749 | this.directionX = 0; 750 | this.directionY = 0; 751 | 752 | this.wrapperOffset = utils.offset(this.wrapper); 753 | 754 | this._execEvent('refresh'); 755 | 756 | this.resetPosition(); 757 | 758 | // INSERT POINT: _refresh 759 | 760 | }, 761 | 762 | on: function (type, fn) { 763 | if ( !this._events[type] ) { 764 | this._events[type] = []; 765 | } 766 | 767 | this._events[type].push(fn); 768 | }, 769 | 770 | off: function (type, fn) { 771 | if ( !this._events[type] ) { 772 | return; 773 | } 774 | 775 | var index = this._events[type].indexOf(fn); 776 | 777 | if ( index > -1 ) { 778 | this._events[type].splice(index, 1); 779 | } 780 | }, 781 | 782 | _execEvent: function (type) { 783 | if ( !this._events[type] ) { 784 | return; 785 | } 786 | 787 | var i = 0, 788 | l = this._events[type].length; 789 | 790 | if ( !l ) { 791 | return; 792 | } 793 | 794 | for ( ; i < l; i++ ) { 795 | this._events[type][i].apply(this, [].slice.call(arguments, 1)); 796 | } 797 | }, 798 | 799 | scrollBy: function (x, y, time, easing) { 800 | x = this.x + x; 801 | y = this.y + y; 802 | time = time || 0; 803 | 804 | this.scrollTo(x, y, time, easing); 805 | }, 806 | 807 | scrollTo: function (x, y, time, easing) { 808 | easing = easing || utils.ease.circular; 809 | 810 | this.isInTransition = this.options.useTransition && time > 0; 811 | var transitionType = this.options.useTransition && easing.style; 812 | if ( !time || transitionType ) { 813 | if(transitionType) { 814 | this._transitionTimingFunction(easing.style); 815 | this._transitionTime(time); 816 | } 817 | this._translate(x, y); 818 | } else { 819 | this._animate(x, y, time, easing.fn); 820 | } 821 | }, 822 | 823 | scrollToElement: function (el, time, offsetX, offsetY, easing) { 824 | el = el.nodeType ? el : this.scroller.querySelector(el); 825 | 826 | if ( !el ) { 827 | return; 828 | } 829 | 830 | var pos = utils.offset(el); 831 | 832 | pos.left -= this.wrapperOffset.left; 833 | pos.top -= this.wrapperOffset.top; 834 | 835 | // if offsetX/Y are true we center the element to the screen 836 | if ( offsetX === true ) { 837 | offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2); 838 | } 839 | if ( offsetY === true ) { 840 | offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2); 841 | } 842 | 843 | pos.left -= offsetX || 0; 844 | pos.top -= offsetY || 0; 845 | 846 | pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left; 847 | pos.top = pos.top > 0 ? 0 : pos.top < this.maxScrollY ? this.maxScrollY : pos.top; 848 | 849 | time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time; 850 | 851 | this.scrollTo(pos.left, pos.top, time, easing); 852 | }, 853 | 854 | _transitionTime: function (time) { 855 | if (!this.options.useTransition) { 856 | return; 857 | } 858 | time = time || 0; 859 | var durationProp = utils.style.transitionDuration; 860 | if(!durationProp) { 861 | return; 862 | } 863 | 864 | this.scrollerStyle[durationProp] = time + 'ms'; 865 | 866 | if ( !time && utils.isBadAndroid ) { 867 | this.scrollerStyle[durationProp] = '0.0001ms'; 868 | // remove 0.0001ms 869 | var self = this; 870 | rAF(function() { 871 | if(self.scrollerStyle[durationProp] === '0.0001ms') { 872 | self.scrollerStyle[durationProp] = '0s'; 873 | } 874 | }); 875 | } 876 | 877 | 878 | if ( this.indicators ) { 879 | for ( var i = this.indicators.length; i--; ) { 880 | this.indicators[i].transitionTime(time); 881 | } 882 | } 883 | 884 | 885 | // INSERT POINT: _transitionTime 886 | 887 | }, 888 | 889 | _transitionTimingFunction: function (easing) { 890 | this.scrollerStyle[utils.style.transitionTimingFunction] = easing; 891 | 892 | 893 | if ( this.indicators ) { 894 | for ( var i = this.indicators.length; i--; ) { 895 | this.indicators[i].transitionTimingFunction(easing); 896 | } 897 | } 898 | 899 | 900 | // INSERT POINT: _transitionTimingFunction 901 | 902 | }, 903 | 904 | _translate: function (x, y) { 905 | if ( this.options.useTransform ) { 906 | 907 | /* REPLACE START: _translate */ 908 | 909 | this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ; 910 | 911 | /* REPLACE END: _translate */ 912 | 913 | } else { 914 | x = Math.round(x); 915 | y = Math.round(y); 916 | this.scrollerStyle.left = x + 'px'; 917 | this.scrollerStyle.top = y + 'px'; 918 | } 919 | 920 | this.x = x; 921 | this.y = y; 922 | 923 | 924 | if ( this.indicators ) { 925 | for ( var i = this.indicators.length; i--; ) { 926 | this.indicators[i].updatePosition(); 927 | } 928 | } 929 | 930 | 931 | // INSERT POINT: _translate 932 | 933 | }, 934 | 935 | _initEvents: function (remove) { 936 | var eventType = remove ? utils.removeEvent : utils.addEvent, 937 | target = this.options.bindToWrapper ? this.wrapper : window; 938 | 939 | eventType(window, 'orientationchange', this); 940 | eventType(window, 'resize', this); 941 | 942 | if ( this.options.click ) { 943 | eventType(this.wrapper, 'click', this, true); 944 | } 945 | 946 | if ( !this.options.disableMouse ) { 947 | eventType(this.wrapper, 'mousedown', this); 948 | eventType(target, 'mousemove', this); 949 | eventType(target, 'mousecancel', this); 950 | eventType(target, 'mouseup', this); 951 | } 952 | 953 | if ( utils.hasPointer && !this.options.disablePointer ) { 954 | eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this); 955 | eventType(target, utils.prefixPointerEvent('pointermove'), this); 956 | eventType(target, utils.prefixPointerEvent('pointercancel'), this); 957 | eventType(target, utils.prefixPointerEvent('pointerup'), this); 958 | } 959 | 960 | if ( utils.hasTouch && !this.options.disableTouch ) { 961 | eventType(this.wrapper, 'touchstart', this); 962 | eventType(target, 'touchmove', this); 963 | eventType(target, 'touchcancel', this); 964 | eventType(target, 'touchend', this); 965 | } 966 | 967 | eventType(this.scroller, 'transitionend', this); 968 | eventType(this.scroller, 'webkitTransitionEnd', this); 969 | eventType(this.scroller, 'oTransitionEnd', this); 970 | eventType(this.scroller, 'MSTransitionEnd', this); 971 | }, 972 | 973 | getComputedPosition: function () { 974 | var matrix = window.getComputedStyle(this.scroller, null), 975 | x, y; 976 | 977 | if ( this.options.useTransform ) { 978 | matrix = matrix[utils.style.transform].split(')')[0].split(', '); 979 | x = +(matrix[12] || matrix[4]); 980 | y = +(matrix[13] || matrix[5]); 981 | } else { 982 | x = +matrix.left.replace(/[^-\d.]/g, ''); 983 | y = +matrix.top.replace(/[^-\d.]/g, ''); 984 | } 985 | 986 | return { x: x, y: y }; 987 | }, 988 | _initIndicators: function () { 989 | var interactive = this.options.interactiveScrollbars, 990 | customStyle = typeof this.options.scrollbars != 'string', 991 | indicators = [], 992 | indicator; 993 | 994 | var that = this; 995 | 996 | this.indicators = []; 997 | 998 | if ( this.options.scrollbars ) { 999 | // Vertical scrollbar 1000 | if ( this.options.scrollY ) { 1001 | indicator = { 1002 | el: createDefaultScrollbar('v', interactive, this.options.scrollbars), 1003 | interactive: interactive, 1004 | defaultScrollbars: true, 1005 | customStyle: customStyle, 1006 | resize: this.options.resizeScrollbars, 1007 | shrink: this.options.shrinkScrollbars, 1008 | fade: this.options.fadeScrollbars, 1009 | listenX: false 1010 | }; 1011 | 1012 | this.wrapper.appendChild(indicator.el); 1013 | indicators.push(indicator); 1014 | } 1015 | 1016 | // Horizontal scrollbar 1017 | if ( this.options.scrollX ) { 1018 | indicator = { 1019 | el: createDefaultScrollbar('h', interactive, this.options.scrollbars), 1020 | interactive: interactive, 1021 | defaultScrollbars: true, 1022 | customStyle: customStyle, 1023 | resize: this.options.resizeScrollbars, 1024 | shrink: this.options.shrinkScrollbars, 1025 | fade: this.options.fadeScrollbars, 1026 | listenY: false 1027 | }; 1028 | 1029 | this.wrapper.appendChild(indicator.el); 1030 | indicators.push(indicator); 1031 | } 1032 | } 1033 | 1034 | if ( this.options.indicators ) { 1035 | // TODO: check concat compatibility 1036 | indicators = indicators.concat(this.options.indicators); 1037 | } 1038 | 1039 | for ( var i = indicators.length; i--; ) { 1040 | this.indicators.push( new Indicator(this, indicators[i]) ); 1041 | } 1042 | 1043 | // TODO: check if we can use array.map (wide compatibility and performance issues) 1044 | function _indicatorsMap (fn) { 1045 | if (that.indicators) { 1046 | for ( var i = that.indicators.length; i--; ) { 1047 | fn.call(that.indicators[i]); 1048 | } 1049 | } 1050 | } 1051 | 1052 | if ( this.options.fadeScrollbars ) { 1053 | this.on('scrollEnd', function () { 1054 | _indicatorsMap(function () { 1055 | this.fade(); 1056 | }); 1057 | }); 1058 | 1059 | this.on('scrollCancel', function () { 1060 | _indicatorsMap(function () { 1061 | this.fade(); 1062 | }); 1063 | }); 1064 | 1065 | this.on('scrollStart', function () { 1066 | _indicatorsMap(function () { 1067 | this.fade(1); 1068 | }); 1069 | }); 1070 | 1071 | this.on('beforeScrollStart', function () { 1072 | _indicatorsMap(function () { 1073 | this.fade(1, true); 1074 | }); 1075 | }); 1076 | } 1077 | 1078 | 1079 | this.on('refresh', function () { 1080 | _indicatorsMap(function () { 1081 | this.refresh(); 1082 | }); 1083 | }); 1084 | 1085 | this.on('destroy', function () { 1086 | _indicatorsMap(function () { 1087 | this.destroy(); 1088 | }); 1089 | 1090 | delete this.indicators; 1091 | }); 1092 | }, 1093 | 1094 | _initWheel: function () { 1095 | utils.addEvent(this.wrapper, 'wheel', this); 1096 | utils.addEvent(this.wrapper, 'mousewheel', this); 1097 | utils.addEvent(this.wrapper, 'DOMMouseScroll', this); 1098 | 1099 | this.on('destroy', function () { 1100 | clearTimeout(this.wheelTimeout); 1101 | this.wheelTimeout = null; 1102 | utils.removeEvent(this.wrapper, 'wheel', this); 1103 | utils.removeEvent(this.wrapper, 'mousewheel', this); 1104 | utils.removeEvent(this.wrapper, 'DOMMouseScroll', this); 1105 | }); 1106 | }, 1107 | 1108 | _wheel: function (e) { 1109 | if ( !this.enabled ) { 1110 | return; 1111 | } 1112 | 1113 | e.preventDefault(); 1114 | 1115 | var wheelDeltaX, wheelDeltaY, 1116 | newX, newY, 1117 | that = this; 1118 | 1119 | if ( this.wheelTimeout === undefined ) { 1120 | that._execEvent('scrollStart'); 1121 | } 1122 | 1123 | // Execute the scrollEnd event after 400ms the wheel stopped scrolling 1124 | clearTimeout(this.wheelTimeout); 1125 | this.wheelTimeout = setTimeout(function () { 1126 | if(!that.options.snap) { 1127 | that._execEvent('scrollEnd'); 1128 | } 1129 | that.wheelTimeout = undefined; 1130 | }, 400); 1131 | 1132 | if ( 'deltaX' in e ) { 1133 | if (e.deltaMode === 1) { 1134 | wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed; 1135 | wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed; 1136 | } else { 1137 | wheelDeltaX = -e.deltaX; 1138 | wheelDeltaY = -e.deltaY; 1139 | } 1140 | } else if ( 'wheelDeltaX' in e ) { 1141 | wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed; 1142 | wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed; 1143 | } else if ( 'wheelDelta' in e ) { 1144 | wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed; 1145 | } else if ( 'detail' in e ) { 1146 | wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed; 1147 | } else { 1148 | return; 1149 | } 1150 | 1151 | wheelDeltaX *= this.options.invertWheelDirection; 1152 | wheelDeltaY *= this.options.invertWheelDirection; 1153 | 1154 | if ( !this.hasVerticalScroll ) { 1155 | wheelDeltaX = wheelDeltaY; 1156 | wheelDeltaY = 0; 1157 | } 1158 | 1159 | if ( this.options.snap ) { 1160 | newX = this.currentPage.pageX; 1161 | newY = this.currentPage.pageY; 1162 | 1163 | if ( wheelDeltaX > 0 ) { 1164 | newX--; 1165 | } else if ( wheelDeltaX < 0 ) { 1166 | newX++; 1167 | } 1168 | 1169 | if ( wheelDeltaY > 0 ) { 1170 | newY--; 1171 | } else if ( wheelDeltaY < 0 ) { 1172 | newY++; 1173 | } 1174 | 1175 | this.goToPage(newX, newY); 1176 | 1177 | return; 1178 | } 1179 | 1180 | newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0); 1181 | newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0); 1182 | 1183 | this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0; 1184 | this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0; 1185 | 1186 | if ( newX > 0 ) { 1187 | newX = 0; 1188 | } else if ( newX < this.maxScrollX ) { 1189 | newX = this.maxScrollX; 1190 | } 1191 | 1192 | if ( newY > 0 ) { 1193 | newY = 0; 1194 | } else if ( newY < this.maxScrollY ) { 1195 | newY = this.maxScrollY; 1196 | } 1197 | 1198 | this.scrollTo(newX, newY, 0); 1199 | 1200 | // INSERT POINT: _wheel 1201 | }, 1202 | 1203 | _initSnap: function () { 1204 | this.currentPage = {}; 1205 | 1206 | if ( typeof this.options.snap == 'string' ) { 1207 | this.options.snap = this.scroller.querySelectorAll(this.options.snap); 1208 | } 1209 | 1210 | this.on('refresh', function () { 1211 | var i = 0, l, 1212 | m = 0, n, 1213 | cx, cy, 1214 | x = 0, y, 1215 | stepX = this.options.snapStepX || this.wrapperWidth, 1216 | stepY = this.options.snapStepY || this.wrapperHeight, 1217 | el; 1218 | 1219 | this.pages = []; 1220 | 1221 | if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) { 1222 | return; 1223 | } 1224 | 1225 | if ( this.options.snap === true ) { 1226 | cx = Math.round( stepX / 2 ); 1227 | cy = Math.round( stepY / 2 ); 1228 | 1229 | while ( x > -this.scrollerWidth ) { 1230 | this.pages[i] = []; 1231 | l = 0; 1232 | y = 0; 1233 | 1234 | while ( y > -this.scrollerHeight ) { 1235 | this.pages[i][l] = { 1236 | x: Math.max(x, this.maxScrollX), 1237 | y: Math.max(y, this.maxScrollY), 1238 | width: stepX, 1239 | height: stepY, 1240 | cx: x - cx, 1241 | cy: y - cy 1242 | }; 1243 | 1244 | y -= stepY; 1245 | l++; 1246 | } 1247 | 1248 | x -= stepX; 1249 | i++; 1250 | } 1251 | } else { 1252 | el = this.options.snap; 1253 | l = el.length; 1254 | n = -1; 1255 | 1256 | for ( ; i < l; i++ ) { 1257 | if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) { 1258 | m = 0; 1259 | n++; 1260 | } 1261 | 1262 | if ( !this.pages[m] ) { 1263 | this.pages[m] = []; 1264 | } 1265 | 1266 | x = Math.max(-el[i].offsetLeft, this.maxScrollX); 1267 | y = Math.max(-el[i].offsetTop, this.maxScrollY); 1268 | cx = x - Math.round(el[i].offsetWidth / 2); 1269 | cy = y - Math.round(el[i].offsetHeight / 2); 1270 | 1271 | this.pages[m][n] = { 1272 | x: x, 1273 | y: y, 1274 | width: el[i].offsetWidth, 1275 | height: el[i].offsetHeight, 1276 | cx: cx, 1277 | cy: cy 1278 | }; 1279 | 1280 | if ( x > this.maxScrollX ) { 1281 | m++; 1282 | } 1283 | } 1284 | } 1285 | 1286 | this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0); 1287 | 1288 | // Update snap threshold if needed 1289 | if ( this.options.snapThreshold % 1 === 0 ) { 1290 | this.snapThresholdX = this.options.snapThreshold; 1291 | this.snapThresholdY = this.options.snapThreshold; 1292 | } else { 1293 | this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold); 1294 | this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold); 1295 | } 1296 | }); 1297 | 1298 | this.on('flick', function () { 1299 | var time = this.options.snapSpeed || Math.max( 1300 | Math.max( 1301 | Math.min(Math.abs(this.x - this.startX), 1000), 1302 | Math.min(Math.abs(this.y - this.startY), 1000) 1303 | ), 300); 1304 | 1305 | this.goToPage( 1306 | this.currentPage.pageX + this.directionX, 1307 | this.currentPage.pageY + this.directionY, 1308 | time 1309 | ); 1310 | }); 1311 | }, 1312 | 1313 | _nearestSnap: function (x, y) { 1314 | if ( !this.pages.length ) { 1315 | return { x: 0, y: 0, pageX: 0, pageY: 0 }; 1316 | } 1317 | 1318 | var i = 0, 1319 | l = this.pages.length, 1320 | m = 0; 1321 | 1322 | // Check if we exceeded the snap threshold 1323 | if ( Math.abs(x - this.absStartX) < this.snapThresholdX && 1324 | Math.abs(y - this.absStartY) < this.snapThresholdY ) { 1325 | return this.currentPage; 1326 | } 1327 | 1328 | if ( x > 0 ) { 1329 | x = 0; 1330 | } else if ( x < this.maxScrollX ) { 1331 | x = this.maxScrollX; 1332 | } 1333 | 1334 | if ( y > 0 ) { 1335 | y = 0; 1336 | } else if ( y < this.maxScrollY ) { 1337 | y = this.maxScrollY; 1338 | } 1339 | 1340 | for ( ; i < l; i++ ) { 1341 | if ( x >= this.pages[i][0].cx ) { 1342 | x = this.pages[i][0].x; 1343 | break; 1344 | } 1345 | } 1346 | 1347 | l = this.pages[i].length; 1348 | 1349 | for ( ; m < l; m++ ) { 1350 | if ( y >= this.pages[0][m].cy ) { 1351 | y = this.pages[0][m].y; 1352 | break; 1353 | } 1354 | } 1355 | 1356 | if ( i == this.currentPage.pageX ) { 1357 | i += this.directionX; 1358 | 1359 | if ( i < 0 ) { 1360 | i = 0; 1361 | } else if ( i >= this.pages.length ) { 1362 | i = this.pages.length - 1; 1363 | } 1364 | 1365 | x = this.pages[i][0].x; 1366 | } 1367 | 1368 | if ( m == this.currentPage.pageY ) { 1369 | m += this.directionY; 1370 | 1371 | if ( m < 0 ) { 1372 | m = 0; 1373 | } else if ( m >= this.pages[0].length ) { 1374 | m = this.pages[0].length - 1; 1375 | } 1376 | 1377 | y = this.pages[0][m].y; 1378 | } 1379 | 1380 | return { 1381 | x: x, 1382 | y: y, 1383 | pageX: i, 1384 | pageY: m 1385 | }; 1386 | }, 1387 | 1388 | goToPage: function (x, y, time, easing) { 1389 | easing = easing || this.options.bounceEasing; 1390 | 1391 | if ( x >= this.pages.length ) { 1392 | x = this.pages.length - 1; 1393 | } else if ( x < 0 ) { 1394 | x = 0; 1395 | } 1396 | 1397 | if ( y >= this.pages[x].length ) { 1398 | y = this.pages[x].length - 1; 1399 | } else if ( y < 0 ) { 1400 | y = 0; 1401 | } 1402 | 1403 | var posX = this.pages[x][y].x, 1404 | posY = this.pages[x][y].y; 1405 | 1406 | time = time === undefined ? this.options.snapSpeed || Math.max( 1407 | Math.max( 1408 | Math.min(Math.abs(posX - this.x), 1000), 1409 | Math.min(Math.abs(posY - this.y), 1000) 1410 | ), 300) : time; 1411 | 1412 | this.currentPage = { 1413 | x: posX, 1414 | y: posY, 1415 | pageX: x, 1416 | pageY: y 1417 | }; 1418 | 1419 | this.scrollTo(posX, posY, time, easing); 1420 | }, 1421 | 1422 | next: function (time, easing) { 1423 | var x = this.currentPage.pageX, 1424 | y = this.currentPage.pageY; 1425 | 1426 | x++; 1427 | 1428 | if ( x >= this.pages.length && this.hasVerticalScroll ) { 1429 | x = 0; 1430 | y++; 1431 | } 1432 | 1433 | this.goToPage(x, y, time, easing); 1434 | }, 1435 | 1436 | prev: function (time, easing) { 1437 | var x = this.currentPage.pageX, 1438 | y = this.currentPage.pageY; 1439 | 1440 | x--; 1441 | 1442 | if ( x < 0 && this.hasVerticalScroll ) { 1443 | x = 0; 1444 | y--; 1445 | } 1446 | 1447 | this.goToPage(x, y, time, easing); 1448 | }, 1449 | 1450 | _initKeys: function (e) { 1451 | // default key bindings 1452 | var keys = { 1453 | pageUp: 33, 1454 | pageDown: 34, 1455 | end: 35, 1456 | home: 36, 1457 | left: 37, 1458 | up: 38, 1459 | right: 39, 1460 | down: 40 1461 | }; 1462 | var i; 1463 | 1464 | // if you give me characters I give you keycode 1465 | if ( typeof this.options.keyBindings == 'object' ) { 1466 | for ( i in this.options.keyBindings ) { 1467 | if ( typeof this.options.keyBindings[i] == 'string' ) { 1468 | this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCodeAt(0); 1469 | } 1470 | } 1471 | } else { 1472 | this.options.keyBindings = {}; 1473 | } 1474 | 1475 | for ( i in keys ) { 1476 | this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i]; 1477 | } 1478 | 1479 | utils.addEvent(window, 'keydown', this); 1480 | 1481 | this.on('destroy', function () { 1482 | utils.removeEvent(window, 'keydown', this); 1483 | }); 1484 | }, 1485 | 1486 | _key: function (e) { 1487 | if ( !this.enabled ) { 1488 | return; 1489 | } 1490 | 1491 | var snap = this.options.snap, // we are using this alot, better to cache it 1492 | newX = snap ? this.currentPage.pageX : this.x, 1493 | newY = snap ? this.currentPage.pageY : this.y, 1494 | now = utils.getTime(), 1495 | prevTime = this.keyTime || 0, 1496 | acceleration = 0.250, 1497 | pos; 1498 | 1499 | if ( this.options.useTransition && this.isInTransition ) { 1500 | pos = this.getComputedPosition(); 1501 | 1502 | this._translate(Math.round(pos.x), Math.round(pos.y)); 1503 | this.isInTransition = false; 1504 | } 1505 | 1506 | this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0; 1507 | 1508 | switch ( e.keyCode ) { 1509 | case this.options.keyBindings.pageUp: 1510 | if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) { 1511 | newX += snap ? 1 : this.wrapperWidth; 1512 | } else { 1513 | newY += snap ? 1 : this.wrapperHeight; 1514 | } 1515 | break; 1516 | case this.options.keyBindings.pageDown: 1517 | if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) { 1518 | newX -= snap ? 1 : this.wrapperWidth; 1519 | } else { 1520 | newY -= snap ? 1 : this.wrapperHeight; 1521 | } 1522 | break; 1523 | case this.options.keyBindings.end: 1524 | newX = snap ? this.pages.length-1 : this.maxScrollX; 1525 | newY = snap ? this.pages[0].length-1 : this.maxScrollY; 1526 | break; 1527 | case this.options.keyBindings.home: 1528 | newX = 0; 1529 | newY = 0; 1530 | break; 1531 | case this.options.keyBindings.left: 1532 | newX += snap ? -1 : 5 + this.keyAcceleration>>0; 1533 | break; 1534 | case this.options.keyBindings.up: 1535 | newY += snap ? 1 : 5 + this.keyAcceleration>>0; 1536 | break; 1537 | case this.options.keyBindings.right: 1538 | newX -= snap ? -1 : 5 + this.keyAcceleration>>0; 1539 | break; 1540 | case this.options.keyBindings.down: 1541 | newY -= snap ? 1 : 5 + this.keyAcceleration>>0; 1542 | break; 1543 | default: 1544 | return; 1545 | } 1546 | 1547 | if ( snap ) { 1548 | this.goToPage(newX, newY); 1549 | return; 1550 | } 1551 | 1552 | if ( newX > 0 ) { 1553 | newX = 0; 1554 | this.keyAcceleration = 0; 1555 | } else if ( newX < this.maxScrollX ) { 1556 | newX = this.maxScrollX; 1557 | this.keyAcceleration = 0; 1558 | } 1559 | 1560 | if ( newY > 0 ) { 1561 | newY = 0; 1562 | this.keyAcceleration = 0; 1563 | } else if ( newY < this.maxScrollY ) { 1564 | newY = this.maxScrollY; 1565 | this.keyAcceleration = 0; 1566 | } 1567 | 1568 | this.scrollTo(newX, newY, 0); 1569 | 1570 | this.keyTime = now; 1571 | }, 1572 | 1573 | _animate: function (destX, destY, duration, easingFn) { 1574 | var that = this, 1575 | startX = this.x, 1576 | startY = this.y, 1577 | startTime = utils.getTime(), 1578 | destTime = startTime + duration; 1579 | 1580 | function step () { 1581 | var now = utils.getTime(), 1582 | newX, newY, 1583 | easing; 1584 | 1585 | if ( now >= destTime ) { 1586 | that.isAnimating = false; 1587 | that._translate(destX, destY); 1588 | 1589 | if ( !that.resetPosition(that.options.bounceTime) ) { 1590 | that._execEvent('scrollEnd'); 1591 | } 1592 | 1593 | return; 1594 | } 1595 | 1596 | now = ( now - startTime ) / duration; 1597 | easing = easingFn(now); 1598 | newX = ( destX - startX ) * easing + startX; 1599 | newY = ( destY - startY ) * easing + startY; 1600 | that._translate(newX, newY); 1601 | 1602 | if ( that.isAnimating ) { 1603 | rAF(step); 1604 | } 1605 | } 1606 | 1607 | this.isAnimating = true; 1608 | step(); 1609 | }, 1610 | handleEvent: function (e) { 1611 | switch ( e.type ) { 1612 | case 'touchstart': 1613 | case 'pointerdown': 1614 | case 'MSPointerDown': 1615 | case 'mousedown': 1616 | this._start(e); 1617 | break; 1618 | case 'touchmove': 1619 | case 'pointermove': 1620 | case 'MSPointerMove': 1621 | case 'mousemove': 1622 | this._move(e); 1623 | break; 1624 | case 'touchend': 1625 | case 'pointerup': 1626 | case 'MSPointerUp': 1627 | case 'mouseup': 1628 | case 'touchcancel': 1629 | case 'pointercancel': 1630 | case 'MSPointerCancel': 1631 | case 'mousecancel': 1632 | this._end(e); 1633 | break; 1634 | case 'orientationchange': 1635 | case 'resize': 1636 | this._resize(); 1637 | break; 1638 | case 'transitionend': 1639 | case 'webkitTransitionEnd': 1640 | case 'oTransitionEnd': 1641 | case 'MSTransitionEnd': 1642 | this._transitionEnd(e); 1643 | break; 1644 | case 'wheel': 1645 | case 'DOMMouseScroll': 1646 | case 'mousewheel': 1647 | this._wheel(e); 1648 | break; 1649 | case 'keydown': 1650 | this._key(e); 1651 | break; 1652 | case 'click': 1653 | if ( this.enabled && !e._constructed ) { 1654 | e.preventDefault(); 1655 | e.stopPropagation(); 1656 | } 1657 | break; 1658 | } 1659 | } 1660 | }; 1661 | function createDefaultScrollbar (direction, interactive, type) { 1662 | var scrollbar = document.createElement('div'), 1663 | indicator = document.createElement('div'); 1664 | 1665 | if ( type === true ) { 1666 | scrollbar.style.cssText = 'position:absolute;z-index:9999'; 1667 | indicator.style.cssText = '-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px'; 1668 | } 1669 | 1670 | indicator.className = 'iScrollIndicator'; 1671 | 1672 | if ( direction == 'h' ) { 1673 | if ( type === true ) { 1674 | scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0'; 1675 | indicator.style.height = '100%'; 1676 | } 1677 | scrollbar.className = 'iScrollHorizontalScrollbar'; 1678 | } else { 1679 | if ( type === true ) { 1680 | scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px'; 1681 | indicator.style.width = '100%'; 1682 | } 1683 | scrollbar.className = 'iScrollVerticalScrollbar'; 1684 | } 1685 | 1686 | scrollbar.style.cssText += ';overflow:hidden'; 1687 | 1688 | if ( !interactive ) { 1689 | scrollbar.style.pointerEvents = 'none'; 1690 | } 1691 | 1692 | scrollbar.appendChild(indicator); 1693 | 1694 | return scrollbar; 1695 | } 1696 | 1697 | function Indicator (scroller, options) { 1698 | this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el; 1699 | this.wrapperStyle = this.wrapper.style; 1700 | this.indicator = this.wrapper.children[0]; 1701 | this.indicatorStyle = this.indicator.style; 1702 | this.scroller = scroller; 1703 | 1704 | this.options = { 1705 | listenX: true, 1706 | listenY: true, 1707 | interactive: false, 1708 | resize: true, 1709 | defaultScrollbars: false, 1710 | shrink: false, 1711 | fade: false, 1712 | speedRatioX: 0, 1713 | speedRatioY: 0 1714 | }; 1715 | 1716 | for ( var i in options ) { 1717 | this.options[i] = options[i]; 1718 | } 1719 | 1720 | this.sizeRatioX = 1; 1721 | this.sizeRatioY = 1; 1722 | this.maxPosX = 0; 1723 | this.maxPosY = 0; 1724 | 1725 | if ( this.options.interactive ) { 1726 | if ( !this.options.disableTouch ) { 1727 | utils.addEvent(this.indicator, 'touchstart', this); 1728 | utils.addEvent(window, 'touchend', this); 1729 | } 1730 | if ( !this.options.disablePointer ) { 1731 | utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this); 1732 | utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this); 1733 | } 1734 | if ( !this.options.disableMouse ) { 1735 | utils.addEvent(this.indicator, 'mousedown', this); 1736 | utils.addEvent(window, 'mouseup', this); 1737 | } 1738 | } 1739 | 1740 | if ( this.options.fade ) { 1741 | this.wrapperStyle[utils.style.transform] = this.scroller.translateZ; 1742 | var durationProp = utils.style.transitionDuration; 1743 | if(!durationProp) { 1744 | return; 1745 | } 1746 | this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms'; 1747 | // remove 0.0001ms 1748 | var self = this; 1749 | if(utils.isBadAndroid) { 1750 | rAF(function() { 1751 | if(self.wrapperStyle[durationProp] === '0.0001ms') { 1752 | self.wrapperStyle[durationProp] = '0s'; 1753 | } 1754 | }); 1755 | } 1756 | this.wrapperStyle.opacity = '0'; 1757 | } 1758 | } 1759 | 1760 | Indicator.prototype = { 1761 | handleEvent: function (e) { 1762 | switch ( e.type ) { 1763 | case 'touchstart': 1764 | case 'pointerdown': 1765 | case 'MSPointerDown': 1766 | case 'mousedown': 1767 | this._start(e); 1768 | break; 1769 | case 'touchmove': 1770 | case 'pointermove': 1771 | case 'MSPointerMove': 1772 | case 'mousemove': 1773 | this._move(e); 1774 | break; 1775 | case 'touchend': 1776 | case 'pointerup': 1777 | case 'MSPointerUp': 1778 | case 'mouseup': 1779 | case 'touchcancel': 1780 | case 'pointercancel': 1781 | case 'MSPointerCancel': 1782 | case 'mousecancel': 1783 | this._end(e); 1784 | break; 1785 | } 1786 | }, 1787 | 1788 | destroy: function () { 1789 | if ( this.options.fadeScrollbars ) { 1790 | clearTimeout(this.fadeTimeout); 1791 | this.fadeTimeout = null; 1792 | } 1793 | if ( this.options.interactive ) { 1794 | utils.removeEvent(this.indicator, 'touchstart', this); 1795 | utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this); 1796 | utils.removeEvent(this.indicator, 'mousedown', this); 1797 | 1798 | utils.removeEvent(window, 'touchmove', this); 1799 | utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this); 1800 | utils.removeEvent(window, 'mousemove', this); 1801 | 1802 | utils.removeEvent(window, 'touchend', this); 1803 | utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this); 1804 | utils.removeEvent(window, 'mouseup', this); 1805 | } 1806 | 1807 | if ( this.options.defaultScrollbars ) { 1808 | this.wrapper.parentNode.removeChild(this.wrapper); 1809 | } 1810 | }, 1811 | 1812 | _start: function (e) { 1813 | var point = e.touches ? e.touches[0] : e; 1814 | 1815 | e.preventDefault(); 1816 | e.stopPropagation(); 1817 | 1818 | this.transitionTime(); 1819 | 1820 | this.initiated = true; 1821 | this.moved = false; 1822 | this.lastPointX = point.pageX; 1823 | this.lastPointY = point.pageY; 1824 | 1825 | this.startTime = utils.getTime(); 1826 | 1827 | if ( !this.options.disableTouch ) { 1828 | utils.addEvent(window, 'touchmove', this); 1829 | } 1830 | if ( !this.options.disablePointer ) { 1831 | utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this); 1832 | } 1833 | if ( !this.options.disableMouse ) { 1834 | utils.addEvent(window, 'mousemove', this); 1835 | } 1836 | 1837 | this.scroller._execEvent('beforeScrollStart'); 1838 | }, 1839 | 1840 | _move: function (e) { 1841 | var point = e.touches ? e.touches[0] : e, 1842 | deltaX, deltaY, 1843 | newX, newY, 1844 | timestamp = utils.getTime(); 1845 | 1846 | if ( !this.moved ) { 1847 | this.scroller._execEvent('scrollStart'); 1848 | } 1849 | 1850 | this.moved = true; 1851 | 1852 | deltaX = point.pageX - this.lastPointX; 1853 | this.lastPointX = point.pageX; 1854 | 1855 | deltaY = point.pageY - this.lastPointY; 1856 | this.lastPointY = point.pageY; 1857 | 1858 | newX = this.x + deltaX; 1859 | newY = this.y + deltaY; 1860 | 1861 | this._pos(newX, newY); 1862 | 1863 | // INSERT POINT: indicator._move 1864 | 1865 | e.preventDefault(); 1866 | e.stopPropagation(); 1867 | }, 1868 | 1869 | _end: function (e) { 1870 | if ( !this.initiated ) { 1871 | return; 1872 | } 1873 | 1874 | this.initiated = false; 1875 | 1876 | e.preventDefault(); 1877 | e.stopPropagation(); 1878 | 1879 | utils.removeEvent(window, 'touchmove', this); 1880 | utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this); 1881 | utils.removeEvent(window, 'mousemove', this); 1882 | 1883 | if ( this.scroller.options.snap ) { 1884 | var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y); 1885 | 1886 | var time = this.options.snapSpeed || Math.max( 1887 | Math.max( 1888 | Math.min(Math.abs(this.scroller.x - snap.x), 1000), 1889 | Math.min(Math.abs(this.scroller.y - snap.y), 1000) 1890 | ), 300); 1891 | 1892 | if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) { 1893 | this.scroller.directionX = 0; 1894 | this.scroller.directionY = 0; 1895 | this.scroller.currentPage = snap; 1896 | this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing); 1897 | } 1898 | } 1899 | 1900 | if ( this.moved ) { 1901 | this.scroller._execEvent('scrollEnd'); 1902 | } 1903 | }, 1904 | 1905 | transitionTime: function (time) { 1906 | time = time || 0; 1907 | var durationProp = utils.style.transitionDuration; 1908 | if(!durationProp) { 1909 | return; 1910 | } 1911 | 1912 | this.indicatorStyle[durationProp] = time + 'ms'; 1913 | 1914 | if ( !time && utils.isBadAndroid ) { 1915 | this.indicatorStyle[durationProp] = '0.0001ms'; 1916 | // remove 0.0001ms 1917 | var self = this; 1918 | rAF(function() { 1919 | if(self.indicatorStyle[durationProp] === '0.0001ms') { 1920 | self.indicatorStyle[durationProp] = '0s'; 1921 | } 1922 | }); 1923 | } 1924 | }, 1925 | 1926 | transitionTimingFunction: function (easing) { 1927 | this.indicatorStyle[utils.style.transitionTimingFunction] = easing; 1928 | }, 1929 | 1930 | refresh: function () { 1931 | this.transitionTime(); 1932 | 1933 | if ( this.options.listenX && !this.options.listenY ) { 1934 | this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none'; 1935 | } else if ( this.options.listenY && !this.options.listenX ) { 1936 | this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none'; 1937 | } else { 1938 | this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none'; 1939 | } 1940 | 1941 | if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) { 1942 | utils.addClass(this.wrapper, 'iScrollBothScrollbars'); 1943 | utils.removeClass(this.wrapper, 'iScrollLoneScrollbar'); 1944 | 1945 | if ( this.options.defaultScrollbars && this.options.customStyle ) { 1946 | if ( this.options.listenX ) { 1947 | this.wrapper.style.right = '8px'; 1948 | } else { 1949 | this.wrapper.style.bottom = '8px'; 1950 | } 1951 | } 1952 | } else { 1953 | utils.removeClass(this.wrapper, 'iScrollBothScrollbars'); 1954 | utils.addClass(this.wrapper, 'iScrollLoneScrollbar'); 1955 | 1956 | if ( this.options.defaultScrollbars && this.options.customStyle ) { 1957 | if ( this.options.listenX ) { 1958 | this.wrapper.style.right = '2px'; 1959 | } else { 1960 | this.wrapper.style.bottom = '2px'; 1961 | } 1962 | } 1963 | } 1964 | 1965 | var r = this.wrapper.offsetHeight; // force refresh 1966 | 1967 | if ( this.options.listenX ) { 1968 | this.wrapperWidth = this.wrapper.clientWidth; 1969 | if ( this.options.resize ) { 1970 | this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8); 1971 | this.indicatorStyle.width = this.indicatorWidth + 'px'; 1972 | } else { 1973 | this.indicatorWidth = this.indicator.clientWidth; 1974 | } 1975 | 1976 | this.maxPosX = this.wrapperWidth - this.indicatorWidth; 1977 | 1978 | if ( this.options.shrink == 'clip' ) { 1979 | this.minBoundaryX = -this.indicatorWidth + 8; 1980 | this.maxBoundaryX = this.wrapperWidth - 8; 1981 | } else { 1982 | this.minBoundaryX = 0; 1983 | this.maxBoundaryX = this.maxPosX; 1984 | } 1985 | 1986 | this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX)); 1987 | } 1988 | 1989 | if ( this.options.listenY ) { 1990 | this.wrapperHeight = this.wrapper.clientHeight; 1991 | if ( this.options.resize ) { 1992 | this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8); 1993 | this.indicatorStyle.height = this.indicatorHeight + 'px'; 1994 | } else { 1995 | this.indicatorHeight = this.indicator.clientHeight; 1996 | } 1997 | 1998 | this.maxPosY = this.wrapperHeight - this.indicatorHeight; 1999 | 2000 | if ( this.options.shrink == 'clip' ) { 2001 | this.minBoundaryY = -this.indicatorHeight + 8; 2002 | this.maxBoundaryY = this.wrapperHeight - 8; 2003 | } else { 2004 | this.minBoundaryY = 0; 2005 | this.maxBoundaryY = this.maxPosY; 2006 | } 2007 | 2008 | this.maxPosY = this.wrapperHeight - this.indicatorHeight; 2009 | this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY)); 2010 | } 2011 | 2012 | this.updatePosition(); 2013 | }, 2014 | 2015 | updatePosition: function () { 2016 | var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0, 2017 | y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0; 2018 | 2019 | if ( !this.options.ignoreBoundaries ) { 2020 | if ( x < this.minBoundaryX ) { 2021 | if ( this.options.shrink == 'scale' ) { 2022 | this.width = Math.max(this.indicatorWidth + x, 8); 2023 | this.indicatorStyle.width = this.width + 'px'; 2024 | } 2025 | x = this.minBoundaryX; 2026 | } else if ( x > this.maxBoundaryX ) { 2027 | if ( this.options.shrink == 'scale' ) { 2028 | this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8); 2029 | this.indicatorStyle.width = this.width + 'px'; 2030 | x = this.maxPosX + this.indicatorWidth - this.width; 2031 | } else { 2032 | x = this.maxBoundaryX; 2033 | } 2034 | } else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) { 2035 | this.width = this.indicatorWidth; 2036 | this.indicatorStyle.width = this.width + 'px'; 2037 | } 2038 | 2039 | if ( y < this.minBoundaryY ) { 2040 | if ( this.options.shrink == 'scale' ) { 2041 | this.height = Math.max(this.indicatorHeight + y * 3, 8); 2042 | this.indicatorStyle.height = this.height + 'px'; 2043 | } 2044 | y = this.minBoundaryY; 2045 | } else if ( y > this.maxBoundaryY ) { 2046 | if ( this.options.shrink == 'scale' ) { 2047 | this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8); 2048 | this.indicatorStyle.height = this.height + 'px'; 2049 | y = this.maxPosY + this.indicatorHeight - this.height; 2050 | } else { 2051 | y = this.maxBoundaryY; 2052 | } 2053 | } else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) { 2054 | this.height = this.indicatorHeight; 2055 | this.indicatorStyle.height = this.height + 'px'; 2056 | } 2057 | } 2058 | 2059 | this.x = x; 2060 | this.y = y; 2061 | 2062 | if ( this.scroller.options.useTransform ) { 2063 | this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ; 2064 | } else { 2065 | this.indicatorStyle.left = x + 'px'; 2066 | this.indicatorStyle.top = y + 'px'; 2067 | } 2068 | }, 2069 | 2070 | _pos: function (x, y) { 2071 | if ( x < 0 ) { 2072 | x = 0; 2073 | } else if ( x > this.maxPosX ) { 2074 | x = this.maxPosX; 2075 | } 2076 | 2077 | if ( y < 0 ) { 2078 | y = 0; 2079 | } else if ( y > this.maxPosY ) { 2080 | y = this.maxPosY; 2081 | } 2082 | 2083 | x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x; 2084 | y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y; 2085 | 2086 | this.scroller.scrollTo(x, y); 2087 | }, 2088 | 2089 | fade: function (val, hold) { 2090 | if ( hold && !this.visible ) { 2091 | return; 2092 | } 2093 | 2094 | clearTimeout(this.fadeTimeout); 2095 | this.fadeTimeout = null; 2096 | 2097 | var time = val ? 250 : 500, 2098 | delay = val ? 0 : 300; 2099 | 2100 | val = val ? '1' : '0'; 2101 | 2102 | this.wrapperStyle[utils.style.transitionDuration] = time + 'ms'; 2103 | 2104 | this.fadeTimeout = setTimeout((function (val) { 2105 | this.wrapperStyle.opacity = val; 2106 | this.visible = +val; 2107 | }).bind(this, val), delay); 2108 | } 2109 | }; 2110 | 2111 | IScroll.utils = utils; 2112 | 2113 | if ( typeof module != 'undefined' && module.exports ) { 2114 | module.exports = IScroll; 2115 | } else if ( typeof define == 'function' && define.amd ) { 2116 | define( function () { return IScroll; } ); 2117 | } else { 2118 | window.IScroll = IScroll; 2119 | } 2120 | 2121 | })(window, document, Math); 2122 | --------------------------------------------------------------------------------