CusVideo.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. import React, { Component } from "react";
  2. import {
  3. Platform,
  4. Text,
  5. View,
  6. ImageBackground,
  7. Button,
  8. Image,
  9. TouchableOpacity,
  10. PanResponder,
  11. StyleSheet
  12. } from "react-native";
  13. import { createStackNavigator, createAppContainer } from "react-navigation";
  14. import Video from "react-native-video";
  15. import SeekBar from "../components/SeekBar";
  16. const instructions = Platform.select({
  17. ios: "Press Cmd+R to reload,\n" + "Cmd+D or shake for dev menu",
  18. android:
  19. "Double tap R on your keyboard to reload,\n" +
  20. "Shake or press menu button for dev menu"
  21. });
  22. type Props = {};
  23. export default class CusVideo extends React.Component {
  24. constructor(props) {
  25. super(props);
  26. this.pressStatus = false;
  27. this.progress = 0;
  28. }
  29. config = {
  30. changeX: 0,
  31. changeY: 0,
  32. xDiff: 0,
  33. yDiff: 0
  34. };
  35. state = {
  36. player_status_icon: require("../images/video/start.png"),
  37. rate: 1,
  38. volume: 1,
  39. muted: false,
  40. resizeMode: "stretch",
  41. duration: 0.0,
  42. currentTime: 0.0,
  43. paused: false,
  44. wheel: false,
  45. isFull: false,
  46. needback: this.props.needback,
  47. show_controller: true,
  48. show_loading: true
  49. };
  50. render() {
  51. if (!this.props.show) {
  52. return null;
  53. }
  54. return (
  55. <View style={[this.props.style, { overflow: "hidden" }]}>
  56. <Video
  57. {...this.videotouch.panHandlers}
  58. style={{ flex: 1 }}
  59. source={{
  60. uri: this.props.uri
  61. }}
  62. ref={ref => {
  63. this.player = ref;
  64. }}
  65. // poster={this.props.poster}
  66. resizeMode={[this.state.resizeMode]}
  67. // posterResizeMode={this.state.resizeMode}
  68. onBuffer={this.onBuffer.bind(this)}
  69. rate={this.state.rate} //播放速率
  70. paused={this.state.paused} //暂停
  71. volume={this.state.volume} //调节音量
  72. muted={this.state.muted} //控制音频是否静音
  73. resizeMode={this.state.resizeMode} //缩放模式
  74. onLoadStart={this.loadStart} // 当视频开始加载时的回调函数
  75. onLoad={this.onLoad} //加载媒体并准备播放时调用的回调函数。
  76. onProgress={this.onProgress} //视频播放过程中每个间隔进度单位调用的回调函数
  77. onEnd={this.onEnd} //视频播放结束时的回调函数
  78. onAudioBecomingNoisy={this.onAudioBecomingNoisy} //音频变得嘈杂时的回调 - 应暂停视频
  79. onAudioFocusChanged={this.onAudioFocusChanged} //音频焦点丢失时的回调 - 如果焦点丢失则暂停
  80. repeat={this.state.wheel} //确定在到达结尾时是否重复播放视频。
  81. onError={this.onError} // 当视频不能加载,或出错后的回调函数
  82. playInBackground={false} // 当app转到后台运行的时候,播放是否暂停
  83. playWhenInactive={true} // [iOS] Video continues to play when control or notification center are shown. 仅适用于IOS
  84. />
  85. <View
  86. style={{
  87. position: "absolute",
  88. width: "100%",
  89. height: "100%",
  90. alignItems: "center",
  91. justifyContent: "center"
  92. }}
  93. >
  94. <Loading show={this.state.show_loading} />
  95. </View>
  96. <TopController
  97. ref={view => (this.topcontroller = view)}
  98. needback={this.state.needback}
  99. videoback={this.videoBackClick.bind(this)}
  100. />
  101. <BottomController
  102. ref={view => (this.bottomcontroller = view)}
  103. show={this.state.show_controller}
  104. touch_up_callback={this.touch_up_callback.bind(this)}
  105. full_click={this.presentFullscreenPlayer.bind(this)}
  106. play_click={this.play.bind(this)}
  107. duration={this.state.duration}
  108. currentTime={this.state.currentTime}
  109. player_status_icon={this.state.player_status_icon}
  110. />
  111. </View>
  112. );
  113. }
  114. componentWillMount() {
  115. this.videotouch = PanResponder.create({
  116. onStartShouldSetPanResponder: (evt, gestureState) => true,
  117. onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
  118. onMoveShouldSetPanResponder: (evt, gestureState) => true,
  119. onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
  120. onPanResponderGrant: (evt, gestureState) => {
  121. this.pressStatus = true;
  122. this.config.changeY = evt.nativeEvent.pageY;
  123. this.config.changeX = evt.nativeEvent.pageX;
  124. if (this.state.show_controller) {
  125. this.hideController();
  126. } else {
  127. this.showController();
  128. }
  129. console.log("onPanResponderGrant");
  130. },
  131. onPanResponderStart: (evt, gestureState) => {
  132. this.pressStatus = true;
  133. console.log("onPanResponderStart");
  134. },
  135. onPanResponderMove: (evt, gestureState) => {
  136. this.config.yDiff = evt.nativeEvent.pageY - this.config.changeY;
  137. this.config.xDiff = evt.nativeEvent.pageX - this.config.changeX;
  138. this.config.changeY = evt.nativeEvent.pageY;
  139. this.config.changeX = evt.nativeEvent.pageX;
  140. },
  141. onPanResponderEnd: (evt, gestureState) => {
  142. this.pressStatus = true;
  143. console.log("onPanResponderEnd");
  144. },
  145. onPanResponderTerminationRequest: (evt, gestureState) => true,
  146. onPanResponderRelease: (evt, gestureState) => {
  147. if (this.pressStatus) {
  148. this.props.onPress && this.props.onPress();
  149. }
  150. },
  151. onPanResponderTerminate: (evt, gestureState) => {
  152. // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
  153. },
  154. onShouldBlockNativeResponder: (evt, gestureState) => {
  155. // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
  156. // 默认返回true。目前暂时只支持android。
  157. //基于业务交互场景,如果这里使用js事件处理,会导致容器不能左右滑动。所以设置成false.
  158. return false;
  159. }
  160. });
  161. }
  162. loadStart() {
  163. // alert("loadStart");
  164. }
  165. onBuffer({ isBuffering }: { isBuffering: boolean }) {
  166. //true为正在加载,false为没有加载,此处给loading提示
  167. this.setState({
  168. show_loading: isBuffering
  169. });
  170. console.log("isBuffering:" + isBuffering);
  171. }
  172. onLoad = data => {
  173. //获取的是秒数
  174. this.setState({ duration: data.duration });
  175. this.bottomcontroller.setMax(data.duration);
  176. };
  177. onProgress = data => {
  178. this.setState({
  179. currentTime: data.currentTime
  180. });
  181. if (this.state.show_controller) {
  182. this.bottomcontroller.setProgress(this.state.currentTime);
  183. this.bottomcontroller.setMax(this.state.duration);
  184. }
  185. };
  186. onError() {
  187. alert("播放器异常");
  188. }
  189. onEnd() {
  190. alert("播放结束");
  191. }
  192. play() {
  193. //controller的play点击无法换图...
  194. if (this.state.paused) {
  195. this.start();
  196. } else {
  197. this.pause();
  198. }
  199. }
  200. pause() {
  201. this.setState({
  202. paused: true,
  203. player_status_icon: require("../images/video/start.png")
  204. });
  205. this.player_icon_index = 1;
  206. }
  207. start() {
  208. this.setState({
  209. paused: false,
  210. player_status_icon: require("../images/video/pause.png")
  211. });
  212. this.player_icon_index = 0;
  213. }
  214. seekTo(progress) {
  215. this.player.seek(progress);
  216. }
  217. presentFullscreenPlayer() {
  218. if (this.state.isFull) {
  219. this.setState({
  220. isFull: false,
  221. needback: this.props.needback
  222. });
  223. } else {
  224. this.setState({
  225. isFull: true,
  226. needback: true
  227. });
  228. }
  229. this.props.videofullScreenPlayer();
  230. this.bottomcontroller.setProgress(this.state.currentTime);
  231. }
  232. touch_up_callback(progress) {
  233. //抬起之后,获取算出来的progress
  234. this.setState({
  235. currentTime: progress
  236. });
  237. this.seekTo(progress);
  238. }
  239. refreshVideo() {
  240. this.setState({
  241. duration: 0,
  242. currentTime: 0
  243. });
  244. }
  245. videoBackClick() {
  246. // if (this.props.needback != undefined && this.props.needback) {
  247. // }
  248. if (this.state.isFull) {
  249. //全屏状态下,变小屏
  250. this.presentFullscreenPlayer();
  251. } else {
  252. this.props.videoback();
  253. }
  254. }
  255. showController() {
  256. this.setState({
  257. show_controller: true
  258. // needback: this.props.needback
  259. });
  260. this.bottomcontroller.setBottom(0);
  261. this.topcontroller.setTop(0);
  262. }
  263. hideController() {
  264. this.setState({
  265. show_controller: false
  266. // needback: false
  267. });
  268. this.bottomcontroller.setBottom(-50);
  269. this.topcontroller.setTop(-50);
  270. }
  271. }
  272. /**
  273. * 将秒转换为 分:秒
  274. * s int 秒数
  275. */
  276. function formatTime(s) {
  277. //计算分钟
  278. //算法:将秒数除以60,然后下舍入,既得到分钟数
  279. var h;
  280. h = Math.floor(s / 60);
  281. //计算秒
  282. //算法:取得秒%60的余数,既得到秒数
  283. s = Math.round(s % 60);
  284. //将变量转换为字符串
  285. h += "";
  286. s += "";
  287. //如果只有一位数,前面增加一个0
  288. h = h.length == 1 ? "0" + h : h;
  289. s = s.length == 1 ? "0" + s : s;
  290. return h + ":" + s;
  291. }
  292. class TopController extends Component {
  293. state = {
  294. controller_top: 0
  295. };
  296. render() {
  297. if (this.props.needback == false) {
  298. return null;
  299. }
  300. return (
  301. <View
  302. style={[styles.player_controller, { top: this.state.controller_top }]}
  303. >
  304. <View
  305. style={{
  306. flex: 1,
  307. alignItems: "center"
  308. }}
  309. >
  310. <TouchableOpacity
  311. //点击事件放在这个组件里才管用
  312. style={{
  313. justifyContent: "center",
  314. width: "100%",
  315. height: "100%"
  316. }}
  317. onPress={() => this.props.videoback()}
  318. >
  319. <Image
  320. style={[styles.player_pause_icon]}
  321. source={require("../images/video/back.png")}
  322. />
  323. </TouchableOpacity>
  324. </View>
  325. <View style={{ flex: 2 }} />
  326. <View
  327. style={{
  328. flex: 9,
  329. overflow: "hidden"
  330. }}
  331. />
  332. <View style={{ flex: 2 }} />
  333. <View
  334. style={{
  335. flex: 1,
  336. alignItems: "center"
  337. }}
  338. />
  339. </View>
  340. );
  341. }
  342. setTop(top) {
  343. this.setState({
  344. controller_top: top
  345. });
  346. }
  347. }
  348. class Loading extends Component {
  349. state = {
  350. image_arr: [
  351. require("../images/video/loading1.png"),
  352. require("../images/video/loading2.png"),
  353. require("../images/video/loading3.png"),
  354. require("../images/video/loading3.png"),
  355. require("../images/video/loading4.png"),
  356. require("../images/video/loading5.png"),
  357. require("../images/video/loading6.png"),
  358. require("../images/video/loading7.png"),
  359. require("../images/video/loading8.png"),
  360. require("../images/video/loading9.png"),
  361. require("../images/video/loading10.png"),
  362. require("../images/video/loading11.png"),
  363. require("../images/video/loading12.png"),
  364. require("../images/video/loading13.png"),
  365. require("../images/video/loading14.png"),
  366. require("../images/video/loading15.png"),
  367. require("../images/video/loading16.png"),
  368. require("../images/video/loading17.png"),
  369. require("../images/video/loading18.png"),
  370. require("../images/video/loading19.png"),
  371. require("../images/video/loading20.png")
  372. ],
  373. loading_index: 0
  374. };
  375. render() {
  376. if (this.props.show) {
  377. return (
  378. <Image
  379. source={this.state.image_arr[this.state.loading_index]}
  380. style={{ width: "20%", height: "10%", backgroundColor: "blue" }}
  381. />
  382. );
  383. } else {
  384. return null;
  385. }
  386. }
  387. }
  388. class BottomController extends Component {
  389. state = {
  390. controller_bottom: 0
  391. };
  392. render() {
  393. return (
  394. <View
  395. style={[
  396. styles.player_controller,
  397. { bottom: this.state.controller_bottom }
  398. ]}
  399. >
  400. <View
  401. style={{
  402. flex: 1,
  403. alignItems: "center"
  404. }}
  405. >
  406. {/* 暂停播放按钮 */}
  407. <TouchableOpacity
  408. //点击事件放在这个组件里才管用
  409. style={{
  410. justifyContent: "center",
  411. width: "100%",
  412. height: "100%"
  413. }}
  414. onPress={() => this.props.play_click()}
  415. >
  416. <Image
  417. style={[styles.player_pause_icon]}
  418. source={this.props.player_status_icon}
  419. />
  420. </TouchableOpacity>
  421. </View>
  422. <View style={{ flex: 2 }}>
  423. {/* //左侧当前播放时长 */}
  424. <Text style={[styles.player_time]}>
  425. {formatTime(this.props.currentTime)}
  426. </Text>
  427. </View>
  428. <View
  429. style={{
  430. flex: 9,
  431. overflow: "hidden"
  432. }}
  433. >
  434. {/* //中间进度条 */}
  435. <SeekBar
  436. style={{ flex: 1 }}
  437. ref={view => (this.seekbar = view)}
  438. //必须带此方法,作为滑动之后抬起的回调
  439. touchUpCallBack={this.props.touch_up_callback}
  440. />
  441. </View>
  442. <View style={{ flex: 2 }}>
  443. {/* //右侧总时长 */}
  444. <Text style={[styles.player_time]}>
  445. {formatTime(this.props.duration)}
  446. </Text>
  447. </View>
  448. <View
  449. style={{
  450. flex: 1,
  451. alignItems: "center"
  452. }}
  453. >
  454. <TouchableOpacity
  455. style={{
  456. justifyContent: "center",
  457. width: "100%",
  458. height: "100%"
  459. }}
  460. onPress={() => this.props.full_click()}
  461. >
  462. {/* //放大按钮 */}
  463. <Image
  464. style={[styles.fullscreen_icon]}
  465. source={require("../images/video/fullscreen.png")}
  466. />
  467. </TouchableOpacity>
  468. </View>
  469. </View>
  470. );
  471. }
  472. setMax(duration) {
  473. this.seekbar.setMax(duration);
  474. }
  475. getMax() {
  476. return this.seekbar.getMax();
  477. }
  478. setBottom(bottom) {
  479. this.setState({
  480. controller_bottom: bottom
  481. });
  482. }
  483. setProgress(currentTime) {
  484. this.seekbar.setProgress(currentTime);
  485. }
  486. }
  487. const styles = StyleSheet.create({
  488. player_controller: {
  489. flexDirection: "row",
  490. position: "absolute",
  491. width: "100%",
  492. height: 50,
  493. alignItems: "center",
  494. justifyContent: "center"
  495. },
  496. player_pause_icon: {
  497. width: "80%",
  498. resizeMode: "center",
  499. height: "40%",
  500. justifyContent: "center",
  501. alignItems: "center",
  502. left: 5
  503. },
  504. fullscreen_icon: {
  505. width: "80%",
  506. resizeMode: "center",
  507. height: "30%",
  508. justifyContent: "center",
  509. alignItems: "center"
  510. },
  511. player_time: {
  512. fontSize: 18,
  513. color: "white",
  514. alignItems: "center",
  515. justifyContent: "center",
  516. textAlignVertical: "center",
  517. textAlign: "center"
  518. }
  519. });
  520. /**
  521. 使用方法
  522. <CusVideo
  523. uri={this.state.video_uri}
  524. ref={view => (this.video = view)}
  525. needback={true} //(是否需要小窗口的返回按钮)
  526. videoback={this.clickVideoBack.bind(this)}//(小窗口返回按钮的事件)
  527. videofullScreenPlayer={this.fullScreenPlayer.bind(this)}//(点击全屏按钮的事件)
  528. style={{
  529. flex: this.state.video_flex,
  530. width: this.state.video_width,
  531. height: this.state.video_height
  532. }}
  533. />
  534. */