index.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. },
  67. () => {
  68. this.ready = true;
  69. },
  70. );
  71. }
  72. componentWillUnmount() {
  73. if (this.player) {
  74. this.player.dispose();
  75. }
  76. }
  77. clearTimeUpdate() {
  78. if (this.timeInterval) {
  79. clearInterval(this.timeInterval);
  80. this.timeInterval = null;
  81. }
  82. }
  83. refreshTimeUpdate() {
  84. if (this.timeInterval) {
  85. clearInterval(this.timeInterval);
  86. this.timeInterval = null;
  87. }
  88. this.timeInterval = setInterval(() => {
  89. const { onTimeUpdate } = this.props;
  90. if (onTimeUpdate) onTimeUpdate(this.player.currentTime());
  91. this.setState({ progress: this.player.currentTime() * 100 / this.player.duration() });
  92. }, 1000);
  93. }
  94. onChangeProgress(value) {
  95. if (!this.ready) return;
  96. this.player.currentTime((this.player.duration() * value) / 100);
  97. this.setState({ progress: (this.player.currentTime() * 100) / this.player.duration() });
  98. const { onChangeProgress } = this.props;
  99. const { playing } = this.state;
  100. if (!playing) {
  101. this.player.pause();
  102. }
  103. if (onChangeProgress) onChangeProgress(this.player.currentTime());
  104. }
  105. onPlay() {
  106. if (!this.ready) return;
  107. const { onPlay } = this.props;
  108. this.player.play();
  109. this.setState({ playing: true });
  110. if (onPlay) onPlay(this.player.currentTime());
  111. this.refreshTimeUpdate();
  112. }
  113. onPause() {
  114. if (!this.ready) return;
  115. const { onPause } = this.props;
  116. this.player.pause();
  117. this.setState({ playing: false });
  118. if (onPause) onPause(this.player.currentTime());
  119. this.clearTimeUpdate();
  120. }
  121. onNext() {
  122. const { onNext } = this.props;
  123. this.player.pause();
  124. this.clearTimeUpdate();
  125. this.setState({ playing: false });
  126. if (onNext) onNext();
  127. }
  128. onSpeed(speed) {
  129. this.player.playbackRate(speed);
  130. this.setState({ selectSpeed: false, speed });
  131. }
  132. selectSpeed(value) {
  133. this.setState({ selectSpeed: value });
  134. }
  135. onFullChange() {
  136. this.setState({ fulling: !this.state.fulling });
  137. if (this.props.onFullChange) this.props.onFullChange();
  138. }
  139. onFull() {
  140. fullAgent = () => this.onFullChange();
  141. fullScreen(this.state.id);
  142. }
  143. onExitFull() {
  144. exitFullscreen();
  145. }
  146. showProgressTip(e) {
  147. let x = e.clientX;
  148. const percent = x * 100 / this.progress.clientWidth;
  149. const text = percent > 0 ? formatSecondAuto(percent * this.player.duration() / 100) : '00:00';
  150. const width = text.length > 5 ? 67.8 : 47.3;
  151. x += 1;
  152. x -= width / 2;
  153. if (x < 0) {
  154. x = 0;
  155. } else if (x + width > this.progress.clientWidth) {
  156. x = this.progress.clientWidth - width;
  157. }
  158. this.setState({ pt: { left: x, display: 'block', text, width } });
  159. }
  160. hideProgressTip() {
  161. this.setState({ pt: {} });
  162. }
  163. render() {
  164. const { btnList = [], children, onAction, hideAction, water } = this.props;
  165. const { playing, fulling, id, selectSpeed, speed } = this.state;
  166. return (
  167. <div id={id} className={`video-item ${!hideAction ? 'action' : ''} ${fulling ? 'full' : ''}`}>
  168. <div className="video-wrapper c-p" onClick={() => {
  169. return playing ? this.onPause() : this.onPlay();
  170. }}>
  171. <video
  172. ref={node => {
  173. this.videoNode = node;
  174. }}
  175. className="video-js vjs-fluid"
  176. />
  177. {water}
  178. {!playing && <Assets className="play c-p" name="play" />}
  179. {playing && <Assets className="stop c-p" name="stop" />}
  180. </div>
  181. <div className="video-bottom">
  182. {/* <div className="progress" /> */}
  183. {this.renderProgress()}
  184. {!hideAction && (
  185. <div className="action-bar">
  186. <div className="d-i-b m-r-1">
  187. <Assets
  188. name={!playing ? 'play2' : 'stop2'}
  189. onClick={() => (playing ? this.onPause() : this.onPlay())}
  190. />
  191. {/* {playing && <Assets name="stop2" onClick={() => this.onPause()} />} */}
  192. </div>
  193. <div className="d-i-b m-r-1">
  194. <Assets name="next2" onClick={() => this.onNext()} />
  195. </div>
  196. <div className="m-r-1 c-w">{this.ready ? (formatSecondAuto(this.player.currentTime())) : ('00:00')}</div>
  197. <div className="m-r-1 c-w">/</div>
  198. <div className="m-r-1 c-w">{this.ready ? (formatSecondAuto(this.player.duration())) : ('00:00')}</div>
  199. <div className="flex-block" />
  200. {btnList.map(btn => {
  201. if (btn.full && !fulling) return '';
  202. if (!btn.show) return '';
  203. return (
  204. <div className="d-i-b m-r-1">
  205. {btn.render ? (
  206. <div
  207. className="fix-btn-action d-i-b"
  208. onClick={() => {
  209. if (btn.pause) this.onPause();
  210. if (onAction) onAction(btn.key);
  211. }}
  212. >
  213. {btn.render(btn.active)}
  214. </div>
  215. ) : (<div
  216. className={`btn-action ${btn.active ? 'active' : ''}`}
  217. onClick={() => onAction && onAction(btn.key)}
  218. >{btn.title}</div>)}
  219. </div>
  220. );
  221. })}
  222. <div className="d-i-b m-r-1">
  223. <div className={`btn-action ${selectSpeed ? 'active' : ''}`} onClick={() => this.selectSpeed(!selectSpeed)}>
  224. 倍速
  225. </div>
  226. </div>
  227. <div className="d-i-b">
  228. {!fulling && <Assets name="full2" onClick={() => this.onFull()} />}
  229. {fulling && <Assets name="reduction2" onClick={() => this.onExitFull()} />}
  230. </div>
  231. </div>
  232. )}
  233. </div>
  234. {selectSpeed && (
  235. <div className="select-speed">
  236. <div className={`item ${speed === 0.5 ? 'active' : ''}`} onClick={() => this.onSpeed(0.5)}>
  237. 0.5
  238. </div>
  239. <div className={`item ${speed === 0.75 ? 'active' : ''}`} onClick={() => this.onSpeed(0.75)}>
  240. 0.75
  241. </div>
  242. <div className={`item ${speed === 1 ? 'active' : ''}`} onClick={() => this.onSpeed(1)}>
  243. 正常
  244. </div>
  245. <div className={`item ${speed === 1.25 ? 'active' : ''}`} onClick={() => this.onSpeed(1.25)}>
  246. 1.25
  247. </div>
  248. <div className={`item ${speed === 1.5 ? 'active' : ''}`} onClick={() => this.onSpeed(1.5)}>
  249. 1.5
  250. </div>
  251. <div className={`item ${speed === 2.0 ? 'active' : ''}`} onClick={() => this.onSpeed(2)}>
  252. 2.0
  253. </div>
  254. </div>
  255. )}
  256. {children}
  257. </div>
  258. );
  259. }
  260. renderProgress() {
  261. const { hideProgress } = this.props;
  262. const { progress, pt = {} } = this.state;
  263. return (
  264. !hideProgress && (
  265. <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>
  266. )
  267. );
  268. }
  269. }