index.js 9.1 KB

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