├── .eslintrc
├── .gitignore
├── .npmignore
├── .stylelintrc
├── LICENSE
├── README.md
├── dist
├── MusicPlayer.js
├── MusicPlayer.scss
└── components
│ └── Progress
│ ├── index.js
│ └── index.scss
├── package.json
├── public
├── index.html
└── manifest.json
├── src
├── MusicPlayer.js
├── MusicPlayer.scss
├── components
│ └── Progress
│ │ ├── index.js
│ │ └── index.scss
├── examples
│ ├── BasicPlayer
│ │ └── index.js
│ ├── ExampleComponent.js
│ ├── VerticalPlayer
│ │ └── index.js
│ └── playlist.js
└── index.js
└── yarn.lock
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": ["airbnb", "prettier"],
4 | "plugins": ["react", "jsx-a11y", "import"],
5 | "rules": {
6 | "react/forbid-prop-types": "off",
7 | "react/prefer-stateless-function": "off",
8 | "react/prop-types": "off",
9 | "react/no-danger": "off",
10 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
11 | "react/jsx-one-expression-per-line": "off",
12 | "react/jsx-wrap-multilines": "off",
13 | "linebreak-style": "off",
14 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
15 | "no-shadow": "off",
16 | "jsx-a11y/anchor-is-valid": "off"
17 | },
18 | "settings": {
19 | "import/core-modules": []
20 | },
21 | "env": {
22 | "browser": true
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | # css
24 | src/**/*.css
25 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | build
2 | node_modules
3 | public
4 | src
5 | .eslintrc
6 | .stylelintrc
7 | .gitignore
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-recommended-scss",
3 | "rules": {
4 | "indentation": 2
5 | }
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Shixiang Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Responsive Music Player
2 |
3 | ## Example
4 |
5 | Please see a [demo](http://alickzhang.github.io/react-responsive-music-player/).
6 |
7 | 
8 | 
9 |
10 | ## Install
11 |
12 | ```bash
13 | npm install react-responsive-music-player --save
14 | ```
15 |
16 | or
17 |
18 | ```bash
19 | yarn add react-responsive-music-player
20 | ```
21 |
22 | ## Usage
23 |
24 | ```jsx
25 | import React, { Component } from 'react';
26 | import MusicPlayer from 'react-responsive-music-player';
27 |
28 | class App extends Component {
29 | render() {
30 | return (
31 |
32 |
33 |
34 | );
35 | }
36 | }
37 | ```
38 |
39 | ## API
40 |
41 | ### props
42 |
43 | | prop | type | default | notes |
44 | | ------------- | -------------- | ---------- | ------------------------------------------------ |
45 | | mode | string | horizontal | set the layout of player: vertical or horizontal |
46 | | width | number, string | 100% |
47 | | autoplay | bool | false |
48 | | progressColor | string | #66cccc | the color of the progress |
49 | | btnColor | string | #4a4a4a | the color of the buttons |
50 | | playlist | array | [] | the playlist |
51 | | style | object | {} |
52 |
53 | ### JSON
54 |
55 | ```
56 | const playlist = [
57 | {
58 | url: 'path/to/mp3',
59 | cover: 'path/to/jpg',
60 | title: 'Despacito',
61 | artist: [
62 | 'Luis Fonsi',
63 | 'Daddy Yankee'
64 | ]
65 | },
66 | {
67 | url: 'path/to/mp3',
68 | cover: 'path/to/jpg',
69 | title: 'Bedtime Stories',
70 | artist: [
71 | 'Jay Chou'
72 | ]
73 | }
74 | ]
75 | ```
76 |
77 | ## Development
78 |
79 | ```bash
80 | npm install
81 | npm start
82 | ```
83 |
84 | ## License
85 |
86 | MIT License
87 |
88 | Copyright (c) 2019 Shixiang Zhang
89 |
--------------------------------------------------------------------------------
/dist/MusicPlayer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _react = _interopRequireWildcard(require("react"));
9 |
10 | var _propTypes = _interopRequireDefault(require("prop-types"));
11 |
12 | var _classnames = _interopRequireDefault(require("classnames"));
13 |
14 | var _Progress = _interopRequireDefault(require("./components/Progress"));
15 |
16 | require("./MusicPlayer.scss");
17 |
18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19 |
20 | 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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
21 |
22 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
23 |
24 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
25 |
26 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
27 |
28 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
29 |
30 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
31 |
32 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
33 |
34 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
35 |
36 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
37 |
38 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
39 |
40 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
41 |
42 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
43 |
44 | var formatTime = function formatTime(time) {
45 | /* eslint no-restricted-globals: off */
46 | if (isNaN(time) || time === 0) {
47 | return '';
48 | }
49 |
50 | var mins = Math.floor(time / 60);
51 | var secs = (time % 60).toFixed();
52 | return "".concat(mins < 10 ? '0' : '').concat(mins, ":").concat(secs < 10 ? '0' : '').concat(secs);
53 | };
54 |
55 | var processArtistName = function processArtistName(artistList) {
56 | return artistList.join(' / ');
57 | };
58 |
59 | var getPlayModeClass = function getPlayModeClass(playMode) {
60 | if (playMode === 'loop') return 'refresh';
61 | if (playMode === 'random') return 'random';
62 | return 'repeat';
63 | };
64 |
65 | var MusicPlayer =
66 | /*#__PURE__*/
67 | function (_Component) {
68 | _inherits(MusicPlayer, _Component);
69 |
70 | function MusicPlayer(props) {
71 | var _this;
72 |
73 | _classCallCheck(this, MusicPlayer);
74 |
75 | _this = _possibleConstructorReturn(this, _getPrototypeOf(MusicPlayer).call(this, props));
76 |
77 | _defineProperty(_assertThisInitialized(_this), "updateProgress", function () {
78 | var _this$audioContainer$ = _this.audioContainer.current,
79 | duration = _this$audioContainer$.duration,
80 | currentTime = _this$audioContainer$.currentTime;
81 | var progress = currentTime / duration || 0;
82 |
83 | _this.setState({
84 | progress: progress,
85 | leftTime: duration - currentTime
86 | });
87 | });
88 |
89 | _defineProperty(_assertThisInitialized(_this), "end", function () {
90 | _this.handleNext();
91 | });
92 |
93 | _defineProperty(_assertThisInitialized(_this), "handleAdjustProgress", function (value) {
94 | var currentTime = _this.audioContainer.current.duration * value;
95 | _this.audioContainer.current.currentTime = currentTime;
96 |
97 | _this.setState({
98 | play: true,
99 | progress: value
100 | }, function () {
101 | return _this.audioContainer.current.play();
102 | });
103 | });
104 |
105 | _defineProperty(_assertThisInitialized(_this), "handleAdjustVolume", function (value) {
106 | var volume = value < 0 ? 0 : value;
107 | _this.audioContainer.current.volume = volume;
108 |
109 | _this.setState({
110 | volume: volume
111 | });
112 | });
113 |
114 | _defineProperty(_assertThisInitialized(_this), "handleToggle", function () {
115 | var play = _this.state.play;
116 |
117 | if (play) {
118 | _this.audioContainer.current.pause();
119 | } else {
120 | _this.audioContainer.current.play();
121 | }
122 |
123 | _this.setState(function (_ref) {
124 | var play = _ref.play;
125 | return {
126 | play: !play
127 | };
128 | });
129 | });
130 |
131 | _defineProperty(_assertThisInitialized(_this), "handlePrev", function () {
132 | var playlist = _this.props.playlist;
133 | var _this$state = _this.state,
134 | playMode = _this$state.playMode,
135 | activeMusicIndex = _this$state.activeMusicIndex;
136 |
137 | if (playMode === 'repeat') {
138 | _this.playMusic(activeMusicIndex);
139 | } else if (playMode === 'loop') {
140 | var total = playlist.length;
141 | var index = activeMusicIndex > 0 ? activeMusicIndex - 1 : total - 1;
142 |
143 | _this.playMusic(index);
144 | } else if (playMode === 'random') {
145 | var randomIndex = Math.floor(Math.random() * playlist.length);
146 |
147 | while (randomIndex === activeMusicIndex) {
148 | randomIndex = Math.floor(Math.random() * playlist.length);
149 | }
150 |
151 | _this.playMusic(randomIndex);
152 | } else {
153 | _this.setState({
154 | play: false
155 | });
156 | }
157 | });
158 |
159 | _defineProperty(_assertThisInitialized(_this), "handleNext", function () {
160 | var playlist = _this.props.playlist;
161 | var _this$state2 = _this.state,
162 | playMode = _this$state2.playMode,
163 | activeMusicIndex = _this$state2.activeMusicIndex;
164 |
165 | if (playMode === 'repeat') {
166 | _this.playMusic(activeMusicIndex);
167 | } else if (playMode === 'loop') {
168 | var total = playlist.length;
169 | var index = activeMusicIndex < total - 1 ? activeMusicIndex + 1 : 0;
170 |
171 | _this.playMusic(index);
172 | } else if (playMode === 'random') {
173 | var randomIndex = Math.floor(Math.random() * playlist.length);
174 |
175 | while (randomIndex === activeMusicIndex) {
176 | randomIndex = Math.floor(Math.random() * playlist.length);
177 | }
178 |
179 | _this.playMusic(randomIndex);
180 | } else {
181 | _this.setState({
182 | play: false
183 | });
184 | }
185 | });
186 |
187 | _defineProperty(_assertThisInitialized(_this), "handleChangePlayMode", function () {
188 | var playMode = _this.state.playMode;
189 |
190 | var index = _this.modeList.indexOf(playMode);
191 |
192 | index = (index + 1) % _this.modeList.length;
193 |
194 | _this.setState({
195 | playMode: _this.modeList[index]
196 | });
197 | });
198 |
199 | _defineProperty(_assertThisInitialized(_this), "playMusic", function (index) {
200 | _this.setState({
201 | activeMusicIndex: index,
202 | leftTime: 0,
203 | play: true,
204 | progress: 0
205 | }, function () {
206 | _this.audioContainer.current.currentTime = 0;
207 |
208 | _this.audioContainer.current.play();
209 | });
210 | });
211 |
212 | _this.state = {
213 | activeMusicIndex: 0,
214 | leftTime: 0,
215 | play: props.autoplay || false,
216 | playMode: 'loop',
217 | progress: 0,
218 | volume: 1
219 | };
220 | _this.modeList = ['loop', 'random', 'repeat'];
221 | _this.audioContainer = _react.default.createRef();
222 | return _this;
223 | }
224 |
225 | _createClass(MusicPlayer, [{
226 | key: "componentDidMount",
227 | value: function componentDidMount() {
228 | this.audioContainer.current.addEventListener('timeupdate', this.updateProgress);
229 | this.audioContainer.current.addEventListener('ended', this.end);
230 | }
231 | }, {
232 | key: "componentWillUnmount",
233 | value: function componentWillUnmount() {
234 | this.audioContainer.current.removeEventListener('timeupdate', this.updateProgress);
235 | this.audioContainer.current.removeEventListener('ended', this.end);
236 | }
237 | }, {
238 | key: "render",
239 | value: function render() {
240 | var _this$props = this.props,
241 | playlist = _this$props.playlist,
242 | mode = _this$props.mode,
243 | width = _this$props.width,
244 | progressColor = _this$props.progressColor,
245 | btnColor = _this$props.btnColor,
246 | style = _this$props.style;
247 | var _this$state3 = this.state,
248 | play = _this$state3.play,
249 | progress = _this$state3.progress,
250 | leftTime = _this$state3.leftTime,
251 | volume = _this$state3.volume,
252 | activeMusicIndex = _this$state3.activeMusicIndex,
253 | playMode = _this$state3.playMode;
254 | var activeMusic = playlist[activeMusicIndex];
255 | var playModeClass = getPlayModeClass(playMode);
256 | var btnStyle = {
257 | color: btnColor
258 | };
259 | return _react.default.createElement("div", {
260 | className: (0, _classnames.default)('player', {
261 | vertical: mode === 'vertical'
262 | }),
263 | style: _objectSpread({}, style, {
264 | width: typeof width === 'string' ? width : "".concat(width, "px")
265 | })
266 | }, _react.default.createElement("audio", {
267 | autoPlay: play,
268 | preload: "auto",
269 | ref: this.audioContainer,
270 | src: activeMusic.url
271 | }, _react.default.createElement("track", {
272 | kind: "captions"
273 | })), _react.default.createElement("div", {
274 | className: "player-control"
275 | }, _react.default.createElement("div", {
276 | className: "music-info"
277 | }, _react.default.createElement("h2", {
278 | className: "title"
279 | }, activeMusic.title), _react.default.createElement("h3", {
280 | className: "artist"
281 | }, processArtistName(activeMusic.artist))), _react.default.createElement("div", {
282 | className: "time-and-volume"
283 | }, _react.default.createElement("div", {
284 | className: "time-remaining"
285 | }, "-", formatTime(leftTime)), _react.default.createElement("div", {
286 | className: "volume-control"
287 | }, _react.default.createElement("i", {
288 | className: "volume-icon fa fa-volume-up"
289 | }), _react.default.createElement("div", {
290 | className: "volume-bar"
291 | }, _react.default.createElement(_Progress.default, {
292 | percent: volume,
293 | onClick: this.handleAdjustVolume
294 | })))), _react.default.createElement(_Progress.default, {
295 | percent: progress,
296 | strokeColor: progressColor,
297 | onClick: this.handleAdjustProgress
298 | }), _react.default.createElement("div", {
299 | className: "controls"
300 | }, _react.default.createElement("button", {
301 | type: "button",
302 | className: "fa fa-".concat(playModeClass),
303 | style: btnStyle,
304 | onClick: this.handleChangePlayMode
305 | }), _react.default.createElement("button", {
306 | type: "button",
307 | className: "fa fa-step-backward",
308 | style: btnStyle,
309 | onClick: this.handlePrev
310 | }), _react.default.createElement("button", {
311 | type: "button",
312 | className: "fa fa-".concat(play ? 'pause' : 'play'),
313 | style: btnStyle,
314 | onClick: this.handleToggle
315 | }), _react.default.createElement("button", {
316 | type: "button",
317 | className: "fa fa-step-forward",
318 | style: btnStyle,
319 | onClick: this.handleNext
320 | }))), _react.default.createElement("div", {
321 | className: "player-cover",
322 | style: {
323 | backgroundImage: "url(".concat(activeMusic.cover, ")")
324 | }
325 | }));
326 | }
327 | }]);
328 |
329 | return MusicPlayer;
330 | }(_react.Component);
331 |
332 | exports.default = MusicPlayer;
333 |
334 | _defineProperty(MusicPlayer, "propTypes", {
335 | playlist: _propTypes.default.arrayOf(_propTypes.default.shape({
336 | url: _propTypes.default.string,
337 | cover: _propTypes.default.string,
338 | title: _propTypes.default.string,
339 | artist: _propTypes.default.arrayOf(_propTypes.default.string)
340 | })).isRequired,
341 | mode: _propTypes.default.oneOf(['horizontal', 'vertical']),
342 | width: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]),
343 | autoplay: _propTypes.default.bool,
344 | progressColor: _propTypes.default.string,
345 | btnColor: _propTypes.default.string,
346 | style: _propTypes.default.object
347 | });
348 |
349 | _defineProperty(MusicPlayer, "defaultProps", {
350 | mode: 'horizontal',
351 | width: '100%',
352 | autoplay: false,
353 | progressColor: '#66cccc',
354 | btnColor: '#4a4a4a',
355 | style: {}
356 | });
--------------------------------------------------------------------------------
/dist/MusicPlayer.scss:
--------------------------------------------------------------------------------
1 | @import '~normalize.css/normalize.css';
2 | @import '~font-awesome/css/font-awesome.css';
3 |
4 | $font-weight: 400 !default;
5 | $font-size-lg: 1.5em !default;
6 | $font-size-md: 1em !default;
7 | $font-size-sm: 0.8em !default;
8 | $player-time-width: 3.2em !default;
9 | $player-title-color: #030303 !default;
10 | $player-artist-color: #4a4a4a !default;
11 | $player-time-color: #9b9b9b !default;
12 | $player-volume-width: 50px !default;
13 | $player-control-font-size: 28px !default;
14 | $player-cover-size: 240px !default;
15 |
16 | * {
17 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, PingFang SC, Source Han Sans CN,
18 | Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif;
19 | text-rendering: optimizeLegibility;
20 | -webkit-font-smoothing: antialiased;
21 | -moz-osx-font-smoothing: grayscale;
22 | }
23 |
24 | .player {
25 | max-width: 800px;
26 | display: flex;
27 |
28 | .player-control {
29 | flex: 1;
30 |
31 | .music-info {
32 | .title,
33 | .artist {
34 | font-weight: $font-weight;
35 | text-overflow: ellipsis;
36 | overflow: hidden;
37 | white-space: nowrap;
38 | }
39 |
40 | .title {
41 | margin-bottom: 0;
42 | color: $player-title-color;
43 | font-size: $font-size-lg;
44 | }
45 |
46 | .artist {
47 | color: $player-artist-color;
48 | font-size: $font-size-md;
49 | }
50 | }
51 |
52 | .time-and-volume {
53 | display: flex;
54 | color: $player-time-color;
55 | font-size: $font-size-sm;
56 | line-height: 2;
57 |
58 | .time-remaining {
59 | width: $player-time-width;
60 | margin-right: 10px;
61 | text-align: left;
62 | font-weight: $font-weight;
63 | }
64 |
65 | .volume-control {
66 | display: flex;
67 | align-items: center;
68 |
69 | .volume-icon {
70 | margin-right: 4px;
71 | color: $player-time-color;
72 | cursor: pointer;
73 | }
74 |
75 | .volume-bar {
76 | width: $player-volume-width;
77 | opacity: 0;
78 | transition: opacity 0.5s linear;
79 | }
80 |
81 | &:hover {
82 | .volume-bar {
83 | opacity: 1;
84 | }
85 | }
86 | }
87 | }
88 |
89 | .controls {
90 | margin-top: 30px;
91 | display: flex;
92 | justify-content: space-between;
93 |
94 | button {
95 | width: 50px;
96 | margin: 0;
97 | padding: 0;
98 | font-size: $player-control-font-size;
99 | background-color: transparent;
100 | border: none;
101 | box-shadow: none;
102 | outline: none;
103 | user-select: none;
104 | touch-action: manipulation;
105 | cursor: pointer;
106 |
107 | &:first-child {
108 | margin-right: auto;
109 | }
110 | }
111 | }
112 | }
113 |
114 | .player-cover {
115 | width: $player-cover-size;
116 | height: $player-cover-size;
117 | margin-left: 20px;
118 | flex: none;
119 | border-radius: 50%;
120 | background-size: cover;
121 | background-position: center;
122 | background-repeat: no-repeat;
123 | }
124 |
125 | &.vertical {
126 | flex-direction: column-reverse;
127 |
128 | .player-control {
129 | .music-info {
130 | text-align: center;
131 | }
132 |
133 | .controls {
134 | justify-content: space-evenly;
135 |
136 | button {
137 | &:first-child {
138 | margin: 0;
139 | }
140 | }
141 | }
142 | }
143 |
144 | .player-cover {
145 | margin: 0 auto;
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/dist/components/Progress/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | var _react = _interopRequireWildcard(require("react"));
9 |
10 | var _propTypes = _interopRequireDefault(require("prop-types"));
11 |
12 | require("./index.scss");
13 |
14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15 |
16 | 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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
17 |
18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
19 |
20 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
21 |
22 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
23 |
24 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
25 |
26 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
27 |
28 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
29 |
30 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
31 |
32 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
33 |
34 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
35 |
36 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
37 |
38 | var Progress =
39 | /*#__PURE__*/
40 | function (_Component) {
41 | _inherits(Progress, _Component);
42 |
43 | function Progress() {
44 | var _this;
45 |
46 | _classCallCheck(this, Progress);
47 |
48 | _this = _possibleConstructorReturn(this, _getPrototypeOf(Progress).call(this));
49 |
50 | _defineProperty(_assertThisInitialized(_this), "onClick", function (_ref) {
51 | var clientX = _ref.clientX;
52 | var onClick = _this.props.onClick;
53 | var progressRef = _this.progressContainer.current;
54 | var progress = (clientX - progressRef.getBoundingClientRect().left) / progressRef.clientWidth;
55 | onClick(progress);
56 | });
57 |
58 | _defineProperty(_assertThisInitialized(_this), "onKeyDown", function (_ref2) {
59 | var keyCode = _ref2.keyCode;
60 | var _this$props = _this.props,
61 | percent = _this$props.percent,
62 | onClick = _this$props.onClick;
63 |
64 | switch (keyCode) {
65 | case 37:
66 | case 40:
67 | onClick(Math.max(percent - 0.05, 0));
68 | break;
69 |
70 | case 38:
71 | case 39:
72 | onClick(Math.min(percent + 0.05, 0.9999));
73 | break;
74 |
75 | default:
76 | break;
77 | }
78 | });
79 |
80 | _this.progressContainer = _react.default.createRef();
81 | return _this;
82 | }
83 |
84 | _createClass(Progress, [{
85 | key: "render",
86 | value: function render() {
87 | var _this$props2 = this.props,
88 | percent = _this$props2.percent,
89 | strokeColor = _this$props2.strokeColor,
90 | strokeWidth = _this$props2.strokeWidth;
91 | return _react.default.createElement("div", {
92 | ref: this.progressContainer,
93 | role: "progressbar",
94 | tabIndex: "-1",
95 | className: "progress",
96 | style: {
97 | height: "".concat(strokeWidth, "px")
98 | },
99 | onClick: this.onClick,
100 | onKeyDown: this.onKeyDown
101 | }, _react.default.createElement("div", {
102 | className: "progress-inner",
103 | style: {
104 | width: "".concat(percent * 100, "%"),
105 | backgroundColor: strokeColor
106 | }
107 | }));
108 | }
109 | }]);
110 |
111 | return Progress;
112 | }(_react.Component);
113 |
114 | exports.default = Progress;
115 |
116 | _defineProperty(Progress, "propTypes", {
117 | percent: _propTypes.default.number,
118 | strokeColor: _propTypes.default.string,
119 | strokeWidth: _propTypes.default.number
120 | });
121 |
122 | _defineProperty(Progress, "defaultProps", {
123 | percent: 0,
124 | strokeColor: '#9b9b9b',
125 | strokeWidth: 2
126 | });
--------------------------------------------------------------------------------
/dist/components/Progress/index.scss:
--------------------------------------------------------------------------------
1 | .progress {
2 | width: 100%;
3 | background-color: #dadada;
4 | outline: none;
5 | cursor: pointer;
6 |
7 | .progress-inner {
8 | height: 100%;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-responsive-music-player",
3 | "version": "0.4.3",
4 | "description": "A responsive music player built with React",
5 | "main": "dist/MusicPlayer.js",
6 | "module": "dist/MusicPlayer.js",
7 | "babel": {
8 | "presets": [
9 | "@babel/preset-env",
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "@babel/plugin-proposal-class-properties"
14 | ]
15 | },
16 | "keywords": [
17 | "react",
18 | "react-component",
19 | "music",
20 | "player",
21 | "audio"
22 | ],
23 | "author": "Alick Zhang",
24 | "license": "MIT",
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/alickzhang/react-responsive-music-player.git"
28 | },
29 | "homepage": "http://alickzhang.github.io/react-responsive-music-player",
30 | "bugs": {
31 | "url": "https://github.com/alickzhang/react-responsive-music-player/issues"
32 | },
33 | "dependencies": {
34 | "classnames": "^2.2.6",
35 | "font-awesome": "^4.7.0",
36 | "gh-pages": "^2.0.1",
37 | "node-sass": "^4.11.0",
38 | "normalize.css": "^8.0.1",
39 | "prop-types": "^15.7.2",
40 | "react": "^16.8.6",
41 | "react-dom": "^16.8.6",
42 | "react-scripts": "^2.1.8"
43 | },
44 | "devDependencies": {
45 | "@babel/cli": "^7.4.3",
46 | "@babel/plugin-proposal-class-properties": "^7.4.0",
47 | "@babel/preset-env": "^7.4.3",
48 | "@babel/preset-react": "^7.0.0",
49 | "babel-plugin-import": "^1.11.0",
50 | "eslint-config-airbnb": "^17.1.0",
51 | "eslint-config-prettier": "^4.1.0",
52 | "eslint-plugin-import": "^2.17.2",
53 | "eslint-plugin-jsx-a11y": "^6.2.1",
54 | "eslint-plugin-react": "^7.12.4",
55 | "prettier": "^1.16.4",
56 | "stylelint": "^10.0.1",
57 | "stylelint-config-recommended-scss": "^3.2.0",
58 | "stylelint-scss": "^3.6.0"
59 | },
60 | "scripts": {
61 | "start": "react-scripts start",
62 | "build": "react-scripts build",
63 | "test": "react-scripts test",
64 | "eslint": "./node_modules/.bin/eslint --quiet ./src/",
65 | "eject": "react-scripts eject",
66 | "publish:npm": "NODE_ENV=production && rm -rf dist && mkdir dist && npx babel src --out-dir dist --copy-files && rm -rf dist/examples && rm -rf dist/index.js",
67 | "predeploy": "npm run build",
68 | "deploy": "gh-pages -d build"
69 | },
70 | "browserslist": [
71 | ">0.2%",
72 | "not dead",
73 | "not ie <= 11",
74 | "not op_mini all"
75 | ]
76 | }
77 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 | React Responsive Music Player
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/MusicPlayer.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 | import Progress from './components/Progress';
5 | import './MusicPlayer.scss';
6 |
7 | const formatTime = time => {
8 | /* eslint no-restricted-globals: off */
9 | if (isNaN(time) || time === 0) {
10 | return '';
11 | }
12 | const mins = Math.floor(time / 60);
13 | const secs = (time % 60).toFixed();
14 | return `${mins < 10 ? '0' : ''}${mins}:${secs < 10 ? '0' : ''}${secs}`;
15 | };
16 |
17 | const processArtistName = artistList => artistList.join(' / ');
18 |
19 | const getPlayModeClass = playMode => {
20 | if (playMode === 'loop') return 'refresh';
21 | if (playMode === 'random') return 'random';
22 | return 'repeat';
23 | };
24 |
25 | export default class MusicPlayer extends Component {
26 | static propTypes = {
27 | playlist: PropTypes.arrayOf(
28 | PropTypes.shape({
29 | url: PropTypes.string,
30 | cover: PropTypes.string,
31 | title: PropTypes.string,
32 | artist: PropTypes.arrayOf(PropTypes.string)
33 | })
34 | ).isRequired,
35 | mode: PropTypes.oneOf(['horizontal', 'vertical']),
36 | width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
37 | autoplay: PropTypes.bool,
38 | progressColor: PropTypes.string,
39 | btnColor: PropTypes.string,
40 | style: PropTypes.object
41 | };
42 |
43 | static defaultProps = {
44 | mode: 'horizontal',
45 | width: '100%',
46 | autoplay: false,
47 | progressColor: '#66cccc',
48 | btnColor: '#4a4a4a',
49 | style: {}
50 | };
51 |
52 | constructor(props) {
53 | super(props);
54 | this.state = {
55 | activeMusicIndex: 0,
56 | leftTime: 0,
57 | play: props.autoplay || false,
58 | playMode: 'loop',
59 | progress: 0,
60 | volume: 1
61 | };
62 | this.modeList = ['loop', 'random', 'repeat'];
63 | this.audioContainer = React.createRef();
64 | }
65 |
66 | componentDidMount() {
67 | this.audioContainer.current.addEventListener('timeupdate', this.updateProgress);
68 | this.audioContainer.current.addEventListener('ended', this.end);
69 | }
70 |
71 | componentWillUnmount() {
72 | this.audioContainer.current.removeEventListener('timeupdate', this.updateProgress);
73 | this.audioContainer.current.removeEventListener('ended', this.end);
74 | }
75 |
76 | updateProgress = () => {
77 | const { duration, currentTime } = this.audioContainer.current;
78 | const progress = currentTime / duration || 0;
79 | this.setState({ progress, leftTime: duration - currentTime });
80 | };
81 |
82 | end = () => {
83 | this.handleNext();
84 | };
85 |
86 | handleAdjustProgress = value => {
87 | const currentTime = this.audioContainer.current.duration * value;
88 | this.audioContainer.current.currentTime = currentTime;
89 | this.setState({ play: true, progress: value }, () => this.audioContainer.current.play());
90 | };
91 |
92 | handleAdjustVolume = value => {
93 | const volume = value < 0 ? 0 : value;
94 | this.audioContainer.current.volume = volume;
95 | this.setState({ volume });
96 | };
97 |
98 | handleToggle = () => {
99 | const { play } = this.state;
100 | if (play) {
101 | this.audioContainer.current.pause();
102 | } else {
103 | this.audioContainer.current.play();
104 | }
105 | this.setState(({ play }) => ({ play: !play }));
106 | };
107 |
108 | handlePrev = () => {
109 | const { playlist } = this.props;
110 | const { playMode, activeMusicIndex } = this.state;
111 | if (playMode === 'repeat') {
112 | this.playMusic(activeMusicIndex);
113 | } else if (playMode === 'loop') {
114 | const total = playlist.length;
115 | const index = activeMusicIndex > 0 ? activeMusicIndex - 1 : total - 1;
116 | this.playMusic(index);
117 | } else if (playMode === 'random') {
118 | let randomIndex = Math.floor(Math.random() * playlist.length);
119 | while (randomIndex === activeMusicIndex) {
120 | randomIndex = Math.floor(Math.random() * playlist.length);
121 | }
122 | this.playMusic(randomIndex);
123 | } else {
124 | this.setState({ play: false });
125 | }
126 | };
127 |
128 | handleNext = () => {
129 | const { playlist } = this.props;
130 | const { playMode, activeMusicIndex } = this.state;
131 | if (playMode === 'repeat') {
132 | this.playMusic(activeMusicIndex);
133 | } else if (playMode === 'loop') {
134 | const total = playlist.length;
135 | const index = activeMusicIndex < total - 1 ? activeMusicIndex + 1 : 0;
136 | this.playMusic(index);
137 | } else if (playMode === 'random') {
138 | let randomIndex = Math.floor(Math.random() * playlist.length);
139 | while (randomIndex === activeMusicIndex) {
140 | randomIndex = Math.floor(Math.random() * playlist.length);
141 | }
142 | this.playMusic(randomIndex);
143 | } else {
144 | this.setState({ play: false });
145 | }
146 | };
147 |
148 | handleChangePlayMode = () => {
149 | const { playMode } = this.state;
150 | let index = this.modeList.indexOf(playMode);
151 | index = (index + 1) % this.modeList.length;
152 | this.setState({ playMode: this.modeList[index] });
153 | };
154 |
155 | playMusic = index => {
156 | this.setState({ activeMusicIndex: index, leftTime: 0, play: true, progress: 0 }, () => {
157 | this.audioContainer.current.currentTime = 0;
158 | this.audioContainer.current.play();
159 | });
160 | };
161 |
162 | render() {
163 | const { playlist, mode, width, progressColor, btnColor, style } = this.props;
164 | const { play, progress, leftTime, volume, activeMusicIndex, playMode } = this.state;
165 | const activeMusic = playlist[activeMusicIndex];
166 | const playModeClass = getPlayModeClass(playMode);
167 | const btnStyle = { color: btnColor };
168 |
169 | return (
170 |
174 |
177 |
178 |
179 |
{activeMusic.title}
180 | {processArtistName(activeMusic.artist)}
181 |
182 |
183 |
-{formatTime(leftTime)}
184 |
190 |
191 |
192 |
193 |
199 |
200 |
206 |
207 |
208 |
209 |
210 |
211 | );
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/MusicPlayer.scss:
--------------------------------------------------------------------------------
1 | @import '~normalize.css/normalize.css';
2 | @import '~font-awesome/css/font-awesome.css';
3 |
4 | $font-weight: 400 !default;
5 | $font-size-lg: 1.5em !default;
6 | $font-size-md: 1em !default;
7 | $font-size-sm: 0.8em !default;
8 | $player-time-width: 3.2em !default;
9 | $player-title-color: #030303 !default;
10 | $player-artist-color: #4a4a4a !default;
11 | $player-time-color: #9b9b9b !default;
12 | $player-volume-width: 50px !default;
13 | $player-control-font-size: 28px !default;
14 | $player-cover-size: 240px !default;
15 |
16 | * {
17 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, PingFang SC, Source Han Sans CN,
18 | Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif;
19 | text-rendering: optimizeLegibility;
20 | -webkit-font-smoothing: antialiased;
21 | -moz-osx-font-smoothing: grayscale;
22 | }
23 |
24 | .player {
25 | max-width: 800px;
26 | display: flex;
27 |
28 | .player-control {
29 | flex: 1;
30 |
31 | .music-info {
32 | .title,
33 | .artist {
34 | font-weight: $font-weight;
35 | text-overflow: ellipsis;
36 | overflow: hidden;
37 | white-space: nowrap;
38 | }
39 |
40 | .title {
41 | margin-bottom: 0;
42 | color: $player-title-color;
43 | font-size: $font-size-lg;
44 | }
45 |
46 | .artist {
47 | color: $player-artist-color;
48 | font-size: $font-size-md;
49 | }
50 | }
51 |
52 | .time-and-volume {
53 | display: flex;
54 | color: $player-time-color;
55 | font-size: $font-size-sm;
56 | line-height: 2;
57 |
58 | .time-remaining {
59 | width: $player-time-width;
60 | margin-right: 10px;
61 | text-align: left;
62 | font-weight: $font-weight;
63 | }
64 |
65 | .volume-control {
66 | display: flex;
67 | align-items: center;
68 |
69 | .volume-icon {
70 | margin-right: 4px;
71 | color: $player-time-color;
72 | cursor: pointer;
73 | }
74 |
75 | .volume-bar {
76 | width: $player-volume-width;
77 | opacity: 0;
78 | transition: opacity 0.5s linear;
79 | }
80 |
81 | &:hover {
82 | .volume-bar {
83 | opacity: 1;
84 | }
85 | }
86 | }
87 | }
88 |
89 | .controls {
90 | margin-top: 30px;
91 | display: flex;
92 | justify-content: space-between;
93 |
94 | button {
95 | width: 50px;
96 | margin: 0;
97 | padding: 0;
98 | font-size: $player-control-font-size;
99 | background-color: transparent;
100 | border: none;
101 | box-shadow: none;
102 | outline: none;
103 | user-select: none;
104 | touch-action: manipulation;
105 | cursor: pointer;
106 |
107 | &:first-child {
108 | margin-right: auto;
109 | }
110 | }
111 | }
112 | }
113 |
114 | .player-cover {
115 | width: $player-cover-size;
116 | height: $player-cover-size;
117 | margin-left: 20px;
118 | flex: none;
119 | border-radius: 50%;
120 | background-size: cover;
121 | background-position: center;
122 | background-repeat: no-repeat;
123 | }
124 |
125 | &.vertical {
126 | flex-direction: column-reverse;
127 |
128 | .player-control {
129 | .music-info {
130 | text-align: center;
131 | }
132 |
133 | .controls {
134 | justify-content: space-evenly;
135 |
136 | button {
137 | &:first-child {
138 | margin: 0;
139 | }
140 | }
141 | }
142 | }
143 |
144 | .player-cover {
145 | margin: 0 auto;
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/components/Progress/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import './index.scss';
4 |
5 | export default class Progress extends Component {
6 | static propTypes = {
7 | percent: PropTypes.number,
8 | strokeColor: PropTypes.string,
9 | strokeWidth: PropTypes.number
10 | };
11 |
12 | static defaultProps = {
13 | percent: 0,
14 | strokeColor: '#9b9b9b',
15 | strokeWidth: 2
16 | };
17 |
18 | constructor() {
19 | super();
20 | this.progressContainer = React.createRef();
21 | }
22 |
23 | onClick = ({ clientX }) => {
24 | const { onClick } = this.props;
25 | const progressRef = this.progressContainer.current;
26 | const progress = (clientX - progressRef.getBoundingClientRect().left) / progressRef.clientWidth;
27 | onClick(progress);
28 | };
29 |
30 | onKeyDown = ({ keyCode }) => {
31 | const { percent, onClick } = this.props;
32 | switch (keyCode) {
33 | case 37:
34 | case 40:
35 | onClick(Math.max(percent - 0.05, 0));
36 | break;
37 | case 38:
38 | case 39:
39 | onClick(Math.min(percent + 0.05, 0.9999));
40 | break;
41 | default:
42 | break;
43 | }
44 | };
45 |
46 | render() {
47 | const { percent, strokeColor, strokeWidth } = this.props;
48 | return (
49 |
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/Progress/index.scss:
--------------------------------------------------------------------------------
1 | .progress {
2 | width: 100%;
3 | background-color: #dadada;
4 | outline: none;
5 | cursor: pointer;
6 |
7 | .progress-inner {
8 | height: 100%;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/examples/BasicPlayer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Player from '../../MusicPlayer';
3 | import playlist from '../playlist';
4 |
5 | export default () => (
6 |
9 | );
10 |
--------------------------------------------------------------------------------
/src/examples/ExampleComponent.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import BasicPlayer from './BasicPlayer';
3 | import VerticalPlayer from './VerticalPlayer';
4 |
5 | export default () => (
6 |
7 |
8 |
9 |
10 | );
11 |
--------------------------------------------------------------------------------
/src/examples/VerticalPlayer/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Player from '../../MusicPlayer';
3 | import playlist from '../playlist';
4 |
5 | export default () => ;
6 |
--------------------------------------------------------------------------------
/src/examples/playlist.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | url: 'http://res.cloudinary.com/alick/video/upload/v1502689683/Luis_Fonsi_-_Despacito_ft._Daddy_Yankee_uyvqw9.mp3',
4 | cover: 'http://res.cloudinary.com/alick/image/upload/v1502689731/Despacito_uvolhp.jpg',
5 | title: 'Despacito',
6 | artist: ['Luis Fonsi', 'Daddy Yankee']
7 | },
8 | {
9 | url: 'http://res.cloudinary.com/alick/video/upload/v1502375674/Bedtime_Stories.mp3',
10 | cover: 'http://res.cloudinary.com/alick/image/upload/v1502375978/bedtime_stories_bywggz.jpg',
11 | title: 'Bedtime Stories',
12 | artist: ['Jay Chou']
13 | },
14 | {
15 | url: 'http://res.cloudinary.com/alick/video/upload/v1502444212/Actor_ud8ccw.mp3',
16 | cover: 'http://res.cloudinary.com/alick/image/upload/v1502444304/actor_umzdur.jpg',
17 | title: '演员',
18 | artist: ['薛之谦']
19 | },
20 | {
21 | url: 'http://res.cloudinary.com/alick/video/upload/v1502444215/Bridge_of_Fate_aaksg1.mp3',
22 | cover: 'http://res.cloudinary.com/alick/image/upload/v1502444306/Bridge_of_Fate_o36rem.jpg',
23 | title: 'Bridge of Fate',
24 | artist: ['王力宏', '谭维维']
25 | },
26 | {
27 | url: 'http://res.cloudinary.com/alick/video/upload/v1502444222/Goodbye_byaom5.mp3',
28 | cover: 'http://res.cloudinary.com/alick/image/upload/v1502444310/Goodbye_hpubmk.jpg',
29 | title: 'Goodbye',
30 | artist: ['G.E.M.']
31 | }
32 | ];
33 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import ExampleComponent from './examples/ExampleComponent';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------