index.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import React, { Component } from 'react';
  2. import { Slider } from 'antd';
  3. import './index.less';
  4. import 'video.js/dist/video-js.css';
  5. import videojs from 'video.js';
  6. import Assets from '@src/components/Assets';
  7. import { generateUUID, formatSecondAuto } from '@src/services/Tools';
  8. function fullScreen(id) {
  9. const element = document.getElementById(id);
  10. if (element.requestFullscreen) {
  11. element.requestFullscreen();
  12. } else if (element.msRequestFullscreen) {
  13. element.msRequestFullscreen();
  14. } else if (element.mozRequestFullScreen) {
  15. element.mozRequestFullScreen();
  16. } else if (element.webkitRequestFullscreen) {
  17. element.webkitRequestFullscreen();
  18. }
  19. }
  20. function exitFullscreen() {
  21. if (document.exitFullscreen) {
  22. document.exitFullscreen();
  23. } else if (document.msExitFullscreen) {
  24. document.msExitFullscreen();
  25. } else if (document.mozCancelFullScreen) {
  26. document.mozCancelFullScreen();
  27. } else if (document.webkitExitFullscreen) {
  28. document.webkitExitFullscreen();
  29. }
  30. }
  31. let fullAgent = null;
  32. document.addEventListener('fullscreenchange', () => {
  33. if (fullAgent) fullAgent();
  34. });
  35. /* Firefox */
  36. document.addEventListener('mozfullscreenchange', () => {
  37. if (fullAgent) fullAgent();
  38. });
  39. /* Chrome, Safari and Opera */
  40. document.addEventListener('webkitfullscreenchange', () => {
  41. if (fullAgent) fullAgent();
  42. });
  43. /* IE / Edge */
  44. document.addEventListener('msfullscreenchange', () => {
  45. if (fullAgent) fullAgent();
  46. });
  47. export default class Video extends Component {
  48. constructor(props) {
  49. super(props);
  50. this.ready = false;
  51. this.state = { id: generateUUID(8), playing: false, fulling: false, progress: 0, speed: 1 };
  52. }
  53. componentDidMount() {
  54. this.player = videojs(
  55. this.videoNode,
  56. {
  57. controls: true,
  58. sources: [
  59. {
  60. src: this.props.src,
  61. type: 'video/mp4',
  62. },
  63. ],
  64. width: this.props.width,
  65. height: this.props.height,
  66. fluid: true,
  67. },
  68. () => {
  69. this.ready = true;
  70. },
  71. );
  72. }
  73. componentWillUnmount() {
  74. if (this.player) {
  75. this.player.dispose();
  76. }
  77. }
  78. clearTimeUpdate() {
  79. if (this.timeInterval) {
  80. clearInterval(this.timeInterval);
  81. this.timeInterval = null;
  82. }
  83. }
  84. refreshTimeUpdate() {
  85. if (this.timeInterval) {
  86. clearInterval(this.timeInterval);
  87. this.timeInterval = null;
  88. }
  89. this.timeInterval = setInterval(() => {
  90. const { onTimeUpdate } = this.props;
  91. if (onTimeUpdate) onTimeUpdate(this.player.currentTime());
  92. if (this.player.currentTime() === this.player.duration()) {
  93. this.onPause();
  94. }
  95. this.setState({ progress: this.player.currentTime() * 100 / this.player.duration() });
  96. }, 1000);
  97. }
  98. onChangeProgress(value) {
  99. if (!this.ready) return;
  100. this.player.currentTime((this.player.duration() * value) / 100);
  101. this.setState({ progress: (this.player.currentTime() * 100) / this.player.duration() });
  102. const { onChangeProgress } = this.props;
  103. const { playing } = this.state;
  104. if (!playing) {
  105. this.player.pause();
  106. }
  107. if (onChangeProgress) onChangeProgress(this.player.currentTime());
  108. }
  109. onPlay() {
  110. if (!this.ready) return;
  111. const { onPlay } = this.props;
  112. this.player.play();
  113. this.setState({ playing: true });
  114. if (onPlay) onPlay(this.player.currentTime());
  115. this.refreshTimeUpdate();
  116. }
  117. onPause() {
  118. if (!this.ready) return;
  119. const { onPause } = this.props;
  120. this.player.pause();
  121. this.setState({ playing: false });
  122. if (onPause) onPause(this.player.currentTime());
  123. this.clearTimeUpdate();
  124. }
  125. onNext() {
  126. const { onNext } = this.props;
  127. this.player.pause();
  128. this.clearTimeUpdate();
  129. this.setState({ playing: false });
  130. if (onNext) onNext();
  131. }
  132. onSpeed(speed) {
  133. this.player.playbackRate(speed);
  134. this.setState({ selectSpeed: false, speed });
  135. }
  136. selectSpeed(value) {
  137. this.setState({ selectSpeed: value });
  138. }
  139. onFullChange() {
  140. this.setState({ fulling: !this.state.fulling });
  141. if (this.props.onFullChange) this.props.onFullChange();
  142. }
  143. onFull() {
  144. fullAgent = () => this.onFullChange();
  145. fullScreen(this.state.id);
  146. }
  147. onExitFull() {
  148. exitFullscreen();
  149. }
  150. showProgressTip(e) {
  151. let x = e.clientX;
  152. const percent = x * 100 / this.progress.clientWidth;
  153. const text = percent > 0 ? formatSecondAuto(percent * this.player.duration() / 100) : '00:00';
  154. const width = text.length > 5 ? 67.8 : 47.3;
  155. x += 1;
  156. x -= width / 2;
  157. if (x < 0) {
  158. x = 0;
  159. } else if (x + width > this.progress.clientWidth) {
  160. x = this.progress.clientWidth - width;
  161. }
  162. this.setState({ pt: { left: x, display: 'block', text, width } });
  163. }
  164. hideProgressTip() {
  165. this.setState({ pt: {} });
  166. }
  167. render() {
  168. const { btnList = [], children, onAction, hideAction, water } = this.props;
  169. const { playing, fulling, id, selectSpeed, speed } = this.state;
  170. return (
  171. <div id={id} className={`video-item ${!hideAction ? 'action' : ''} ${fulling ? 'full' : ''}`}>
  172. <div className="video-wrapper c-p" onClick={() => {
  173. return playing ? this.onPause() : this.onPlay();
  174. }}>
  175. <video
  176. onContextMenu={(e) => {
  177. e.preventDefault();
  178. return false;
  179. }}
  180. controlslist="nodownload"
  181. ref={node => {
  182. this.videoNode = node;
  183. }}
  184. className="video-js vjs-fluid"
  185. />
  186. {water}
  187. {!playing && <Assets className="play c-p" name="play" />}
  188. {playing && <Assets className="stop c-p" name="stop" />}
  189. </div>
  190. <div className="video-bottom">
  191. {/* <div className="progress" /> */}
  192. {this.renderProgress()}
  193. {!hideAction && (
  194. <div className="action-bar">
  195. <div className="d-i-b m-r-1">
  196. <Assets
  197. name={!playing ? 'play2' : 'stop2'}
  198. onClick={() => (playing ? this.onPause() : this.onPlay())}
  199. />
  200. {/* {playing && <Assets name="stop2" onClick={() => this.onPause()} />} */}
  201. </div>
  202. <div className="d-i-b m-r-1">
  203. <Assets name="next2" onClick={() => this.onNext()} />
  204. </div>
  205. <div className="m-r-1 c-w">{this.ready ? (formatSecondAuto(this.player.currentTime())) : ('00:00')}</div>
  206. <div className="m-r-1 c-w">/</div>
  207. <div className="m-r-1 c-w">{this.ready ? (formatSecondAuto(this.player.duration())) : ('00:00')}</div>
  208. <div className="flex-block" />
  209. {btnList.map(btn => {
  210. if (btn.full && !fulling) return '';
  211. if (!btn.show) return '';
  212. return (
  213. <div className="d-i-b m-r-1">
  214. {btn.render ? (
  215. <div
  216. className="fix-btn-action d-i-b"
  217. onClick={() => {
  218. if (btn.pause) this.onPause();
  219. if (onAction) onAction(btn.key);
  220. }}
  221. >
  222. {btn.render(btn.active)}
  223. </div>
  224. ) : (<div
  225. className={`btn-action ${btn.active ? 'active' : ''}`}
  226. onClick={() => onAction && onAction(btn.key)}
  227. >{btn.title}</div>)}
  228. </div>
  229. );
  230. })}
  231. <div className="d-i-b m-r-1">
  232. <div className={`btn-action ${selectSpeed ? 'active' : ''}`} onClick={() => this.selectSpeed(!selectSpeed)}>
  233. 倍速
  234. </div>
  235. </div>
  236. <div className="d-i-b">
  237. {!fulling && <Assets name="full2" onClick={() => this.onFull()} />}
  238. {fulling && <Assets name="reduction2" onClick={() => this.onExitFull()} />}
  239. </div>
  240. </div>
  241. )}
  242. </div>
  243. {selectSpeed && (
  244. <div className="select-speed">
  245. <div className={`item ${speed === 0.5 ? 'active' : ''}`} onClick={() => this.onSpeed(0.5)}>
  246. 0.5
  247. </div>
  248. <div className={`item ${speed === 0.75 ? 'active' : ''}`} onClick={() => this.onSpeed(0.75)}>
  249. 0.75
  250. </div>
  251. <div className={`item ${speed === 1 ? 'active' : ''}`} onClick={() => this.onSpeed(1)}>
  252. 正常
  253. </div>
  254. <div className={`item ${speed === 1.25 ? 'active' : ''}`} onClick={() => this.onSpeed(1.25)}>
  255. 1.25
  256. </div>
  257. <div className={`item ${speed === 1.5 ? 'active' : ''}`} onClick={() => this.onSpeed(1.5)}>
  258. 1.5
  259. </div>
  260. <div className={`item ${speed === 2.0 ? 'active' : ''}`} onClick={() => this.onSpeed(2)}>
  261. 2.0
  262. </div>
  263. </div>
  264. )}
  265. {children}
  266. </div>
  267. );
  268. }
  269. renderProgress() {
  270. const { hideProgress } = this.props;
  271. const { progress, pt = {} } = this.state;
  272. return (
  273. !hideProgress && (
  274. <div ref={ref => { if (ref) this.progress = ref; }} onMouseMove={(e) => this.showProgressTip(e)} onMouseLeave={() => this.hideProgressTip()}><Slider value={progress || 0} step={0.01} tooltipVisible={false} onChange={value => this.onChangeProgress(value)} /><div className={'show-progress-tip'} style={{ ...pt }}>{pt.text}</div></div>
  275. )
  276. );
  277. }
  278. }