├── .babelrc
├── .gitignore
├── .npmignore
├── README.md
├── example
└── src
│ ├── app.js
│ └── index.html
├── package.json
├── src
├── index.css
└── index.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | "@babel/plugin-proposal-class-properties",
8 | ]
9 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | example/dist
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # .npmignore
2 | src
3 | examples
4 | .babelrc
5 | .gitignore
6 | webpack.config.js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/feawesome/react-awesome-player/stargazers)
2 | [](https://github.com/feawesome/react-awesome-player/issues)
3 | [](https://github.com/feawesome/react-awesome-player/network)
4 | [](https://github.com/feawesome/react-awesome-player)
5 | [](https://github.com/feawesome/react-awesome-player)
6 | [](https://twitter.com/intent/tweet?url=https://github.com/feawesome/react-awesome-player)
7 |
8 | [](https://nodei.co/npm/react-awesome-player/)
9 | [](https://nodei.co/npm/react-awesome-player/)
10 |
11 |
12 | ## react-awesome-player
13 |
14 | [video.js](https://github.com/videojs/video.js) player component for React.
15 |
16 | Secondary development based on video.js, perfectly compatible with React , support subtitle display and the live stream of HLS. It is an awesome plugin for playing video on webpage.
17 | If anything goes wrong during using, please submit issues in this repository, or send email to author: **returnzp@gmail.com**
18 |
19 | ### Example
20 |
21 | * [Demo Page](https://feawesome.github.io/react-awesome-player)
22 | * Demo Code
23 | ```jsx
24 | import React from 'react'
25 | import { render } from 'react-dom'
26 | import ReactAwesomePlayer from 'react-awesome-player'
27 |
28 | class App extends React.Component {
29 | state = {
30 | options: {
31 | poster: "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=854361313,3188166359&fm=26&gp=0.jpg",
32 | sources: [{
33 | type: "video/mp4",
34 | src: "https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm"
35 | }],
36 | subtitles: [{
37 | language: 'zh',
38 | url: "https://feawesome.github.io/react-awesome-player/zh.vtt",
39 | label: "中文"
40 | },
41 | {
42 | language: 'en',
43 | url: "https://feawesome.github.io/react-awesome-player/en.vtt",
44 | label: "EN"
45 | }],
46 | defaultSubtitle: 'en'
47 | }
48 | }
49 | loadeddata() {
50 | console.log('loadeddata')
51 | }
52 | canplay() {
53 | console.log('canplay')
54 | }
55 | canplaythrough() {
56 | console.log('canplaythrough')
57 | }
58 | play() {
59 | console.log('play')
60 | }
61 | pause() {
62 | console.log('pause')
63 | }
64 | waiting() {
65 | console.log('waiting')
66 | }
67 | playing() {
68 | console.log('playing')
69 | }
70 | ended() {
71 | console.log('ended')
72 | }
73 | error() {
74 | console.log('error')
75 | }
76 |
77 | render () {
78 | const { options } = this.state
79 | return
80 | { console.log(video) }}
82 | options={options}
83 | loadeddata={this.loadeddata}
84 | canplay={this.canplay}
85 | canplaythrough={this.canplaythrough}
86 | play={this.play}
87 | pause={this.pause}
88 | waiting={this.waiting}
89 | playing={this.playing}
90 | ended={this.ended}
91 | error={this.error}
92 | />
93 |
94 | }
95 | }
96 |
97 | render(, document.getElementById('root'))
98 |
99 | ```
100 |
101 |
102 | ### Install
103 | #### NPM
104 |
105 | ``` bash
106 | npm install react-awesome-player --save
107 | ```
108 |
109 |
110 | ### API
111 | - component api:
112 |
113 | | API | description | type | default |
114 | | - | - | - | - |
115 | | events | custom videojs event to component | Array | [] |
116 | | playsInline | set player not full-screen in mobile device | Boolean | true |
117 | | crossOrigin | set crossOrigin to video | String | '' |
118 |
119 | - video.js api
120 | * [video.js options](http://docs.videojs.com/tutorial-options.html)
121 | * [video.js docs](http://docs.videojs.com/)
122 |
123 | ### videojs plugins
124 |
125 | - [videojs-resolution-switcher](https://github.com/kmoskwiak/videojs-resolution-switcher)
126 | - [videojs-contrib-hls](https://github.com/videojs/videojs-contrib-hls)
127 | - [videojs-youtube](https://github.com/videojs/videojs-youtube)
128 | - [videojs-vimeo](https://github.com/videojs/videojs-vimeo)
129 | - [videojs-hotkeys](https://github.com/ctd1500/videojs-hotkeys)
130 | - [videojs-flash](https://github.com/videojs/videojs-flash)
131 | - [videojs-contrib-ads](https://github.com/videojs/videojs-contrib-ads)
132 | - [more plugins...](https://github.com/search?o=desc&q=videojs+plugin&s=stars&type=Repositories&utf8=%E2%9C%93)
133 |
134 |
135 | ### Author
136 | **Peng Zhang**
137 | **returnzp@gmail.com**
138 |
--------------------------------------------------------------------------------
/example/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import ReactAwesomePlayer from 'react-awesome-player'
4 |
5 | class App extends React.Component {
6 | state = {
7 | options: {
8 | poster: "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=854361313,3188166359&fm=26&gp=0.jpg",
9 | sources: [{
10 | type: "video/mp4",
11 | src: "https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm"
12 | }],
13 | subtitles: [{
14 | language: 'zh',
15 | url: "https://feawesome.github.io/react-awesome-player/zh.vtt",
16 | label: "中文"
17 | },
18 | {
19 | language: 'en',
20 | url: "https://feawesome.github.io/react-awesome-player/en.vtt",
21 | label: "EN"
22 | }],
23 | defaultSubtitle: 'en'
24 | }
25 | }
26 | loadeddata() {
27 | console.log('loadeddata')
28 | }
29 | canplay() {
30 | console.log('canplay')
31 | }
32 | canplaythrough() {
33 | console.log('canplaythrough')
34 | }
35 | play() {
36 | console.log('play')
37 | }
38 | pause() {
39 | console.log('pause')
40 | }
41 | waiting() {
42 | console.log('waiting')
43 | }
44 | playing() {
45 | console.log('playing')
46 | }
47 | ended() {
48 | console.log('ended')
49 | }
50 | error() {
51 | console.log('error')
52 | }
53 |
54 | render () {
55 | const { options } = this.state
56 | return
57 | { console.log(video) }}
59 | options={options}
60 | loadeddata={this.loadeddata}
61 | canplay={this.canplay}
62 | canplaythrough={this.canplaythrough}
63 | play={this.play}
64 | pause={this.pause}
65 | waiting={this.waiting}
66 | playing={this.playing}
67 | ended={this.ended}
68 | error={this.error}
69 | />
70 |
71 | }
72 | }
73 |
74 | render(, document.getElementById('root'))
75 |
--------------------------------------------------------------------------------
/example/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React Awesome Player
5 |
6 |
7 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-awesome-player",
3 | "version": "1.1.1",
4 | "description": "video.js component for React",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "webpack-dev-server --open development",
9 | "build": "webpack --mode production",
10 | "deploy": "gh-pages -d example/dist",
11 | "publish-demo": "npm run build && npm run deploy && npx babel src --out-dir lib"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/feawesome/react-awesome-player.git"
16 | },
17 | "keywords": [
18 | "react-video-player",
19 | "react-awesome-player",
20 | "react video player",
21 | "video player",
22 | "react player",
23 | "react video"
24 | ],
25 | "author": {
26 | "name": "Peng Zhang",
27 | "email": "returnzp@gmail.com"
28 | },
29 | "license": "MIT",
30 | "devDependencies": {
31 | "@babel/cli": "^7.2.3",
32 | "@babel/core": "^7.2.2",
33 | "@babel/preset-env": "^7.3.1",
34 | "@babel/preset-react": "^7.0.0",
35 | "babel-loader": "^8.0.5",
36 | "gh-pages": "^2.0.1",
37 | "html-webpack-plugin": "^3.2.0",
38 | "react": "^16.7.0",
39 | "react-dom": "^16.7.0",
40 | "webpack": "^4.29.0",
41 | "webpack-cli": "^3.2.1",
42 | "webpack-dev-server": "^3.1.14"
43 | },
44 | "dependencies": {
45 | "@babel/plugin-proposal-class-properties": "^7.5.0",
46 | "css-loader": "^3.0.0",
47 | "object-path": "^0.11.4",
48 | "react-awesome-player": "^1.1.0",
49 | "style-loader": "^0.23.1",
50 | "video.js": "^7.6.0"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | .react-awesome-player * {
2 | outline: 0;
3 | }
4 | .video-js {
5 | height: auto;
6 | }
7 | .react-awesome-player-container {
8 | /* position: relative; */
9 | }
10 | .vjs-time-tooltip {
11 | min-width: 40px;
12 | }
13 | .react-awesome-player-container button {
14 | outline: none;
15 | }
16 |
17 | .vjs-button > .vjs-icon-placeholder:before {
18 | /* line-height: 2; */
19 | }
20 |
21 | /* 声音 */
22 | .react-awesome-player-container .video-js .vjs-volume-panel-horizontal {
23 | display: none !important;
24 | }
25 |
26 | /* 显示时间 */
27 | .react-awesome-player-container .video-js .vjs-control-bar .vjs-current-time {
28 | padding: 0;
29 | display: block !important;
30 | }
31 | .vjs-picture-in-picture-control {
32 | display: none !important;
33 | }
34 | /* 显示时间 */
35 | .react-awesome-player-container .video-js .vjs-time-divider {
36 | padding: 0 2px;
37 | min-width: 0em;
38 | text-align: center;
39 | display: block !important;
40 | }
41 |
42 | /* 显示时间 */
43 | .react-awesome-player-container .video-js .vjs-control-bar .vjs-duration {
44 | padding: 0;
45 | display: block !important;
46 | }
47 |
48 | .video-js .vjs-big-play-button .vjs-icon-placeholder:before, .vjs-button > .vjs-icon-placeholder:before, .video-js .vjs-modal-dialog, .vjs-modal-dialog .vjs-modal-dialog-content {
49 | position: unset;
50 | }
51 |
52 | .react-awesome-player-container video {
53 | object-fit: fill;
54 | }
55 |
56 | .react-awesome-player-container .vjs-fullscreen video {
57 | object-fit: contain;
58 | }
59 |
60 | .react-awesome-player-container .video-layer {
61 | }
62 |
63 | .react-awesome-player-container .video-layer section {
64 | width: 100%;
65 | height: 100%;
66 | display: flex;
67 | align-items: center;
68 | justify-content: center;
69 | text-align: center;
70 | background: #080915;
71 | box-shadow: 0px 0px 30px 1px #103136 inset;
72 | }
73 |
74 | .react-awesome-player-container .video-layer .loader {
75 | position: relative;
76 | width: 40px;
77 | height: 40px;
78 | border-radius: 50%;
79 | }
80 |
81 | .react-awesome-player-container .video-layer .loader-1 .loader-outter {
82 | position: absolute;
83 | border: 4px solid #b50136;
84 | border-left-color: transparent;
85 | border-bottom: 0;
86 | width: 100%;
87 | height: 100%;
88 | border-radius: 50%;
89 | -webkit-animation: loader-1-outter 1s cubic-bezier(0.42, 0.61, 0.58, 0.41)
90 | infinite;
91 | animation: loader-1-outter 1s cubic-bezier(0.42, 0.61, 0.58, 0.41) infinite;
92 | }
93 |
94 | .react-awesome-player-container .video-layer .loader-1 .loader-inner {
95 | position: absolute;
96 | border: 4px solid #b50136;
97 | border-radius: 50%;
98 | width: 18px;
99 | height: 18px;
100 | left: calc(50% - 22px);
101 | top: calc(50% - 24px);
102 | border-right: 0;
103 | border-top-color: transparent;
104 | -webkit-animation: loader-1-inner 1s cubic-bezier(0.42, 0.61, 0.58, 0.41)
105 | infinite;
106 | animation: loader-1-inner 1s cubic-bezier(0.42, 0.61, 0.58, 0.41) infinite;
107 | }
108 |
109 | @keyframes loader-1-outter {
110 | 0% {
111 | -webkit-transform: rotate(0deg);
112 | transform: rotate(0deg);
113 | }
114 | 100% {
115 | -webkit-transform: rotate(360deg);
116 | transform: rotate(360deg);
117 | }
118 | }
119 |
120 | @-webkit-keyframes loader-1-inner {
121 | 0% {
122 | -webkit-transform: rotate(0deg);
123 | transform: rotate(0deg);
124 | }
125 | 100% {
126 | -webkit-transform: rotate(-360deg);
127 | transform: rotate(-360deg);
128 | }
129 | }
130 |
131 | @keyframes loader-1-inner {
132 | 0% {
133 | -webkit-transform: rotate(0deg);
134 | transform: rotate(0deg);
135 | }
136 | 100% {
137 | -webkit-transform: rotate(-360deg);
138 | transform: rotate(-360deg);
139 | }
140 | }
141 |
142 | .no-video {
143 | position: absolute;
144 | width: 100%;
145 | height: 100%;
146 | left: 0;
147 | top: 0;
148 | z-index: 9;
149 | }
150 |
151 | .definition-container {
152 | width: 30px;
153 | background: red;
154 | height: 30px;
155 | position: relative;
156 | }
157 |
158 | .definition-container .definition-btn-container {
159 | background: #ddd;
160 | bottom: 30px;
161 | position: absolute;
162 | display: none;
163 | }
164 |
165 | .vjs-audio-button {
166 | /* display: none; */
167 | }
168 |
169 | .vjs-texttrack-settings {
170 | /* display: none; */
171 | }
172 |
173 | .vjs-menu .vjs-menu-content {
174 | overflow: initial;
175 | }
176 |
177 | .vjs-menu .vjs-menu-content li:first-child{
178 | display: none;
179 | }
180 |
181 | .vjs-menu-content {
182 | overflow-y: hidden;
183 | }
184 |
185 | .video-js
186 | .vjs-subs-caps-button
187 | + .vjs-menu
188 | .vjs-captions-menu-item
189 | .vjs-menu-item-text
190 | .vjs-icon-placeholder:before {
191 | font-family: VideoJS !important;
192 | content: '\F10D' !important;
193 | }
194 |
195 | /* .vjs-icon-subtitles:before,
196 | .video-js .vjs-subtitles-button .vjs-icon-placeholder:before,
197 | .video-js .vjs-subs-caps-button .vjs-icon-placeholder:before,
198 | .video-js.video-js:lang(en-GB)
199 | .vjs-subs-caps-button
200 | .vjs-icon-placeholder:before,
201 | .video-js.video-js:lang(en-IE)
202 | .vjs-subs-caps-button
203 | .vjs-icon-placeholder:before,
204 | .video-js.video-js:lang(en-AU)
205 | .vjs-subs-caps-button
206 | .vjs-icon-placeholder:before,
207 | .video-js.video-js:lang(en-NZ)
208 | .vjs-subs-caps-button
209 | .vjs-icon-placeholder:before {
210 | content: '\e645' !important;
211 | font-size: 14px;
212 | } */
213 |
214 | /* .vjs-icon-play,
215 | .video-js .vjs-big-play-button .vjs-icon-placeholder:before,
216 | .video-js .vjs-play-control .vjs-icon-placeholder {
217 | font-family: 'iconfont';
218 | } */
219 |
220 | /* .vjs-icon-play:before,
221 | .video-js .vjs-big-play-button .vjs-icon-placeholder:before,
222 | .video-js .vjs-play-control .vjs-icon-placeholder:before {
223 | font-size: 14px;
224 | content: '\e644' !important;
225 | } */
226 |
227 | /* .vjs-icon-fullscreen-enter:before,
228 | .video-js .vjs-fullscreen-control .vjs-icon-placeholder:before {
229 | font-size: 14px;
230 | content: '\e646' !important;
231 | } */
232 |
233 | /* .react-awesome-player-container .vjs-icon-play:before,
234 | .react-awesome-player-container .video-js .vjs-big-play-button .vjs-icon-placeholder:before,
235 | .react-awesome-player-container .video-js .vjs-play-control .vjs-icon-placeholder:before {
236 | font-size: 14px;
237 | content: '\e643' !important;
238 | } */
239 |
240 |
241 | /* .vjs-button > .vjs-icon-placeholder:before {
242 | line-height: 42px;
243 | font-size: 20px;
244 | } */
245 |
246 | /* .vjs-icon-pause:before,
247 | .video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder:before {
248 | font-size: 14px;
249 | content: '\e643' !important;
250 | } */
251 |
252 | .vjs-custom-skin > .video-js .vjs-menu-button-popup .vjs-menu {
253 | width: 90px;
254 | left: -30px;
255 | }
256 |
257 | .vjs-custom-skin > .video-js .vjs-control {
258 | width: 30px;
259 | }
260 |
261 | .vjs-custom-skin > .video-js .vjs-play-progress,
262 | .vjs-custom-skin > .video-js .vjs-volume-level {
263 | background-color: #d32f2f;
264 | }
265 |
266 | .vjs-custom-skin > .video-js .vjs-loading-spinner {
267 | border-color: #d32f2f;
268 | }
269 |
270 | .vjs-custom-skin > .video-js .vjs-control-bar .vjs-time-control {
271 | min-width: 6px;
272 | }
273 |
274 | .vjs-custom-skin > .video-js .vjs-control-bar .vjs-time-control div {
275 | height: 42px;
276 | font-size: 12px;
277 | line-height: 42px;
278 | }
279 |
280 | .vjs-custom-skin > .video-js .vjs-control-bar .vjs-time-control div span {
281 | line-height: 39px;
282 | }
283 |
284 | .vjs-custom-skin > .video-js .vjs-time-control {
285 | width: auto;
286 | }
287 |
288 | .vjs-custom-skin > .video-js .vjs-time-control .vjs-current-time-display {
289 | width: auto;
290 | }
291 |
292 | .vjs-custom-skin .video-js .vjs-big-play-button {
293 | margin: 0 !important;
294 | height: 54px !important;
295 | }
296 |
297 | .video-js {
298 | background-color: #212121;
299 | font-family: VideoJS !important;
300 | outline: 0;
301 | }
302 |
303 | .video-js .vjs-volume-panel {
304 | /* display: none !important; */
305 | }
306 |
307 | .video-js .vjs-big-play-button {
308 | top: 50%;
309 | left: 50%;
310 | position: absolute;
311 | -webkit-transform: translate(-50%, -50%);
312 | transform: translate(-50%, -50%);
313 | width: 54px;
314 | height: 54px !important;
315 | border-radius: 50%;
316 | border: 0;
317 | line-height: 54px !important;
318 | background-color: rgba(0, 0, 0, 0.6);
319 | text-align: center;
320 | line-height: 54px;
321 | margin: 0 !important;
322 | }
323 |
324 | .video-js .vjs-custom-skin > .video-js .vjs-big-play-button {
325 | margin: 0 !important;
326 | }
327 |
328 | /* .video-js .vjs-icon-placeholder {
329 | line-height: 54px !important;
330 | font-family: 'iconfont' !important;
331 | } */
332 |
333 | .video-js .vjs-play-progress {
334 | font-family: VideoJS !important;
335 | }
336 |
337 | .video-js .vjs-poster {
338 | background-size: cover;
339 | }
340 |
341 | .video-js:hover .vjs-big-play-button {
342 | background-color: rgba(0, 0, 0, 0.45);
343 | }
344 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * React-Awesome-Player
4 | *
5 | */
6 |
7 | import React from 'react';
8 | import PropTypes from 'prop-types';
9 | import ObjectPath from 'object-path'
10 |
11 | import videojs from 'video.js'
12 | import 'video.js/dist/video-js.css';
13 | import './index.css'
14 |
15 | const DEFAULT_STATE = {
16 | options: {
17 | controls: true, // 是否显示控制条
18 | controlBar: { // 显示控制条内容
19 | timeDivider: true, // 时间分割线
20 | durationDisplay: true, // 显示时间
21 | remainingTimeDisplay: false, // 剩余时间显示
22 | fullscreenToggle: true, // 切换全屏
23 | subtitlesButton: true, // 字幕按钮
24 | },
25 | techOrder: [
26 | 'html5',
27 | ],
28 | autoplay: false, // 自动播放
29 | muted: false,
30 | loop: false, // 循环播放
31 | preload: 'none', // 预加载
32 | // language: 'zh', // 展示语言
33 | aspectRatio: '16:9', // 比例
34 | fluid: true,
35 | poster: '', // 封面图
36 | sources: [],
37 | subtitles: [], // 字幕
38 | defaultSubtitle: '' // 默认字幕
39 | // width: document.documentElement.clientWidth,
40 | },
41 | playInline: true,
42 | crossOrigin: ''
43 | }
44 |
45 | const DEFAULT_EVENTS = [
46 | 'loadeddata',
47 | 'canplay',
48 | 'canplaythrough',
49 | 'play',
50 | 'pause',
51 | 'waiting',
52 | 'playing',
53 | 'ended',
54 | 'error',
55 | ];
56 |
57 | class ReactAwesomePlayer extends React.Component {
58 | static defaultProps = {
59 | options: {}
60 | }
61 |
62 | video = null
63 | state = DEFAULT_STATE
64 |
65 | componentDidMount() {
66 | const sources = ObjectPath(this.props).get('options.sources') || []
67 | const isSource = sources.length
68 |
69 | if (!this.player && isSource) {
70 | this.initialize();
71 | }
72 | }
73 |
74 | componentDidUpdate(prevProps) {
75 | if (this.player && this.props.options !== prevProps.options) {
76 | this.resetUrl();
77 | }
78 | }
79 |
80 | componentWillUnmount() {
81 | if (this.player) {
82 | this.player.dispose();
83 | }
84 | }
85 |
86 | resetUrl() {
87 | const sources = ObjectPath(this.props).get('options.sources') || []
88 |
89 | sources[0] && this.player.src(sources[0].src);
90 | this.player.poster(this.props.options.poster);
91 | }
92 |
93 | initialize() {
94 | // 添加行内播放
95 | if (this.state.playInline) {
96 | this.video.setAttribute('webkit-playsinline', true);
97 | this.video.setAttribute('playsInline', true);
98 | this.video.setAttribute('x5-playsinline', true);
99 | this.video.setAttribute('x5-video-player-type', 'h5');
100 | this.video.setAttribute('x5-video-player-fullscreen', false);
101 | }
102 |
103 | // 跨域视频请求
104 | if (this.state.crossOrigin !== '') {
105 | this.video.crossOrigin = this.state.crossOrigin;
106 | this.video.setAttribute('crossOrigin', this.state.crossOrigin);
107 | }
108 |
109 | // 防止出现报错: "VIDEOJS: ERROR: Unable to find plugin: __ob__"
110 | if (this.state.options.plugins) {
111 | this.setState((prevState) => {
112 | delete prevState.plugins.__ob__;
113 | return prevState;
114 | })
115 | }
116 |
117 | this.setState({
118 | options: {
119 | ...this.state.options,
120 | ...this.props.options,
121 | },
122 | }, () => {
123 | const context = this;
124 |
125 | this.player = videojs(this.video, this.state.options, function () {
126 |
127 | // events
128 | const events = DEFAULT_EVENTS.concat(context.props.events)
129 |
130 | // 监听事件
131 | const onEdEvents = {};
132 | for (let i = 0; i < events.length; i += 1) {
133 | if (typeof events[i] === 'string' && onEdEvents[events[i]] === undefined) {
134 | (event => {
135 | onEdEvents[event] = null;
136 | this.on(event, () => {
137 | if (typeof context.props[event] === 'function') context.props[event](true);
138 | });
139 | })(events[i]);
140 | }
141 | }
142 |
143 | // 监听事件更新事件
144 | this.on('timeupdate', function () {
145 | if (typeof context.props.timeupdate === 'function') context.props.timeupdate(this.currentTime());
146 | });
147 | });
148 | });
149 | }
150 |
151 | render() {
152 | const { subtitles, defaultSubtitle } = this.state.options;
153 | const { onRef } = this.props;
154 |
155 | return (
156 |
157 |
174 |
175 | )
176 | }
177 | }
178 |
179 | ReactAwesomePlayer.propTypes = {
180 | options: PropTypes.object,
181 | defaultSubtitle: PropTypes.string,
182 | events: PropTypes.array,
183 | onRef: PropTypes.func,
184 |
185 | // events props
186 | loadeddata: PropTypes.func,
187 | canplay: PropTypes.func,
188 | canplaythrough: PropTypes.func,
189 | play: PropTypes.func,
190 | pause: PropTypes.func,
191 | waiting: PropTypes.func,
192 | playing: PropTypes.func,
193 | ended: PropTypes.func,
194 | error: PropTypes.func,
195 | timeupdate: PropTypes.func,
196 | };
197 |
198 | export default ReactAwesomePlayer;
199 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require("html-webpack-plugin");
3 | const htmlWebpackPlugin = new HtmlWebpackPlugin({
4 | template: path.join(__dirname, "./example/src/index.html"),
5 | filename: "./index.html"
6 | });
7 |
8 | module.exports = {
9 | entry: path.join(__dirname, "./example/src/app.js"),
10 | output: {
11 | path: path.join(__dirname, "example/dist"),
12 | filename: "bundle.js"
13 | },
14 | module: {
15 | rules: [{
16 | test: /\.(js|jsx)$/,
17 | use: "babel-loader",
18 | exclude: /node_modules/
19 | },
20 | {
21 | // Preprocess our own .css files
22 | // This is the place to add your own loaders (e.g. sass/less etc.)
23 | // for a list of loaders, see https://webpack.js.org/loaders/#styling
24 | test: /\.css$/,
25 | use: ['style-loader', 'css-loader'],
26 | },
27 | ]
28 | },
29 | plugins: [htmlWebpackPlugin],
30 | resolve: {
31 | extensions: [".js", ".jsx"]
32 | },
33 | devServer: {
34 | port: 3001
35 | }
36 | };
37 |
38 |
--------------------------------------------------------------------------------