├── .gitignore
├── LICENSE
├── README.md
├── css
├── fonts
│ ├── icomoon.eot
│ ├── icomoon.svg
│ ├── icomoon.ttf
│ └── icomoon.woff
├── main.css
├── player.css
├── player.less
└── player.min.css
├── images
├── bg.jpg
└── icon.png
├── index.html
├── js
├── player.js
└── player.min.js
└── music
├── Kandy.mp3
└── PaperGangsta.mp3
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /music
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HTML5可视化播放器
2 | ### HTML5VisualizationPlayer
3 |
4 |
5 | 
6 |
7 | 
8 |
9 | 
10 |
11 | 
12 |
13 | HTML5可视化播放器是一款能将音乐播放绘制出频谱的播放器
14 | > [查看DEMO](https://www.hiphopbl.com/radio/ "街舞部落,街舞音乐电台")
15 |
16 |
17 |
18 | ### 浏览器支持情况
19 |
20 |
21 | 浏览器
22 | 是否支持
23 |
24 |
25 | 谷歌
26 | 支持
27 |
28 |
29 | 火狐
30 | 支持
31 |
32 |
33 | IE
34 | 支持(不支持AudioContext对象看不到频谱)
35 |
36 |
37 | Edge
38 | 支持
39 |
40 |
41 | 其他内核
42 | 未知
43 |
44 |
45 |
46 | ### 使用方法
47 |
48 |
49 | 1、引入播放器player.css与player.js
50 |
51 | ``` html
52 |
53 |
54 | ```
55 | 2、HTML中加入下面标签,用于创建播放器
56 |
57 | ``` html
58 |
59 | ```
60 | player外面可以用一个div包起来控制它的大小
61 |
62 | 3、调用生成播放器
63 |
64 | ``` javascript
65 |
66 | var player = new Player();
67 | player.init({
68 | autoPlay: true,//自动播放
69 | effect: 0,//频谱效果,不设置或0为随机变化,1为条形柱状,2为环状声波,3 心电图效果
70 | color: null,//颜色 16进制颜色代码,不设置或设置为空(空字符或null)将随机使用默认颜色
71 | button: {//设置生成的控制按钮,不设置button默认全部创建
72 | prev: true,//上一首
73 | play: true,//播放,暂停
74 | next: true,//下一首
75 | volume: true,//音量
76 | progressControl: true,//是否开启进度控制
77 | },
78 | event: function (e) {
79 | //这是一个事件方法,点击控制按钮会到此方法
80 | //参数:e.eventType 事件类型
81 | //参数:e.describe 事件详情,或参数
82 | //e.eventType prev: 点击上一首,next:点击下一首,play:点击 播放/暂停
83 | console.log(e);
84 | },
85 | energy: function (value) {
86 | //此时播放的能量值,时刻变化
87 | //console.log(value);
88 | },
89 | playList: [//播放列表
90 | {
91 | title: "Kandy",//音乐标题
92 | album: "",//所属专辑
93 | artist: "",//艺术家
94 | mp3: "music/Kandy.mp3",//音乐路径
95 | },
96 | {
97 | title: "Paper Gangsta",//音乐标题
98 | album: "",//所属专辑
99 | artist: "",//艺术家
100 | mp3: "music/PaperGangsta.mp3",//音乐路径
101 | },
102 | ]
103 | });
104 |
105 | ```
106 |
107 | ### 改变频谱效果
108 | 播放时如果想要改变效果调用下面方法
109 |
110 | ``` javascript
111 |
112 | player.change({
113 | effect: 3,//效果 0 还原随机, 1 为条形柱状,2为环状声波,3 心电图效果
114 | color: '#4395ff'//颜色 16进制颜色代码,不设置或设置为空(空字符或null)将随机使用默认颜色
115 | });
116 |
117 | ```
118 |
119 | ### 注意事项
120 |
121 | 1、需要在服务器环境下
122 |
123 | 2、mp3 资源如果存在跨域情况需要对资源进行跨域访问CORS设置,否则获取不到声源
124 |
--------------------------------------------------------------------------------
/css/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoppinRubo/HTML5VisualizationPlayer/d40f6b2a5b9d1985f94b7d68e4ab58f860cfad49/css/fonts/icomoon.eot
--------------------------------------------------------------------------------
/css/fonts/icomoon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Generated by IcoMoon
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/css/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoppinRubo/HTML5VisualizationPlayer/d40f6b2a5b9d1985f94b7d68e4ab58f860cfad49/css/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/css/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoppinRubo/HTML5VisualizationPlayer/d40f6b2a5b9d1985f94b7d68e4ab58f860cfad49/css/fonts/icomoon.woff
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 100%;
4 | margin: 0;
5 | font-family: arial, "Microsoft YaHei";
6 | color: #FEFEFE;
7 | }
8 |
9 | body {
10 | background-size: 100% 100%;
11 | background-position: center center;
12 | background-repeat: no-repeat;
13 | background-image: url("../images/bg.jpg");
14 | -webkit-transition: all .3s ease-out;
15 | -moz-transition: all .3s ease-out;
16 | -ms-transition: all .3s ease-out;
17 | -o-transition: all .3s ease-out;
18 | transition: all .3s ease-out;
19 | }
20 |
21 | #fileWrapper {
22 | transition: all 0.5s ease;
23 | }
24 |
25 | #fileWrapper:hover {
26 | opacity: 1 !important;
27 | }
28 |
29 | #visualizer_wrapper {
30 | text-align: center;
31 | }
32 |
33 | footer {
34 | position: fixed;
35 | bottom: 2px;
36 | color: #aaa;
37 | }
38 |
39 | .play-box {
40 | position: fixed;
41 | width: 1000px;
42 | height: 100px;
43 | margin: 0 auto;
44 | left: 0;
45 | right: 0;
46 | bottom: 0;
47 | }
--------------------------------------------------------------------------------
/css/player.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | .visualizer-player {
3 | display: block;
4 | width: 100%;
5 | height: 100%;
6 | background: #272822;
7 | font-family: arial, "Microsoft YaHei";
8 | border-radius: 3px;
9 | }
10 | @font-face {
11 | /*自定义字体-用于绘制字体图标*/
12 | font-family: 'icomoon';
13 | src: url('fonts/icomoon.eot?lawp0h');
14 | src: url('fonts/icomoon.eot?lawp0h#iefix') format('embedded-opentype'), url('fonts/icomoon.ttf?lawp0h') format('truetype'), url('fonts/icomoon.woff?lawp0h') format('woff'), url('fonts/icomoon.svg?lawp0h#icomoon') format('svg');
15 | font-weight: normal;
16 | font-style: normal;
17 | }
18 | .visualizer-player [class^="icon-"],
19 | .visualizer-player [class*=" icon-"] {
20 | /*统一设置字体图标*/
21 | /* 使用 !important 是要防止与浏览器扩展的问题,改变字体 */
22 | font-family: 'icomoon' !important;
23 | speak: none;
24 | font-style: normal;
25 | font-weight: normal;
26 | font-variant: normal;
27 | text-transform: none;
28 | line-height: 1;
29 | /* 更好的字体渲染 */
30 | -webkit-font-smoothing: antialiased;
31 | -moz-osx-font-smoothing: grayscale;
32 | }
33 | .visualizer-player canvas {
34 | position: absolute;
35 | }
36 | .visualizer-player .visualizer-player-tips {
37 | position: fixed;
38 | top: calc(50vh - 40px);
39 | text-align: center;
40 | line-height: 30px;
41 | left: 0;
42 | right: 0;
43 | width: 300px;
44 | height: 30px;
45 | margin: 0 auto;
46 | background-color: #272822;
47 | border-radius: 3px;
48 | cursor: pointer;
49 | padding: 5px;
50 | font-size: 14px;
51 | }
52 | .visualizer-player .player-control {
53 | position: absolute;
54 | height: 32px;
55 | width: 20%;
56 | top: 40%;
57 | margin: 0 0.5%;
58 | }
59 | .visualizer-player .player-control i {
60 | margin: 0 5px;
61 | font-size: 32px;
62 | cursor: pointer;
63 | color: #A7A7A7;
64 | -moz-user-select: none;
65 | -webkit-user-select: none;
66 | -ms-user-select: none;
67 | user-select: none;
68 | }
69 | .visualizer-player .player-control i:hover {
70 | color: #FFFFFF;
71 | }
72 | .visualizer-player .player-control #volumeBox {
73 | padding-top: 5px;
74 | display: inline-block;
75 | width: 35px;
76 | }
77 | .visualizer-player .player-control #volumeBox #volumeBar {
78 | position: absolute;
79 | margin: -20px 0 0 40px;
80 | border-radius: 4px;
81 | width: 100px;
82 | height: 5px;
83 | background: #5a5a5a;
84 | cursor: pointer;
85 | }
86 | .visualizer-player .player-control #volumeBox #volumeBar #volumeSize {
87 | position: absolute;
88 | left: 0;
89 | bottom: 0;
90 | height: 100%;
91 | border-radius: 4px;
92 | background: #258fb8;
93 | }
94 | .visualizer-player .player-control #volumeBox:hover #volumeBar {
95 | background: #FFFFFF;
96 | }
97 | .visualizer-player .song-info {
98 | position: absolute;
99 | border-bottom: 1px solid #575656;
100 | height: 25px;
101 | width: 100%;
102 | left: 0;
103 | top: 0;
104 | }
105 | .visualizer-player .song-info i {
106 | position: relative;
107 | height: 25px;
108 | width: 25px;
109 | top: 2px;
110 | font-size: 18px;
111 | margin: 0 5px 0 0;
112 | animation: musicIcon 2.5s infinite;
113 | -webkit-animation: musicIcon 2.5s infinite;
114 | /*Safari and Chrome*/
115 | }
116 | .visualizer-player .song-info span {
117 | text-align: center;
118 | font-weight: bold;
119 | margin: 0 5px;
120 | }
121 | .visualizer-player .song-info .player-state {
122 | position: absolute;
123 | z-index: 100;
124 | width: 100%;
125 | height: 100%;
126 | line-height: 25px;
127 | text-align: center;
128 | background: #272822;
129 | border-radius: 4px;
130 | }
131 | .visualizer-player .song-info #songTitle,
132 | .visualizer-player .song-info #artist,
133 | .visualizer-player .song-info #album,
134 | .visualizer-player .song-info span {
135 | margin: 0 5px;
136 | cursor: default;
137 | overflow: hidden;
138 | text-overflow: ellipsis;
139 | white-space: nowrap;
140 | height: 100%;
141 | font-size: 14px;
142 | line-height: 25px;
143 | float: left;
144 | }
145 | .visualizer-player .song-info #album,
146 | .visualizer-player .song-info #artist {
147 | font-size: 12px;
148 | }
149 | .visualizer-player .player-show {
150 | position: absolute;
151 | font-size: 14px;
152 | left: 30%;
153 | height: 50px;
154 | width: 70%;
155 | top: 30%;
156 | cursor: default;
157 | }
158 | .visualizer-player .player-show .player-time {
159 | margin: 1% 0 0 0;
160 | -moz-user-select: none;
161 | -webkit-user-select: none;
162 | -ms-user-select: none;
163 | user-select: none;
164 | }
165 | .visualizer-player .player-show .progress {
166 | position: absolute;
167 | bottom: 15px;
168 | border-radius: 4px;
169 | left: 0;
170 | width: 95%;
171 | height: 10px;
172 | background: #5a5a5a;
173 | }
174 | .visualizer-player .player-show .progress .player-progress-bar {
175 | position: absolute;
176 | left: 0;
177 | bottom: 0;
178 | border-radius: 4px;
179 | height: 100%;
180 | background: #258fb8;
181 | }
182 | @keyframes musicIcon {
183 | from {
184 | font-size: 12px;
185 | }
186 | to {
187 | font-size: 18px;
188 | }
189 | }
190 | @-webkit-keyframes musicIcon {
191 | /*Safari and Chrome*/
192 | from {
193 | font-size: 12px;
194 | }
195 | to {
196 | font-size: 18px;
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/css/player.less:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | .visualizer-player {
4 | @font-face {
5 | /*自定义字体-用于绘制字体图标*/
6 | font-family: 'icomoon';
7 | src: url('fonts/icomoon.eot?lawp0h');
8 | src: url('fonts/icomoon.eot?lawp0h#iefix') format('embedded-opentype'),
9 | url('fonts/icomoon.ttf?lawp0h') format('truetype'),
10 | url('fonts/icomoon.woff?lawp0h') format('woff'),
11 | url('fonts/icomoon.svg?lawp0h#icomoon') format('svg');
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
16 | [class^="icon-"],
17 | [class*=" icon-"] {
18 | /*统一设置字体图标*/
19 | /* 使用 !important 是要防止与浏览器扩展的问题,改变字体 */
20 | font-family: 'icomoon' !important;
21 | speak: none;
22 | font-style: normal;
23 | font-weight: normal;
24 | font-variant: normal;
25 | text-transform: none;
26 | line-height: 1;
27 | /* 更好的字体渲染 */
28 | -webkit-font-smoothing: antialiased;
29 | -moz-osx-font-smoothing: grayscale;
30 | }
31 |
32 | display: block;
33 | width: 100%;
34 | height: 100%;
35 | background: #272822;
36 | font-family: arial,
37 | "Microsoft YaHei";
38 | border-radius: 3px;
39 |
40 | canvas {
41 | position: absolute;
42 | }
43 |
44 | .visualizer-player-tips {
45 | position: fixed;
46 | top: calc(50vh - 40px);
47 | text-align: center;
48 | line-height: 30px;
49 | left: 0;
50 | right: 0;
51 | width: 300px;
52 | height: 30px;
53 | margin: 0 auto;
54 | background-color: #272822;
55 | border-radius: 3px;
56 | cursor: pointer;
57 | padding: 5px;
58 | font-size: 14px;
59 | }
60 |
61 | .player-control {
62 | position: absolute;
63 | height: 32px;
64 | width: 20%;
65 | top: 40%;
66 | margin: 0 0.5%;
67 |
68 | i {
69 | margin: 0 5px;
70 | font-size: 32px;
71 | cursor: pointer;
72 | color: #A7A7A7;
73 | -moz-user-select: none;
74 | -webkit-user-select: none;
75 | -ms-user-select: none;
76 | user-select: none;
77 | }
78 |
79 | i:hover {
80 | color: #FFFFFF;
81 | }
82 |
83 | #volumeBox {
84 | padding-top: 5px;
85 | display: inline-block;
86 | width: 35px;
87 |
88 | #volumeBar {
89 | position: absolute;
90 | margin: -20px 0 0 40px;
91 | border-radius: 4px;
92 | width: 100px;
93 | height: 5px;
94 | background: #5a5a5a;
95 | cursor: pointer;
96 |
97 | #volumeSize {
98 | position: absolute;
99 | left: 0;
100 | bottom: 0;
101 | height: 100%;
102 | border-radius: 4px;
103 | background: #258fb8;
104 | }
105 | }
106 | }
107 |
108 | #volumeBox:hover {
109 | #volumeBar {
110 | background: #FFFFFF;
111 | }
112 | }
113 | }
114 |
115 |
116 | .song-info {
117 | position: absolute;
118 | border-bottom: 1px solid #575656;
119 | height: 25px;
120 | width: 100%;
121 | left: 0;
122 | top: 0;
123 |
124 | i {
125 | position: relative;
126 | height: 25px;
127 | width: 25px;
128 | top: 2px;
129 | font-size: 18px;
130 | margin: 0 5px 0 0;
131 | animation: musicIcon 2.5s infinite;
132 | -webkit-animation: musicIcon 2.5s infinite;
133 | /*Safari and Chrome*/
134 | }
135 |
136 | span {
137 | text-align: center;
138 | font-weight: bold;
139 | margin: 0 5px;
140 | }
141 |
142 | .player-state {
143 | position: absolute;
144 | z-index: 100;
145 | width: 100%;
146 | height: 100%;
147 | line-height: 25px;
148 | text-align: center;
149 | background: #272822;
150 | border-radius: 4px;
151 | }
152 |
153 | #songTitle,
154 | #artist,
155 | #album,
156 | span {
157 | margin: 0 5px;
158 | cursor: default;
159 | overflow: hidden;
160 | text-overflow: ellipsis;
161 | white-space: nowrap;
162 | height: 100%;
163 | font-size: 14px;
164 | line-height: 25px;
165 | float: left;
166 | }
167 |
168 | #album,
169 | #artist {
170 | font-size: 12px;
171 | }
172 | }
173 |
174 | .player-show {
175 | position: absolute;
176 | font-size: 14px;
177 | left: 30%;
178 | height: 50px;
179 | width: 70%;
180 | top: 30%;
181 | cursor: default;
182 |
183 | .player-time {
184 | margin: 1% 0 0 0;
185 | -moz-user-select: none;
186 | -webkit-user-select: none;
187 | -ms-user-select: none;
188 | user-select: none;
189 | }
190 |
191 | .progress {
192 | position: absolute;
193 | bottom: 15px;
194 | border-radius: 4px;
195 | left: 0;
196 | width: 95%;
197 | height: 10px;
198 | background: #5a5a5a;
199 |
200 | .player-progress-bar {
201 | position: absolute;
202 | left: 0;
203 | bottom: 0;
204 | border-radius: 4px;
205 | height: 100%;
206 | background: #258fb8;
207 | }
208 | }
209 | }
210 |
211 | @keyframes musicIcon {
212 | from {
213 | font-size: 12px;
214 | }
215 |
216 | to {
217 | font-size: 18px;
218 | }
219 | }
220 |
221 | @-webkit-keyframes musicIcon
222 |
223 | /*Safari and Chrome*/
224 | {
225 | from {
226 | font-size: 12px;
227 | }
228 |
229 | to {
230 | font-size: 18px;
231 | }
232 | }
233 | }
--------------------------------------------------------------------------------
/css/player.min.css:
--------------------------------------------------------------------------------
1 | @charset "utf-8";.visualizer-player{display:block;width:100%;height:100%;background:#272822;font-family:arial,"Microsoft YaHei";border-radius:3px}@font-face{font-family:icomoon;src:url(fonts/icomoon.eot?lawp0h);src:url(fonts/icomoon.eot?lawp0h#iefix) format('embedded-opentype'),url(fonts/icomoon.ttf?lawp0h) format('truetype'),url(fonts/icomoon.woff?lawp0h) format('woff'),url(fonts/icomoon.svg?lawp0h#icomoon) format('svg');font-weight:400;font-style:normal}.visualizer-player [class*=" icon-"],.visualizer-player [class^=icon-]{font-family:icomoon!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.visualizer-player canvas{position:absolute}.visualizer-player .visualizer-player-tips{position:fixed;top:calc(50vh - 40px);text-align:center;line-height:30px;left:0;right:0;width:300px;height:30px;margin:0 auto;background-color:#272822;border-radius:3px;cursor:pointer;padding:5px;font-size:14px}.visualizer-player .player-control{position:absolute;height:32px;width:20%;top:40%;margin:0 .5%}.visualizer-player .player-control i{margin:0 5px;font-size:32px;cursor:pointer;color:#a7a7a7;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.visualizer-player .player-control i:hover{color:#fff}.visualizer-player .player-control #volumeBox{padding-top:5px;display:inline-block;width:35px}.visualizer-player .player-control #volumeBox #volumeBar{position:absolute;margin:-20px 0 0 40px;border-radius:4px;width:100px;height:5px;background:#5a5a5a;cursor:pointer}.visualizer-player .player-control #volumeBox #volumeBar #volumeSize{position:absolute;left:0;bottom:0;height:100%;border-radius:4px;background:#258fb8}.visualizer-player .player-control #volumeBox:hover #volumeBar{background:#fff}.visualizer-player .song-info{position:absolute;border-bottom:1px solid #575656;height:25px;width:100%;left:0;top:0}.visualizer-player .song-info i{position:relative;height:25px;width:25px;top:2px;font-size:18px;margin:0 5px 0 0;animation:musicIcon 2.5s infinite;-webkit-animation:musicIcon 2.5s infinite}.visualizer-player .song-info span{text-align:center;font-weight:700;margin:0 5px}.visualizer-player .song-info .player-state{position:absolute;z-index:100;width:100%;height:100%;line-height:25px;text-align:center;background:#272822;border-radius:4px}.visualizer-player .song-info #album,.visualizer-player .song-info #artist,.visualizer-player .song-info #songTitle,.visualizer-player .song-info span{margin:0 5px;cursor:default;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;height:100%;font-size:14px;line-height:25px;float:left}.visualizer-player .song-info #album,.visualizer-player .song-info #artist{font-size:12px}.visualizer-player .player-show{position:absolute;font-size:14px;left:30%;height:50px;width:70%;top:30%;cursor:default}.visualizer-player .player-show .player-time{margin:1% 0 0 0;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.visualizer-player .player-show .progress{position:absolute;bottom:15px;border-radius:4px;left:0;width:95%;height:10px;background:#5a5a5a}.visualizer-player .player-show .progress .player-progress-bar{position:absolute;left:0;bottom:0;border-radius:4px;height:100%;background:#258fb8}@keyframes musicIcon{from{font-size:12px}to{font-size:18px}}@-webkit-keyframes musicIcon{from{font-size:12px}to{font-size:18px}}
--------------------------------------------------------------------------------
/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoppinRubo/HTML5VisualizationPlayer/d40f6b2a5b9d1985f94b7d68e4ab58f860cfad49/images/bg.jpg
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoppinRubo/HTML5VisualizationPlayer/d40f6b2a5b9d1985f94b7d68e4ab58f860cfad49/images/icon.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | HTML5 可视化音乐播放器
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
62 |
63 |
--------------------------------------------------------------------------------
/js/player.js:
--------------------------------------------------------------------------------
1 | /**
2 | * HTML5 Audio Visualizer Player
3 | * HTML5音乐可视化播放器
4 | * 版本号:1.4.0
5 | * Author:PoppinRubo
6 | * License: MIT
7 | */
8 |
9 | //创建一个对象方法
10 | function Player() {
11 | //播放获取进度信息时间计时器
12 | var timer;
13 | //加载超时计时
14 | var overtime;
15 | //先把自己用变量储存起来,后面要用
16 | var myself = this;
17 | //默认设置
18 | myself.button = { //设置生成的控制按钮,默认开启
19 | prev: true, //上一首
20 | play: true, //播放,暂停
21 | next: true, //下一首
22 | volume: true, //音量
23 | progressControl: true //进度控制
24 | }
25 |
26 | myself.analyser = null;
27 |
28 | //定义事件默认空方法
29 | myself.event = function (e) {
30 | //未设置事件方法就默认执行空方法
31 | }
32 | //能量传出方法默认空方法
33 | myself.energy = function (v) {
34 | //未设置能量传出就默认执行空方法
35 | }
36 |
37 | //频谱配置初始化,外部调用就开始进行处理
38 | this.init = function (object) {
39 | myself.playList = object.playList || [{
40 | title: '播放列表为空',
41 | album: '',
42 | artist: '',
43 | mp3: ''
44 | }];
45 | myself.autoPlay = object.autoPlay || false;
46 | myself.event = object.event || myself.event;
47 | myself.energy = object.energy || myself.energy;
48 | myself.button = object.button || myself.button;
49 | myself.color = object.color || null;
50 | myself.effect = object.effect || 0; //默认随机,效果为0表示随机切换效果
51 | //记录是否处理过音频,保证createMediaElementSource只创建一次,多次创建会出现错误
52 | myself.handle = 0;
53 | createParts();
54 | }
55 |
56 | //改变效果
57 | this.change = function (object) {
58 | myself.effect = object.effect || 0;
59 | myself.color = object.color || null;
60 | drawSpectrum(myself.analyser);
61 | }
62 |
63 | //实例化一个音频类型window.AudioContext
64 | function windowAudioContext() {
65 | //下面这些是为了统一Chrome和Firefox的AudioContext
66 | window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
67 | window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
68 | window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame;
69 | try {
70 | myself.audioContext = new AudioContext();
71 | } catch (e) {
72 | console.log('您的浏览器不支持 AudioContext,信息:' + e);
73 | }
74 | }
75 |
76 | //创建播放部件
77 | function createParts() {
78 | //创建audio
79 | var audio = document.createElement("AUDIO");
80 | audio.crossOrigin = 'anonymous';
81 | var player = document.getElementsByTagName("player")[0];
82 | player.className = 'visualizer-player';
83 | player.appendChild(audio);
84 | myself.player = player;
85 | myself.audio = audio;
86 |
87 | //音乐部件
88 | var songInfo = document.createElement("div");
89 | var control = document.createElement("div");
90 | var playerShow = document.createElement("div");
91 | var playerTime = document.createElement("div");
92 | var progress = document.createElement("div");
93 | var playerProgressBar = document.createElement("div");
94 | songInfo.className = 'song-info';
95 | control.className = 'player-control';
96 | playerShow.className = 'player-show';
97 | playerTime.className = 'player-time';
98 | progress.className = 'progress';
99 | playerProgressBar.className = 'player-progress-bar';
100 | player.appendChild(songInfo);
101 | player.appendChild(control);
102 | player.appendChild(playerShow);
103 | playerShow.appendChild(playerTime);
104 | playerShow.appendChild(progress);
105 | progress.appendChild(playerProgressBar);
106 |
107 | myself.songInfo = songInfo;
108 | myself.progress = progress;
109 | myself.playerProgressBar = playerProgressBar;
110 |
111 | //创建控制按钮
112 | var button = myself.button;
113 | //创建画布
114 | var canvas = document.createElement("canvas");
115 | canvas.height = 450;
116 | canvas.width = player.clientWidth;
117 | canvas.style.bottom = player.clientHeight + 'px';
118 | player.appendChild(canvas);
119 | myself.canvas = canvas;
120 |
121 | if (button.prev) {
122 | //上一首,按钮创建
123 | var prevBtn = document.createElement('i');
124 | prevBtn.className = "icon-previous";
125 | prevBtn.id = "playPrev";
126 | prevBtn.title = "上一首";
127 | prevBtn.innerHTML = "";
128 | control.appendChild(prevBtn);
129 | //上一首,控制
130 | var playPrev = document.getElementById("playPrev");
131 | playPrev.onclick = function () {
132 | prev();
133 | }
134 | }
135 | if (button.play) {
136 | //播放,暂停,按钮创建
137 | var playBtn = document.createElement('i');
138 | playBtn.className = "icon-play";
139 | playBtn.id = "playControl";
140 | playBtn.title = "播放";
141 | playBtn.innerHTML = "";
142 | playBtn.setAttribute('data', 'pause');
143 | control.appendChild(playBtn);
144 | //播放,暂停,控制
145 | var playControl = document.getElementById("playControl");
146 | playControl.onclick = function () {
147 | play();
148 | }
149 | }
150 | if (button.next) {
151 | //下一首,按钮创建
152 | var nextBtn = document.createElement('i');
153 | nextBtn.className = "icon-next";
154 | nextBtn.id = "playNext";
155 | nextBtn.title = "下一首";
156 | nextBtn.innerHTML = "";
157 | control.appendChild(nextBtn);
158 | //下一首,控制
159 | var playNext = document.getElementById("playNext");
160 | playNext.onclick = function () {
161 | next();
162 | }
163 | }
164 | if (button.volume) {
165 | //按钮与音量控制条容器
166 | var volumeBox = document.createElement('div');
167 | volumeBox.id = "volumeBox";
168 | control.appendChild(volumeBox);
169 | //音量,按钮创建
170 | var volumeBtn = document.createElement('i');
171 | volumeBtn.className = "icon-volume";
172 | volumeBtn.id = "playVolume";
173 | volumeBtn.title = "音量";
174 | if (playBtn) {
175 | playBtn.setAttribute('data', 'normal');
176 | }
177 | volumeBtn.innerHTML = "";
178 | volumeBox.appendChild(volumeBtn);
179 | //音量控制条
180 | var volumeBar = document.createElement('div');
181 | volumeBar.id = "volumeBar";
182 | volumeBox.appendChild(volumeBar);
183 |
184 | var volumeSize = document.createElement('div');
185 | volumeSize.id = "volumeSize";
186 | volumeBar.appendChild(volumeSize);
187 |
188 | volumeBar.onclick = function (event) {
189 | volumeChange(event, volumeBar, volumeSize);
190 | }
191 |
192 | //音量,点击控制,静音-恢复
193 | var volumeBtn = document.getElementById("playVolume");
194 | volumeBtn.onclick = function () {
195 | volume();
196 | }
197 | }
198 |
199 | //显示时间容器
200 | playerTime.innerHTML = "- 00:00 / 00:00 0%";
201 | myself.playerTime = playerTime;
202 |
203 | //进度条
204 | if (myself.button.progressControl) {
205 | var progress = myself.progress;
206 | progress.style.cursor = "pointer";
207 | progress.onclick = function (event) {
208 | progressControl(event, progress);
209 | }
210 | }
211 |
212 | //调用实例化AudioContext
213 | windowAudioContext();
214 |
215 | //加载地址方法,audio加入一个初始地址
216 | var playList = myself.playList;
217 | //把列表第一个mp3地址设置到audio上
218 | myself.audio.src = playList[0].mp3;
219 |
220 | //歌曲信息,创建
221 | var songTitle = document.createElement('div');
222 | songTitle.id = "songTitle";
223 | songInfo.appendChild(songTitle);
224 |
225 | var album = document.createElement('div');
226 | album.id = "album";
227 | songInfo.appendChild(album);
228 |
229 | var span = document.createElement('span');
230 | span.innerHTML = '-';
231 | songInfo.appendChild(span);
232 | myself.division = span;
233 |
234 | var artist = document.createElement('div');
235 | artist.id = "artist";
236 | songInfo.appendChild(artist);
237 |
238 | //记录当前播放在数组里的位置
239 | myself.nowPlay = 0;
240 | //信息设置
241 | updates();
242 | //获取存储音量
243 | myself.audio.volume = volumeGetCookie();
244 | //设置自动播放,开始播放
245 | if (myself.autoPlay) {
246 | play();
247 | }
248 | }
249 |
250 | //播放,暂停 控制
251 | function play() {
252 | var playBtn = document.getElementById("playControl");
253 | //播放控制
254 | if (myself.audio.paused) {
255 | var playing = myself.audio.play();
256 | playing.then(function () {
257 | //字符图标变化
258 | if (playBtn) {
259 | playBtn.setAttribute("data", "play");
260 | playBtn.title = "暂停";
261 | playBtn.innerHTML = "";
262 | }
263 | timer = setInterval(function () {
264 | //显示时长
265 | showTime();
266 | //获取就绪状态并处理相应
267 | playerState();
268 | }, 1000);
269 | //播放媒体信息更新
270 | updates();
271 | //处理播放数据,处理过就不再处理
272 | if (myself.handle == 0) {
273 | playHandle();
274 | }
275 | //如果存在提示则移除
276 | var tips = document.getElementsByClassName('visualizer-player-tips');
277 | if (tips.length > 0) {
278 | myself.player.removeChild(tips[0]);
279 | }
280 | }).catch(function () {
281 | //处理浏览器不支持自动播放情况
282 | var tips = document.createElement("div");
283 | tips.className = 'visualizer-player-tips';
284 | tips.innerHTML = '浏览器不支持自动播放,点我开始播放';
285 | tips.onclick = function () {
286 | myself.player.removeChild(tips);
287 | play();
288 | };
289 | myself.player.appendChild(tips);
290 | return false;
291 | })
292 | } else {
293 | myself.audio.pause();
294 | //字符图标变化
295 | if (playBtn) {
296 | playBtn.setAttribute("data", "pause");
297 | playBtn.title = "播放";
298 | playBtn.innerHTML = "";
299 | }
300 | window.clearInterval(timer);
301 | }
302 | //事件传出
303 | myself.event({
304 | eventType: "play",
305 | describe: "播放/暂停",
306 | playing: !myself.audio.paused
307 | });
308 | }
309 |
310 | //播放媒体信息更新
311 | function updates() {
312 | var list = myself.playList;
313 | var nowPlay = myself.nowPlay;
314 | var songTitle = document.getElementById("songTitle");
315 | songTitle.innerHTML = list[nowPlay].title;
316 | songTitle.title = "歌曲:" + list[nowPlay].title;
317 | var songAlbum = document.getElementById("album");
318 | var albumTitle = list[nowPlay].album || '';
319 | songAlbum.innerHTML = "(" + (albumTitle) + ")";
320 | songAlbum.style.display = albumTitle ? 'block' : 'none';
321 | songAlbum.title = "所属专辑:" + albumTitle;
322 | var songArtist = document.getElementById("artist");
323 | var artistName = list[nowPlay].artist;
324 | myself.division.style.display = artistName ? 'block' : 'none';
325 | songArtist.innerHTML = artistName;
326 | songArtist.title = "艺术家:" + artistName;
327 | }
328 |
329 | //音频播放状态,做消息处理
330 | function playerState() {
331 | //音频当前的就绪状态, 0 未连接 1 打开连接 2 发送请求 3 交互 4 完成交互,接手响应
332 | var state = myself.audio.readyState;
333 | var playerState = document.getElementById("player-state");
334 | var songInfo = myself.songInfo;
335 | if (state == 4) {
336 | if (playerState != null) {
337 | songInfo.removeChild(playerState);
338 | //清除超时计时
339 | window.clearTimeout(overtime);
340 | }
341 | } else {
342 | if (playerState == null) {
343 | playerState = document.createElement("div");
344 | playerState.className = "player-state";
345 | playerState.id = "player-state";
346 | playerState.innerHTML = " 加载中……";
347 | songInfo.appendChild(playerState);
348 | //加载超时处理
349 | overtime = setTimeout(function () { //2分钟后超时处理
350 | if (myself.audio.readyState == 0) {
351 | playerState.innerHTML = "加载失败!"
352 | }
353 | }, 120000)
354 | }
355 | }
356 | }
357 |
358 | //显示时长,进度
359 | function showTime() {
360 | if (myself.audio.readyState == 4) {
361 | //时长总量
362 | var duration = myself.audio.duration;
363 | //时长进度
364 | var currentTime = myself.audio.currentTime;
365 | //剩余量
366 | var surplusTime = duration - currentTime;
367 | var ratio = ((currentTime / duration) * 100).toFixed(1);
368 | //将100.00%变为100%
369 | ratio = ratio == 100.0 ? 100 : ratio;
370 |
371 | function timeFormat(t) {
372 | return Math.floor(t / 60) + ":" + (t % 60 / 100).toFixed(2).slice(-2);
373 | }
374 |
375 | myself.playerTime.innerHTML = "- " + timeFormat(surplusTime) + " / " + timeFormat(duration) + " " + ratio + "%";
376 | myself.playerProgressBar.style.width = ratio + "%";
377 | if (ratio == 100) { //播放结束就播放就调用下一首
378 | next();
379 | }
380 |
381 | } else { //状态不为4说明未就绪显示00:00
382 | myself.playerTime.innerHTML = "- 00:00 / 00:00 0%";
383 | }
384 |
385 |
386 | }
387 |
388 | //播放上一首
389 | function prev() {
390 | //数组播放最前移动到最后
391 | if (myself.nowPlay == 0) {
392 | myself.nowPlay = myself.playList.length;
393 | }
394 | //记录当前播放在数组里的位置位置移动,减小
395 | myself.nowPlay = myself.nowPlay - 1;
396 | //媒体url信息更新
397 | myself.audio.src = myself.playList[myself.nowPlay].mp3;
398 | //先清除计时避免越点计时越快
399 | window.clearInterval(timer);
400 | //重绘,变换效果
401 | if (myself.analyser != null) {
402 | drawSpectrum(myself.analyser);
403 | }
404 | //事件传出
405 | myself.event({
406 | eventType: "prev",
407 | describe: "播放上一首"
408 | });
409 | play();
410 | }
411 |
412 | //播放下一首
413 | function next() {
414 | //数组播放最后移动到最前
415 | if (myself.nowPlay == myself.playList.length - 1) {
416 | myself.nowPlay = -1;
417 | }
418 | //记录当前播放在数组里的位置位置移动,增加
419 | myself.nowPlay = myself.nowPlay + 1;
420 | //媒体url信息更新
421 | myself.audio.src = myself.playList[myself.nowPlay].mp3;
422 | //先清除计时避免越点计时越快
423 | window.clearInterval(timer);
424 | //重绘,变换效果
425 | if (myself.analyser != null) {
426 | drawSpectrum(myself.analyser);
427 | }
428 | //事件传出
429 | myself.event({
430 | eventType: "next",
431 | describe: "播放上一首"
432 | });
433 | play();
434 | }
435 |
436 | //音量点击控制,静音-恢复
437 | function volume() {
438 | if (myself.button.volume) { //判断是否设置音量按钮
439 | var volumeBtn = document.getElementById("playVolume");
440 | var data = volumeBtn.getAttribute("data");
441 | //字符图标变化
442 | if (data == "normal") {
443 | volumeBtn.setAttribute("data", "mute");
444 | volumeBtn.innerHTML = "";
445 | } else {
446 | volumeBtn.setAttribute("data", "normal");
447 | volumeBtn.innerHTML = ""
448 | }
449 | }
450 | //点击音量控制
451 | if (myself.audio.muted) {
452 | myself.audio.muted = false;
453 | } else {
454 | myself.audio.muted = true;
455 | }
456 | }
457 |
458 | //音量控制条点击设置音量大小
459 | function volumeChange(e, volumeBar, volumeSize) {
460 | //点击的位置
461 | var offsetX = e.offsetX;
462 | //获取音量条总高度
463 | var width = volumeBar.offsetWidth;
464 | //算出占比
465 | var proportion = offsetX / width;
466 | volumeSize.style.width = (proportion * 100) + "%";
467 | var size = proportion;
468 | //音量设置
469 | myself.audio.volume = size;
470 | //音量cookie存储
471 | volumeSetCookie(size);
472 | }
473 |
474 | //音量cookie设置
475 | function volumeSetCookie(size) {
476 | var d = new Date();
477 | d.setHours(d.getHours() + (24 * 30)); //保存一个月
478 | document.cookie = "playerVolume=" + size + ";expires=" + d.toGMTString();
479 | }
480 |
481 | //音量cookie获取
482 | function volumeGetCookie() {
483 | var volumeSize = document.getElementById("volumeSize");
484 | var arr, reg = new RegExp("(^| )playerVolume=([^;]*)(;|$)");
485 | var volume = 1;
486 | if (arr = document.cookie.match(reg)) {
487 | volume = unescape(arr[2]);
488 | } else {
489 | volume = 0.5;
490 | }
491 | volumeSize.style.width = volume * 100 + "%";
492 | return volume;
493 | }
494 |
495 | //进度点击控制
496 | function progressControl(e, progress) {
497 | //点击的位置
498 | var offsetX = e.offsetX;
499 | //获取进度条总长度
500 | var width = progress.offsetWidth;
501 | //算出占比
502 | var proportion = offsetX / width;
503 | //把宽的比例换为播放比例,再计算audio播放位置
504 | var duration = myself.audio.duration;
505 | var playTime = duration * proportion;
506 | //从此处播放
507 | myself.audio.currentTime = playTime;
508 |
509 | }
510 |
511 | //播放处理,提取数据
512 | function playHandle() {
513 | //IE浏览器不绘制频谱
514 | if (!!window.ActiveXObject || "ActiveXObject" in window) {
515 | return false;
516 | }
517 | windowAudioContext();
518 | var audioContext = myself.audioContext;
519 | var analyser = audioContext.createAnalyser();
520 | var playData = audioContext.createMediaElementSource(myself.audio);
521 | // 将播放数据与分析器连接
522 | playData.connect(analyser);
523 | analyser.connect(audioContext.destination);
524 | //接下来把分析器传出去创建频谱
525 | drawSpectrum(analyser);
526 | //记录一下,还会用到analyser
527 | myself.analyser = analyser;
528 | myself.handle = 1;
529 | }
530 |
531 | //频谱效果处理
532 | function drawSpectrum(analyser) {
533 | //颜色数组
534 | var colorArray = ['#f82466', '#00FFFF', '#AFFF7C', '#FFAA6A', '#6AD5FF', '#D26AFF', '#FF6AE6', '#FF6AB8', '#FF6A6A', "#7091FF"];
535 | //颜色随机数
536 | var colorRandom = Math.floor(Math.random() * colorArray.length);
537 | //未设置将随机选取颜色
538 | myself.colour = myself.color || colorArray[colorRandom];
539 | //图形数组
540 | var effectArray = [1, 2, 3];
541 | //效果随机数
542 | var effectRandom = Math.floor(Math.random() * effectArray.length);
543 | var effect = myself.effect || effectArray[effectRandom];
544 | //随机选取效果
545 | switch (effect) {
546 | case 1:
547 | //条形
548 | bar(analyser);
549 | break;
550 | case 2:
551 | //环形声波
552 | circular(analyser);
553 | break;
554 | case 3:
555 | //心电图效果
556 | line(analyser);
557 | break;
558 | default:
559 | //条形
560 | bar(analyser);
561 | }
562 |
563 | }
564 |
565 | //16进制颜色转为RGB格式,传入16进制颜色代码与透明度
566 | function colorRgb(color, opacity) {
567 | var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; //十六进制颜色值的正则表达式
568 | opacity = opacity < 0 ? 0 : opacity; //颜色范围控制
569 | opacity = opacity > 1 ? 1 : opacity;
570 | if (color && reg.test(color)) {
571 | if (color.length === 4) {
572 | var sColorNew = "#";
573 | for (var i = 1; i < 4; i += 1) {
574 | sColorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1));
575 | }
576 | color = sColorNew;
577 | }
578 | //处理六位的颜色值
579 | var sColorChange = [];
580 | for (var i = 1; i < 7; i += 2) {
581 | sColorChange.push(parseInt("0x" + color.slice(i, i + 2)));
582 | }
583 | return "rgba(" + sColorChange.join(",") + "," + opacity + ")";
584 | } else {
585 | return color;
586 | }
587 | }
588 |
589 | //条状效果
590 | function bar(analyser) {
591 | var canvas = myself.canvas,
592 | cwidth = canvas.width,
593 | cheight = canvas.height - 2,
594 | meterWidth = 10, //频谱条宽度
595 | capHeight = 2,
596 | capStyle = '#FFFFFF',
597 | meterNum = 1000 / (10 + 2), //频谱条数量
598 | ctx = canvas.getContext('2d'),
599 | capYPositionArray = [], //将上一画面各帽头的位置保存到这个数组
600 | gradient = ctx.createLinearGradient(0, 0, 0, 300);
601 | gradient.addColorStop(1, myself.colour);
602 | var drawMeter = function () {
603 | var array = new Uint8Array(analyser.frequencyBinCount);
604 | analyser.getByteFrequencyData(array);
605 | var step = Math.round(array.length / meterNum); //计算采样步长
606 | ctx.clearRect(0, 0, cwidth, cheight);
607 | for (var i = 0; i < meterNum; i++) {
608 | var value = array[i * step]; //获取当前能量值
609 | if (capYPositionArray.length < Math.round(meterNum)) {
610 | capYPositionArray.push(value); //初始化保存帽头位置的数组,将第一个画面的数据压入其中
611 | };
612 | ctx.fillStyle = capStyle;
613 | //开始绘制帽头
614 | if (value < capYPositionArray[i]) { //如果当前值小于之前值
615 | ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight); //则使用前一次保存的值来绘制帽头
616 | } else {
617 | ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight); //否则使用当前值直接绘制
618 | capYPositionArray[i] = value;
619 | };
620 | //开始绘制频谱条
621 | ctx.fillStyle = gradient;
622 | ctx.fillRect(i * 12, cheight - value + capHeight, meterWidth, cheight);
623 | //把能量传出
624 | myself.energy(value);
625 | }
626 | requestAnimationFrame(drawMeter);
627 | }
628 | requestAnimationFrame(drawMeter);
629 | }
630 |
631 | //环形声波
632 | function circular(analyser) {
633 | var canvas = myself.canvas,
634 | width = canvas.width,
635 | height = canvas.height,
636 | ctx = canvas.getContext('2d');
637 | var drawCircular = function () {
638 | var array = new Uint8Array(128); //长度为128无符号数组用于保存getByteFrequencyData返回的频域数据
639 | analyser.getByteFrequencyData(array); //以下是根据频率数据画图
640 | ctx.clearRect(0, 0, width, height); //清除画布
641 | for (var i = 0; i < (array.length); i++) {
642 | var value = array[i];
643 | ctx.beginPath();
644 | ctx.arc(width / 2, height / 2, value * 0.8, 0, 400, false);
645 | ctx.lineWidth = 2; //线圈粗细
646 | ctx.strokeStyle = (1, colorRgb(myself.colour, value / 1000)); //颜色透明度随值变化
647 | ctx.stroke(); //画空心圆
648 | ctx.closePath();
649 | //把能量传出
650 | myself.energy(value);
651 | }
652 | requestAnimationFrame(drawCircular);
653 | };
654 | requestAnimationFrame(drawCircular);
655 | }
656 |
657 | //心电图效果
658 | function line(analyser) {
659 | var canvas = myself.canvas,
660 | width = canvas.width,
661 | height = canvas.height,
662 | ctx = canvas.getContext('2d');
663 | var drawLine = function () {
664 | var array = new Uint8Array(128); //长度为128无符号数组用于保存getByteFrequencyData返回的频域数据
665 | analyser.getByteFrequencyData(array); //以下是根据频率数据画图
666 | ctx.clearRect(0, 0, width, height); //清除画布
667 | ctx.strokeStyle = myself.colour;
668 | ctx.lineWidth = 2; //线粗细
669 | ctx.beginPath();
670 | for (var i = 0; i < (array.length); i++) {
671 | var value = array[i];
672 | //绘制线根据能量值变化
673 | ctx.lineTo(i * 9, value + 150);
674 | //把能量传出
675 | myself.energy(value);
676 | };
677 | ctx.stroke();
678 | ctx.closePath();
679 | requestAnimationFrame(drawLine);
680 | };
681 | requestAnimationFrame(drawLine);
682 | }
683 | }
--------------------------------------------------------------------------------
/js/player.min.js:
--------------------------------------------------------------------------------
1 | function Player(){var timer,overtime,myself=this;function windowAudioContext(){window.AudioContext=window.AudioContext||window.webkitAudioContext||window.mozAudioContext||window.msAudioContext,window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame,window.cancelAnimationFrame=window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.msCancelAnimationFrame;try{myself.audioContext=new AudioContext}catch(e){console.log("您的浏览器不支持 AudioContext,信息:"+e)}}function createParts(){var audio=document.createElement("AUDIO");audio.crossOrigin="anonymous";var player=document.getElementsByTagName("player")[0];player.className="visualizer-player",player.appendChild(audio),myself.player=player,myself.audio=audio;var songInfo=document.createElement("div"),control=document.createElement("div"),playerShow=document.createElement("div"),playerTime=document.createElement("div"),progress=document.createElement("div"),playerProgressBar=document.createElement("div");songInfo.className="song-info",control.className="player-control",playerShow.className="player-show",playerTime.className="player-time",progress.className="progress",playerProgressBar.className="player-progress-bar",player.appendChild(songInfo),player.appendChild(control),player.appendChild(playerShow),playerShow.appendChild(playerTime),playerShow.appendChild(progress),progress.appendChild(playerProgressBar),myself.songInfo=songInfo,myself.progress=progress,myself.playerProgressBar=playerProgressBar;var button=myself.button,canvas=document.createElement("canvas"),progress;if(canvas.height=450,canvas.width=player.clientWidth,canvas.style.bottom=player.clientHeight+"px",player.appendChild(canvas),myself.canvas=canvas,button.prev){var prevBtn=document.createElement("i"),playPrev;prevBtn.className="icon-previous",prevBtn.id="playPrev",prevBtn.title="上一首",prevBtn.innerHTML="",control.appendChild(prevBtn),document.getElementById("playPrev").onclick=function(){prev()}}if(button.play){var playBtn=document.createElement("i"),playControl;playBtn.className="icon-play",playBtn.id="playControl",playBtn.title="播放",playBtn.innerHTML="",playBtn.setAttribute("data","pause"),control.appendChild(playBtn),document.getElementById("playControl").onclick=function(){play()}}if(button.next){var nextBtn=document.createElement("i"),playNext;nextBtn.className="icon-next",nextBtn.id="playNext",nextBtn.title="下一首",nextBtn.innerHTML="",control.appendChild(nextBtn),document.getElementById("playNext").onclick=function(){next()}}if(button.volume){var volumeBox=document.createElement("div"),volumeBtn;volumeBox.id="volumeBox",control.appendChild(volumeBox),(volumeBtn=document.createElement("i")).className="icon-volume",volumeBtn.id="playVolume",volumeBtn.title="音量",playBtn&&playBtn.setAttribute("data","normal"),volumeBtn.innerHTML="",volumeBox.appendChild(volumeBtn);var volumeBar=document.createElement("div");volumeBar.id="volumeBar",volumeBox.appendChild(volumeBar);var volumeSize=document.createElement("div"),volumeBtn;volumeSize.id="volumeSize",volumeBar.appendChild(volumeSize),volumeBar.onclick=function(event){volumeChange(event,volumeBar,volumeSize)},(volumeBtn=document.getElementById("playVolume")).onclick=function(){volume()}}(playerTime.innerHTML="- 00:00 / 00:00 0%",myself.playerTime=playerTime,myself.button.progressControl)&&((progress=myself.progress).style.cursor="pointer",progress.onclick=function(event){progressControl(event,progress)});windowAudioContext();var playList=myself.playList;myself.audio.src=playList[0].mp3;var songTitle=document.createElement("div");songTitle.id="songTitle",songInfo.appendChild(songTitle);var album=document.createElement("div");album.id="album",songInfo.appendChild(album);var span=document.createElement("span");span.innerHTML="-",songInfo.appendChild(span),myself.division=span;var artist=document.createElement("div");artist.id="artist",songInfo.appendChild(artist),myself.nowPlay=0,updates(),myself.audio.volume=volumeGetCookie(),myself.autoPlay&&play()}function play(){var playBtn=document.getElementById("playControl"),playing;myself.audio.paused?myself.audio.play().then((function(){playBtn&&(playBtn.setAttribute("data","play"),playBtn.title="暂停",playBtn.innerHTML=""),timer=setInterval((function(){showTime(),playerState()}),1e3),updates(),0==myself.handle&&playHandle();var tips=document.getElementsByClassName("visualizer-player-tips");tips.length>0&&myself.player.removeChild(tips[0])})).catch((function(){var tips=document.createElement("div");return tips.className="visualizer-player-tips",tips.innerHTML="浏览器不支持自动播放,点我开始播放",tips.onclick=function(){myself.player.removeChild(tips),play()},myself.player.appendChild(tips),!1})):(myself.audio.pause(),playBtn&&(playBtn.setAttribute("data","pause"),playBtn.title="播放",playBtn.innerHTML=""),window.clearInterval(timer));myself.event({eventType:"play",describe:"播放/暂停",playing:!myself.audio.paused})}function updates(){var list=myself.playList,nowPlay=myself.nowPlay,songTitle=document.getElementById("songTitle");songTitle.innerHTML=list[nowPlay].title,songTitle.title="歌曲:"+list[nowPlay].title;var songAlbum=document.getElementById("album"),albumTitle=list[nowPlay].album||"";songAlbum.innerHTML="("+albumTitle+")",songAlbum.style.display=albumTitle?"block":"none",songAlbum.title="所属专辑:"+albumTitle;var songArtist=document.getElementById("artist"),artistName=list[nowPlay].artist;myself.division.style.display=artistName?"block":"none",songArtist.innerHTML=artistName,songArtist.title="艺术家:"+artistName}function playerState(){var state=myself.audio.readyState,playerState=document.getElementById("player-state"),songInfo=myself.songInfo;4==state?null!=playerState&&(songInfo.removeChild(playerState),window.clearTimeout(overtime)):null==playerState&&((playerState=document.createElement("div")).className="player-state",playerState.id="player-state",playerState.innerHTML=" 加载中……",songInfo.appendChild(playerState),overtime=setTimeout((function(){0==myself.audio.readyState&&(playerState.innerHTML="加载失败!")}),12e4))}function showTime(){if(4==myself.audio.readyState){var duration=myself.audio.duration,currentTime=myself.audio.currentTime,surplusTime=duration-currentTime,ratio=(currentTime/duration*100).toFixed(1);function timeFormat(t){return Math.floor(t/60)+":"+(t%60/100).toFixed(2).slice(-2)}ratio=100==ratio?100:ratio,myself.playerTime.innerHTML="- "+timeFormat(surplusTime)+" / "+timeFormat(duration)+" "+ratio+"%",myself.playerProgressBar.style.width=ratio+"%",100==ratio&&next()}else myself.playerTime.innerHTML="- 00:00 / 00:00 0%"}function prev(){0==myself.nowPlay&&(myself.nowPlay=myself.playList.length),myself.nowPlay=myself.nowPlay-1,myself.audio.src=myself.playList[myself.nowPlay].mp3,window.clearInterval(timer),null!=myself.analyser&&drawSpectrum(myself.analyser),myself.event({eventType:"prev",describe:"播放上一首"}),play()}function next(){myself.nowPlay==myself.playList.length-1&&(myself.nowPlay=-1),myself.nowPlay=myself.nowPlay+1,myself.audio.src=myself.playList[myself.nowPlay].mp3,window.clearInterval(timer),null!=myself.analyser&&drawSpectrum(myself.analyser),myself.event({eventType:"next",describe:"播放上一首"}),play()}function volume(){if(myself.button.volume){var volumeBtn=document.getElementById("playVolume"),data;"normal"==volumeBtn.getAttribute("data")?(volumeBtn.setAttribute("data","mute"),volumeBtn.innerHTML=""):(volumeBtn.setAttribute("data","normal"),volumeBtn.innerHTML="")}myself.audio.muted?myself.audio.muted=!1:myself.audio.muted=!0}function volumeChange(e,volumeBar,volumeSize){var offsetX,width,proportion=e.offsetX/volumeBar.offsetWidth;volumeSize.style.width=100*proportion+"%";var size=proportion;myself.audio.volume=size,volumeSetCookie(size)}function volumeSetCookie(size){var d=new Date;d.setHours(d.getHours()+720),document.cookie="playerVolume="+size+";expires="+d.toGMTString()}function volumeGetCookie(){var volumeSize=document.getElementById("volumeSize"),arr,reg=new RegExp("(^| )playerVolume=([^;]*)(;|$)"),volume=1;return volume=(arr=document.cookie.match(reg))?unescape(arr[2]):.5,volumeSize.style.width=100*volume+"%",volume}function progressControl(e,progress){var offsetX,width,proportion=e.offsetX/progress.offsetWidth,duration,playTime=myself.audio.duration*proportion;myself.audio.currentTime=playTime}function playHandle(){if(window.ActiveXObject||"ActiveXObject"in window)return!1;windowAudioContext();var audioContext=myself.audioContext,analyser=audioContext.createAnalyser(),playData;audioContext.createMediaElementSource(myself.audio).connect(analyser),analyser.connect(audioContext.destination),drawSpectrum(analyser),myself.analyser=analyser,myself.handle=1}function drawSpectrum(analyser){var colorArray=["#f82466","#00FFFF","#AFFF7C","#FFAA6A","#6AD5FF","#D26AFF","#FF6AE6","#FF6AB8","#FF6A6A","#7091FF"],colorRandom=Math.floor(Math.random()*colorArray.length);myself.colour=myself.color||colorArray[colorRandom];var effectArray=[1,2,3],effectRandom=Math.floor(Math.random()*effectArray.length),effect;switch(myself.effect||effectArray[effectRandom]){case 1:bar(analyser);break;case 2:circular(analyser);break;case 3:line(analyser);break;default:bar(analyser)}}function colorRgb(color,opacity){var reg;if(opacity=(opacity=opacity<0?0:opacity)>1?1:opacity,color&&/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(color)){if(4===color.length){for(var sColorNew="#",i=1;i<4;i+=1)sColorNew+=color.slice(i,i+1).concat(color.slice(i,i+1));color=sColorNew}for(var sColorChange=[],i=1;i<7;i+=2)sColorChange.push(parseInt("0x"+color.slice(i,i+2)));return"rgba("+sColorChange.join(",")+","+opacity+")"}return color}function bar(analyser){var canvas=myself.canvas,cwidth=canvas.width,cheight=canvas.height-2,meterWidth=10,capHeight=2,capStyle="#FFFFFF",meterNum=1e3/12,ctx=canvas.getContext("2d"),capYPositionArray=[],gradient=ctx.createLinearGradient(0,0,0,300);gradient.addColorStop(1,myself.colour);var drawMeter=function(){var array=new Uint8Array(analyser.frequencyBinCount);analyser.getByteFrequencyData(array);var step=Math.round(array.length/(1e3/12));ctx.clearRect(0,0,cwidth,cheight);for(var i=0;i<1e3/12;i++){var value=array[i*step];capYPositionArray.length