meplayer.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /******/ (function(modules) { // webpackBootstrap
  2. /******/ // The module cache
  3. /******/ var installedModules = {};
  4. /******/ // The require function
  5. /******/ function __webpack_require__(moduleId) {
  6. /******/ // Check if module is in cache
  7. /******/ if(installedModules[moduleId])
  8. /******/ return installedModules[moduleId].exports;
  9. /******/ // Create a new module (and put it into the cache)
  10. /******/ var module = installedModules[moduleId] = {
  11. /******/ exports: {},
  12. /******/ id: moduleId,
  13. /******/ loaded: false
  14. /******/ };
  15. /******/ // Execute the module function
  16. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  17. /******/ // Flag the module as loaded
  18. /******/ module.loaded = true;
  19. /******/ // Return the exports of the module
  20. /******/ return module.exports;
  21. /******/ }
  22. /******/ // expose the modules object (__webpack_modules__)
  23. /******/ __webpack_require__.m = modules;
  24. /******/ // expose the module cache
  25. /******/ __webpack_require__.c = installedModules;
  26. /******/ // __webpack_public_path__
  27. /******/ __webpack_require__.p = "";
  28. /******/ // Load entry module and return exports
  29. /******/ return __webpack_require__(0);
  30. /******/ })
  31. /************************************************************************/
  32. /******/ ([
  33. /* 0 */
  34. /***/ function(module, exports, __webpack_require__) {
  35. 'use strict';
  36. var _constants = __webpack_require__(1);
  37. var _lyric = __webpack_require__(2);
  38. var lyric = _interopRequireWildcard(_lyric);
  39. var _utils = __webpack_require__(3);
  40. var utils = _interopRequireWildcard(_utils);
  41. var _spectrum = __webpack_require__(4);
  42. var spectrum = _interopRequireWildcard(_spectrum);
  43. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
  44. window.mePlayer = function (options) {
  45. // 检查必填选项
  46. if (!(options.music && options.music.src)) {
  47. console.error('必须指定音乐地址哦~');
  48. return;
  49. }
  50. var musicConf = options.music,
  51. target = getTarget(options.target),
  52. theme = options.theme ? options.theme : _constants.THEME_DEFAULT,
  53. hasLrc = musicConf.lrc ? true : false,
  54. coverSrc = musicConf.cover ? musicConf.cover : 'https://unsplash.it/78/?random',
  55. currentThemeClass = theme === _constants.THEME_DEFAULT ? 'meplayer-container' : 'meplayer-container-mini',
  56. containerClass = currentThemeClass + ' ' + (hasLrc ? 'meplayer-haslrc' : '') + ' meplayer-isloading',
  57. playerHTMLContent = '<div class="' + containerClass + '">\n <audio src=' + musicConf.src + '></audio>\n <div class="meplayer-info">\n <div class="meplayer-info-cover"><img src=' + coverSrc + ' alt="cd-cover"></div>\n <div class="meplayer-meta">\n <div class="meplayer-meta-title">' + musicConf.title + '</div>\n <div class="meplayer-meta-author">' + musicConf.author + '</div>\n <div class="meplayer-meta-time-tick"><span class="meplayer-meta-time-tick-text"></span></div>\n </div>\n </div>\n <canvas class="meplayer-spectrum"></canvas>\n <div class="meplayer-lyric"><div class="meplayer-lyric-area"></div></div>\n <div class="meplayer-control"><div class="meplayer-control-play"><i class="icon-play"></i><i class="icon-pause"></i></div></div>\n <div class="meplayer-volume-bg"><div class="meplayer-volume"><i class="icon-volume"></i><div class="meplayer-volume-progress"></div></div></div>\n <div class="meplayer-duration"><i class="icon-clock"></i><span class="meplayer-duration-text">loading</span></div>\n <div class="meplayer-loadingsign"><i class="icon-spin animate-spin"></i>loading</div>\n <div class="meplayer-timeline-bg"><div class="meplayer-timeline"><div class="meplayer-timeline-passed"></div></div></div>\n </div>';
  58. target.innerHTML = playerHTMLContent;
  59. var meplayerContainer = target.querySelector('.' + currentThemeClass),
  60. audio = target.querySelector('audio'),
  61. playBtn = target.querySelector('.meplayer-control-play'),
  62. timeTick = target.querySelector('.meplayer-meta-time-tick-text'),
  63. timeCount = target.querySelector('.meplayer-duration'),
  64. timeLine = target.querySelector('.meplayer-timeline'),
  65. timePassed = target.querySelector('.meplayer-timeline-passed'),
  66. volumeArea = target.querySelector('.meplayer-volume'),
  67. volumeProgress = target.querySelector('.meplayer-volume-progress'),
  68. lyricArea = target.querySelector('.meplayer-lyric-area'),
  69. canvas = target.querySelector('.meplayer-spectrum'),
  70. duration;
  71. // 设置在页面加载后立即加载音频
  72. audio.preload = 'auto';
  73. if (hasLrc) {
  74. lyric.parse(musicConf.lrc);
  75. lyric.render(lyricArea);
  76. } else {
  77. // 频谱动画初始化
  78. spectrum.init(canvas);
  79. }
  80. eventInit();
  81. // 重定义meplayer
  82. window.mePlayer = {
  83. play: play,
  84. pause: pause,
  85. toggleTheme: toggleTheme
  86. };
  87. /*
  88. * 工具函数
  89. * */
  90. // 给播放器绑定各种事件
  91. function eventInit() {
  92. audio.addEventListener('ended', function () {
  93. utils.removeClass(meplayerContainer, 'meplayer-isplaying');
  94. });
  95. audio.addEventListener('canplaythrough', function () {
  96. duration = this.duration;
  97. setTimeout(function () {
  98. utils.removeClass(meplayerContainer, 'meplayer-isloading');
  99. timeCount.querySelector('.meplayer-duration-text').innerText = utils.parseSec(duration.toFixed(0));
  100. }, 1000);
  101. });
  102. audio.addEventListener('durationchange', function () {
  103. duration = this.duration;
  104. });
  105. audio.addEventListener('timeupdate', function () {
  106. var curTime = audio.currentTime.toFixed(0);
  107. var curTimeForLrc = audio.currentTime.toFixed(3);
  108. var playPercent = 100 * (curTime / duration);
  109. timePassed.style.width = playPercent.toFixed(2) + '%';
  110. timeTick.innerText = utils.parseSec(curTime);
  111. if (hasLrc && theme === _constants.THEME_DEFAULT) {
  112. var tempLrcIndex = lyric.currentIndex(curTimeForLrc);
  113. var tempLrcLines = lyricArea.querySelectorAll('p');
  114. var tempLrcLinePre = tempLrcLines[tempLrcIndex - 1];
  115. var tempLrcLine = tempLrcLines[tempLrcIndex];
  116. var tempLrcLineNext = tempLrcLines[tempLrcIndex + 1];
  117. if (!tempLrcLine.className.includes('meplayer-lyric-current')) {
  118. utils.removeClass(lyricArea.querySelector('.meplayer-lyric-current'), 'meplayer-lyric-current');
  119. if (lyricArea.querySelector('.meplayer-lyric-pre')) {
  120. utils.removeClass(lyricArea.querySelector('.meplayer-lyric-pre'), 'meplayer-lyric-pre');
  121. }
  122. if (lyricArea.querySelector('.meplayer-lyric-next')) {
  123. utils.removeClass(lyricArea.querySelector('.meplayer-lyric-next'), 'meplayer-lyric-next');
  124. }
  125. utils.addClass(tempLrcLine, 'meplayer-lyric-current');
  126. if (tempLrcLinePre) {
  127. utils.addClass(tempLrcLinePre, 'meplayer-lyric-pre');
  128. }
  129. if (tempLrcLineNext) {
  130. utils.addClass(tempLrcLineNext, 'meplayer-lyric-next');
  131. }
  132. lyricArea.style.webkitTransform = 'translateY(-' + 20 * tempLrcIndex + 'px)';
  133. lyricArea.style.transform = 'translateY(-' + 20 * tempLrcIndex + 'px)';
  134. }
  135. }
  136. });
  137. var _handleMouseWheel;
  138. playBtn.addEventListener('click', function () {
  139. if (audio.paused) {
  140. audio.play();
  141. if (theme === _constants.THEME_DEFAULT && !hasLrc) {
  142. spectrum.draw();
  143. }
  144. // 播放状态中可以用滑轮调节音量
  145. meplayerContainer.addEventListener('mousewheel', function handleMouseWheel() {
  146. var timer = null;
  147. var step = 0.05;
  148. _handleMouseWheel = function _handleMouseWheel(event) {
  149. if (timer) {
  150. clearTimeout(timer);
  151. }
  152. if (!meplayerContainer.className.includes('meplayer-adjusting-volume')) {
  153. utils.addClass(meplayerContainer, 'meplayer-adjusting-volume');
  154. }
  155. if (event.wheelDeltaY < 0 && audio.volume > step) {
  156. audio.volume -= step;
  157. }
  158. if (event.wheelDeltaY > 0 && audio.volume < 1 - step) {
  159. audio.volume += step;
  160. }
  161. if (theme === _constants.THEME_DEFAULT) {
  162. volumeProgress.style.width = audio.volume * 100 + '%';
  163. } else {
  164. volumeArea.querySelector('i').style.opacity = audio.volume;
  165. }
  166. event.preventDefault();
  167. timer = setTimeout(function () {
  168. utils.removeClass(meplayerContainer, 'meplayer-adjusting-volume');
  169. }, 1000);
  170. };
  171. return _handleMouseWheel;
  172. }());
  173. } else {
  174. audio.pause();
  175. spectrum.stop();
  176. meplayerContainer.removeEventListener('mousewheel', _handleMouseWheel);
  177. }
  178. utils.toggleClass(meplayerContainer, 'meplayer-isplaying');
  179. });
  180. timeLine.addEventListener('click', function (event) {
  181. var clickPercent = (event.pageX - utils.getAbsLeft(this)) / this.offsetWidth;
  182. timePassed.style.width = clickPercent * 100 + '%';
  183. audio.currentTime = (clickPercent * duration).toFixed(0);
  184. });
  185. }
  186. function play() {
  187. if (audio.paused) {
  188. audio.play();
  189. }
  190. }
  191. function pause() {
  192. if (!audio.paused) {
  193. audio.pause();
  194. }
  195. }
  196. function toggleTheme() {
  197. var step = 0.03;
  198. var count = 0;
  199. var maxCount = 200;
  200. utils.addClass(meplayerContainer, 'meplayer-changing-theme');
  201. theme = theme === _constants.THEME_DEFAULT ? _constants.THEME_MINI : _constants.THEME_DEFAULT;
  202. loop();
  203. function loop() {
  204. count++;
  205. meplayerContainer.style.opacity -= step;
  206. if (meplayerContainer.style.opacity <= 0) {
  207. step *= -1;
  208. meplayerContainer.style.opacity = 0;
  209. utils.toggleClass(meplayerContainer, 'meplayer-container-mini');
  210. utils.toggleClass(meplayerContainer, 'meplayer-container');
  211. }
  212. if (meplayerContainer.style.opacity < 1 && count < maxCount) {
  213. requestAnimationFrame(loop);
  214. } else {
  215. setTimeout(function () {
  216. utils.removeClass(meplayerContainer, 'meplayer-changing-theme');
  217. }, 500);
  218. }
  219. }
  220. }
  221. };
  222. function getTarget(option) {
  223. if (typeof option === 'string') {
  224. return document.querySelector(option);
  225. } else if (option.toString().includes('HTMLDivElement')) {
  226. return option;
  227. } else {
  228. return document.querySelector('.meplayer');
  229. }
  230. }
  231. /***/ },
  232. /* 1 */
  233. /***/ function(module, exports) {
  234. 'use strict';
  235. Object.defineProperty(exports, "__esModule", {
  236. value: true
  237. });
  238. /*
  239. * 全局常量声明
  240. */
  241. var THEME_DEFAULT = 'default';
  242. var THEME_MINI = 'mini';
  243. var LYRIC_CURRENT_CLASS = 'meplayer-lyric-current';
  244. var LYRIC_NEXT_CLASS = 'meplayer-lyric-next';
  245. exports.THEME_DEFAULT = THEME_DEFAULT;
  246. exports.THEME_MINI = THEME_MINI;
  247. exports.LYRIC_CURRENT_CLASS = LYRIC_CURRENT_CLASS;
  248. exports.LYRIC_NEXT_CLASS = LYRIC_NEXT_CLASS;
  249. /***/ },
  250. /* 2 */
  251. /***/ function(module, exports, __webpack_require__) {
  252. 'use strict';
  253. Object.defineProperty(exports, "__esModule", {
  254. value: true
  255. });
  256. exports.currentIndex = exports.render = exports.parse = undefined;
  257. var _constants = __webpack_require__(1);
  258. var lyrics;
  259. // 歌词解析脚本
  260. // 修改自:https://github.com/DIYgod/APlayer
  261. function parse(text) {
  262. var lyric = text.split('\n');
  263. var lrc = [];
  264. var len = lyric.length;
  265. var reg1 = /\[(\d{2}):(\d{2})\.(\d{2,3})]/g;
  266. var reg2 = /\[(\d{2}):(\d{2})\.(\d{2,3})]/;
  267. for (var i = 0; i < len; i++) {
  268. var time = lyric[i].match(reg1);
  269. var lrcText = lyric[i].replace(reg1, '').replace(/^\s+|\s+$/g, '');
  270. // 排除空行
  271. if (!lrcText) {
  272. continue;
  273. }
  274. if (time != null) {
  275. var timeLen = time.length;
  276. for (var j = 0; j < timeLen; j++) {
  277. var oneTime = reg2.exec(time[j]);
  278. var lrcTime = oneTime[1] * 60 + parseInt(oneTime[2]) + parseInt(oneTime[3]) / ((oneTime[3] + '').length === 2 ? 100 : 1000);
  279. lrc.push({
  280. time: lrcTime,
  281. text: lrcText
  282. });
  283. }
  284. }
  285. }
  286. lrc.sort(function (a, b) {
  287. return a.time - b.time;
  288. });
  289. lyrics = lrc;
  290. return lrc;
  291. }
  292. // 歌词文本解析成DOM结构
  293. function render(target) {
  294. if (!lyrics) {
  295. console.error('未指定歌词文本!');
  296. return;
  297. }
  298. var lyricHTML = '';
  299. for (var i = 0; i < lyrics.length; i++) {
  300. lyricHTML += '<p>' + lyrics[i].text + '</p>';
  301. }
  302. target.innerHTML = lyricHTML;
  303. target.querySelector('p').className = _constants.LYRIC_CURRENT_CLASS;
  304. target.querySelector('p + p').className = _constants.LYRIC_NEXT_CLASS;
  305. }
  306. function currentIndex(time) {
  307. if (time < lyrics[0].time) return 0;
  308. for (var i = 0, l = lyrics.length; i < l; i++) {
  309. if (time >= lyrics[i].time && (!lyrics[i + 1] || time <= lyrics[i + 1].time)) {
  310. break;
  311. }
  312. }
  313. return i;
  314. }
  315. exports.parse = parse;
  316. exports.render = render;
  317. exports.currentIndex = currentIndex;
  318. /***/ },
  319. /* 3 */
  320. /***/ function(module, exports) {
  321. 'use strict';
  322. Object.defineProperty(exports, "__esModule", {
  323. value: true
  324. });
  325. function toggleClass(el, className) {
  326. if (el.classList) {
  327. el.classList.toggle(className);
  328. } else {
  329. var classes = el.className.split(' ');
  330. var existingIndex = classes.indexOf(className);
  331. if (existingIndex >= 0) classes.splice(existingIndex, 1);else classes.push(className);
  332. el.className = classes.join(' ');
  333. }
  334. }
  335. function addClass(el, className) {
  336. if (el.classList) el.classList.add(className);else el.className += ' ' + className;
  337. }
  338. function removeClass(el, className) {
  339. if (el.classList) el.classList.remove(className);else el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
  340. }
  341. function getAbsLeft(el) {
  342. var left = el.offsetLeft;
  343. while (el.offsetParent) {
  344. el = el.offsetParent;
  345. left += el.offsetLeft;
  346. }
  347. return left;
  348. }
  349. function parseSec(sec) {
  350. var tempMin = (sec / 60).toFixed(0);
  351. var tempSec = (sec % 60).toFixed(0);
  352. var curMin = tempMin < 10 ? '0' + tempMin : tempMin;
  353. var curSec = tempSec < 10 ? '0' + tempSec : tempSec;
  354. return curMin + ':' + curSec;
  355. }
  356. exports.toggleClass = toggleClass;
  357. exports.addClass = addClass;
  358. exports.removeClass = removeClass;
  359. exports.getAbsLeft = getAbsLeft;
  360. exports.parseSec = parseSec;
  361. /***/ },
  362. /* 4 */
  363. /***/ function(module, exports) {
  364. 'use strict';
  365. Object.defineProperty(exports, "__esModule", {
  366. value: true
  367. });
  368. /*
  369. * 频谱动画模拟
  370. * */
  371. var canvas,
  372. ctx,
  373. specItems = [],
  374. needStop = false,
  375. timer = null,
  376. random = Math.random;
  377. function randHeight() {
  378. if (random() > 0.8) {
  379. return random() * 8 + 11;
  380. } else {
  381. return random() * 6 + 2;
  382. }
  383. }
  384. var randHeightGenerator = function randHeightGenerator(base) {
  385. var max = base * 1.5 > 28 ? 28 : base * 1.5,
  386. min = 1,
  387. direction = random() > 0.5 ? 1 : -1,
  388. tempHeight = base,
  389. curStep;
  390. return function () {
  391. curStep = direction;
  392. tempHeight += curStep;
  393. if (tempHeight >= max) {
  394. direction *= -1;
  395. tempHeight = max;
  396. } else if (tempHeight <= min) {
  397. direction *= -1;
  398. tempHeight = min;
  399. }
  400. if (random() > 0.9) {
  401. direction *= -1;
  402. }
  403. return tempHeight;
  404. };
  405. };
  406. function loop() {
  407. ctx.clearRect(0, -canvas.height / 2, canvas.width, canvas.height);
  408. for (var i = 0; i < specItems.length; i++) {
  409. var item = specItems[i];
  410. var height = item.getHeight();
  411. ctx.fillRect(i + specItems[i].xSpace, -height / 2, specItems[i].width, height);
  412. }
  413. if (!needStop) {
  414. timer = requestAnimationFrame(loop);
  415. }
  416. }
  417. function init(canvasElem) {
  418. var width = arguments.length <= 1 || arguments[1] === undefined ? 220 : arguments[1];
  419. var height = arguments.length <= 2 || arguments[2] === undefined ? 30 : arguments[2];
  420. var color = arguments.length <= 3 || arguments[3] === undefined ? '#D94240' : arguments[3];
  421. canvas = canvasElem;
  422. canvas.width = width;
  423. canvas.height = height;
  424. ctx = canvas.getContext('2d');
  425. ctx.fillStyle = color;
  426. ctx.translate(0, height / 2);
  427. for (var i = 0; i < 64; i++) {
  428. var xSpace = i == 0 ? 0 : 5 * i;
  429. var tempItem = {
  430. xSpace: xSpace,
  431. width: 1,
  432. getHeight: randHeightGenerator(randHeight())
  433. };
  434. specItems.push(tempItem);
  435. }
  436. }
  437. function draw() {
  438. needStop = false;
  439. loop();
  440. }
  441. function stop() {
  442. needStop = true;
  443. }
  444. exports.init = init;
  445. exports.draw = draw;
  446. exports.stop = stop;
  447. /***/ }
  448. /******/ ]);