main.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import {THEME_DEFAULT, THEME_MINI} from './constants';
  2. import * as lyric from './lyric';
  3. import * as utils from './utils';
  4. import * as spectrum from './spectrum';
  5. var root = typeof window == 'object' && window.window === window ? window :
  6. typeof global == 'object' && global.global === global ? global : this;
  7. root.mePlayer = function (options) {
  8. // 检查必填选项
  9. if (!(options.music && options.music.src)) {
  10. console.error('必须指定音乐地址哦~');
  11. return;
  12. }
  13. var musicConf = options.music,
  14. target = getTarget(options.target),
  15. theme = options.theme ? options.theme : THEME_DEFAULT,
  16. hasLrc = musicConf.lrc ? true : false,
  17. coverSrc = musicConf.cover ? musicConf.cover : 'https://unsplash.it/78/?random',
  18. currentThemeClass = theme === THEME_DEFAULT ? 'meplayer-container' : 'meplayer-container-mini',
  19. containerClass = `${currentThemeClass} ${hasLrc ? 'meplayer-haslrc' : ''} meplayer-isloading`,
  20. playerHTMLContent = `<div class="${containerClass}">
  21. <audio src=${musicConf.src}></audio>
  22. <div class="meplayer-info">
  23. <div class="meplayer-info-cover"><img src=${coverSrc} alt="cd-cover"></div>
  24. <div class="meplayer-meta">
  25. <div class="meplayer-meta-title">${musicConf.title}</div>
  26. <div class="meplayer-meta-author">${musicConf.author}</div>
  27. <div class="meplayer-meta-time-tick"><span class="meplayer-meta-time-tick-text"></span></div>
  28. </div>
  29. </div>
  30. <canvas class="meplayer-spectrum"></canvas>
  31. <div class="meplayer-lyric"><div class="meplayer-lyric-area"></div></div>
  32. <div class="meplayer-control"><div class="meplayer-control-play"><i class="icon-play"></i><i class="icon-pause"></i></div></div>
  33. <div class="meplayer-volume-bg"><div class="meplayer-volume"><i class="icon-volume"></i><div class="meplayer-volume-progress"></div></div></div>
  34. <div class="meplayer-duration"><i class="icon-clock"></i><span class="meplayer-duration-text">loading</span></div>
  35. <div class="meplayer-loadingsign"><i class="icon-spin animate-spin"></i>loading</div>
  36. <div class="meplayer-timeline-bg"><div class="meplayer-timeline"><div class="meplayer-timeline-passed"></div></div></div>
  37. </div>`;
  38. target.innerHTML = playerHTMLContent;
  39. var meplayerContainer = target.querySelector(`.${currentThemeClass}`),
  40. audio = target.querySelector('audio'),
  41. playBtn = target.querySelector('.meplayer-control-play'),
  42. timeTick = target.querySelector('.meplayer-meta-time-tick-text'),
  43. timeCount = target.querySelector('.meplayer-duration'),
  44. timeLine = target.querySelector('.meplayer-timeline'),
  45. timePassed = target.querySelector('.meplayer-timeline-passed'),
  46. volumeArea = target.querySelector('.meplayer-volume'),
  47. volumeProgress = target.querySelector('.meplayer-volume-progress'),
  48. lyricArea = target.querySelector('.meplayer-lyric-area'),
  49. canvas = target.querySelector('.meplayer-spectrum'),
  50. duration;
  51. // 设置在页面加载后立即加载音频
  52. audio.preload = 'auto';
  53. if (hasLrc) {
  54. lyric.parse(musicConf.lrc);
  55. lyric.render(lyricArea);
  56. } else {
  57. // 频谱动画初始化
  58. spectrum.init(canvas);
  59. }
  60. eventInit();
  61. // 重定义meplayer
  62. root.mePlayer = {
  63. play : play,
  64. pause : pause,
  65. toggleTheme: toggleTheme
  66. };
  67. /*
  68. * 工具函数
  69. * */
  70. // 给播放器绑定各种事件
  71. function eventInit() {
  72. audio.addEventListener('ended', function () {
  73. utils.removeClass(meplayerContainer, 'meplayer-isplaying');
  74. });
  75. audio.addEventListener('canplaythrough', function () {
  76. duration = this.duration;
  77. setTimeout(function () {
  78. utils.removeClass(meplayerContainer, 'meplayer-isloading');
  79. timeCount.querySelector('.meplayer-duration-text').innerText = utils.parseSec(duration.toFixed(0));
  80. }, 1000);
  81. });
  82. audio.addEventListener('durationchange', function () {
  83. duration = this.duration;
  84. });
  85. audio.addEventListener('timeupdate', function () {
  86. var curTime = (audio.currentTime).toFixed(0);
  87. var curTimeForLrc = (audio.currentTime).toFixed(3);
  88. var playPercent = 100 * (curTime / duration);
  89. timePassed.style.width = playPercent.toFixed(2) + '%';
  90. timeTick.innerText = utils.parseSec(curTime);
  91. if (hasLrc && theme === THEME_DEFAULT) {
  92. var tempLrcIndex = lyric.currentIndex(curTimeForLrc);
  93. var tempLrcLines = lyricArea.querySelectorAll('p');
  94. var tempLrcLinePre = tempLrcLines[tempLrcIndex - 1];
  95. var tempLrcLine = tempLrcLines[tempLrcIndex];
  96. var tempLrcLineNext = tempLrcLines[tempLrcIndex + 1];
  97. if (!tempLrcLine.className.includes('meplayer-lyric-current')) {
  98. utils.removeClass(lyricArea.querySelector('.meplayer-lyric-current'), 'meplayer-lyric-current');
  99. if (lyricArea.querySelector('.meplayer-lyric-pre')) {
  100. utils.removeClass(lyricArea.querySelector('.meplayer-lyric-pre'), 'meplayer-lyric-pre');
  101. }
  102. if (lyricArea.querySelector('.meplayer-lyric-next')) {
  103. utils.removeClass(lyricArea.querySelector('.meplayer-lyric-next'), 'meplayer-lyric-next');
  104. }
  105. utils.addClass(tempLrcLine, 'meplayer-lyric-current');
  106. if (tempLrcLinePre) {
  107. utils.addClass(tempLrcLinePre, 'meplayer-lyric-pre');
  108. }
  109. if (tempLrcLineNext) {
  110. utils.addClass(tempLrcLineNext, 'meplayer-lyric-next');
  111. }
  112. lyricArea.style.webkitTransform = 'translateY(-' + 20 * tempLrcIndex + 'px)';
  113. lyricArea.style.transform = 'translateY(-' + 20 * tempLrcIndex + 'px)';
  114. }
  115. }
  116. });
  117. var _handleMouseWheel;
  118. playBtn.addEventListener('click', function () {
  119. if (audio.paused) {
  120. audio.play();
  121. if (theme === THEME_DEFAULT && !hasLrc) {
  122. spectrum.draw();
  123. }
  124. // 播放状态中可以用滑轮调节音量
  125. meplayerContainer.addEventListener('mousewheel', function handleMouseWheel() {
  126. var timer = null;
  127. var step = 0.05;
  128. _handleMouseWheel = function (event) {
  129. if (timer) {
  130. clearTimeout(timer);
  131. }
  132. if (!meplayerContainer.className.includes('meplayer-adjusting-volume')) {
  133. utils.addClass(meplayerContainer, 'meplayer-adjusting-volume');
  134. }
  135. if (event.wheelDeltaY < 0 && audio.volume > step) {
  136. audio.volume -= step;
  137. }
  138. if (event.wheelDeltaY > 0 && audio.volume < 1 - step) {
  139. audio.volume += step;
  140. }
  141. if (theme === THEME_DEFAULT) {
  142. volumeProgress.style.width = audio.volume * 100 + '%';
  143. } else {
  144. volumeArea.querySelector('i').style.opacity = audio.volume;
  145. }
  146. event.preventDefault();
  147. timer = setTimeout(function () {
  148. utils.removeClass(meplayerContainer, 'meplayer-adjusting-volume');
  149. }, 1000);
  150. };
  151. return _handleMouseWheel;
  152. }());
  153. } else {
  154. audio.pause();
  155. spectrum.stop();
  156. meplayerContainer.removeEventListener('mousewheel', _handleMouseWheel);
  157. }
  158. utils.toggleClass(meplayerContainer, 'meplayer-isplaying');
  159. });
  160. timeLine.addEventListener('click', function (event) {
  161. var clickPercent = (event.pageX - utils.getAbsLeft(this)) / this.offsetWidth;
  162. timePassed.style.width = clickPercent * 100 + '%';
  163. audio.currentTime = (clickPercent * duration).toFixed(0);
  164. });
  165. }
  166. function play() {
  167. if (audio.paused) {
  168. audio.play();
  169. }
  170. }
  171. function pause() {
  172. if (!audio.paused) {
  173. audio.pause();
  174. }
  175. }
  176. function toggleTheme() {
  177. var step = 0.03;
  178. var count = 0;
  179. var maxCount = 200;
  180. utils.addClass(meplayerContainer, 'meplayer-changing-theme');
  181. theme = theme === THEME_DEFAULT ? THEME_MINI : THEME_DEFAULT;
  182. loop();
  183. function loop() {
  184. count++;
  185. meplayerContainer.style.opacity -= step;
  186. if (meplayerContainer.style.opacity <= 0) {
  187. step *= -1;
  188. meplayerContainer.style.opacity = 0;
  189. utils.toggleClass(meplayerContainer, 'meplayer-container-mini');
  190. utils.toggleClass(meplayerContainer, 'meplayer-container');
  191. }
  192. if (meplayerContainer.style.opacity < 1 && count < maxCount) {
  193. requestAnimationFrame(loop);
  194. } else {
  195. setTimeout(function () {
  196. utils.removeClass(meplayerContainer, 'meplayer-changing-theme');
  197. }, 500);
  198. }
  199. }
  200. }
  201. };
  202. if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
  203. module.exports = root.mePlayer;
  204. }
  205. function getTarget(option) {
  206. if (typeof option === 'string') {
  207. return document.querySelector(option);
  208. } else if (option.toString().includes('HTMLDivElement')) {
  209. return option;
  210. } else {
  211. return document.querySelector('.meplayer');
  212. }
  213. }