react-app-shell
Version:
react打包脚本和example, 这里的版本请忽略
340 lines (301 loc) • 9.2 kB
JavaScript
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import * as tools from '../../../utils/tools';
import * as styles from './video.less';
import playImgSrc from '../../../public/images/feedback/play-btn.png';
/**
* 视频播放, 兼容H5 IOS和安卓
*/
class InnerVideoPlayer extends PureComponent {
static propTypes = {
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
className: PropTypes.string,
preload: PropTypes.string,
poster: PropTypes.string, // 封面图片
src: PropTypes.string, // 视频资源地址
userAgent: PropTypes.string, // 服务端渲染时, 客户端浏览器请求的UA
onPlay: PropTypes.func, // 函数: 播放回调
onPlaying: PropTypes.func, // 函数:播放中回调
onPause: PropTypes.func, // 函数: 播放暂停
onEnd: PropTypes.func, // 函数: 播放结束回调
onClose: PropTypes.func // 函数: 关闭回调
};
constructor(props) {
super(props);
// 添加容器
this.container = document.createElement('div');
document.body.appendChild(this.container);
const { userAgent } = this.props;
this.isIOS = tools.isIOS(userAgent); // 是否是iOS系统
this.isAndroid = tools.isAndroid(userAgent); // 是否是Android系统
this.status = ''; // 状态, reset 已重置, play 已播放, 空字符串/默认值 没有播放过
}
componentDidMount() {
// 禁止滚动
this.container.addEventListener('touchmove', this.preventTouchMove, false);
// 设置内联播放
if (this.isIOS) {
this.player.setAttribute('playsinline', true); // 禁止 iPhone Safari 视频自动全屏
this.player.setAttribute('webkit-playsinline', true); // 禁止 iPhone Safari 视频自动全屏
} else {
this.player.setAttribute('x5-playsinline', true);
}
// 播放
this.player.play();
}
componentWillUnmount() {
clearTimeout(this.playTimer);
// 取消事件监听
this.container.removeEventListener('touchmove', this.preventTouchMove, false);
document.body.removeChild(this.container);
}
/**
* 监听终端横竖屏切换
*/
// watchOrientation = () => {
// if (window.orientation === 90 || window.orientation === -90) {
// this.fullScreen();
// } else {
// this.exitFullScreen();
// }
// console.log(window.orientation, 111);
// }
// /**
// * 进入全屏
// */
// fullScreen = () => {
// const ele = document.documentElement;
// if (ele.requestFullscreen) {
// ele.requestFullscreen();
// } else if (ele.mozRequestFullScreen) {
// ele.mozRequestFullScreen();
// } else if (ele.webkitRequestFullScreen) {
// ele.webkitRequestFullScreen();
// } else if (ele.msRequestFullscreen) {
// ele.msRequestFullscreen();
// }
// // alert(ele.requestFullscreen);
// }
// /**
// * 退出全屏
// */
// exitFullScreen = () => {
// var de = document;
// if (de.exitFullscreen) {
// de.exitFullscreen();
// } else if (de.mozCancelFullScreen) {
// de.mozCancelFullScreen();
// } else if (de.webkitCancelFullScreen) {
// de.webkitCancelFullScreen();
// } else if (de.msExitFullscreen) {
// de.msExitFullscreen();
// }
// }
player = null;
playTimer = null;
preventTouchMove = (event) => {
event.stopPropagation();
event.preventDefault();
};
/**
* 开始播放
*/
handlePlay = () => {
const { onPlay } = this.props;
onPlay && onPlay();
this.status = 'play';
};
/**
* 视频播放中
*/
handlePlaying = () => {
const { onPlaying } = this.props;
onPlaying && onPlaying();
};
/**
* 暂停
*/
handlePause = () => {
const { onPause } = this.props;
onPause && onPause();
};
/**
* 播放结束
*/
handleEnded = () => {
const { onEnd } = this.props;
if (this.isAndroid) {
// 先播放
this.player.play();
// 延迟关闭, 解决安卓全屏播放结束后的广告问题
this.playTimer = setTimeout(() => {
this.player.pause();
}, 60);
}
onEnd && onEnd();
};
/**
* 关闭视频播放层
*/
handleClose = () => {
this.changeViewHeight('visible', 'auto');
this.props.onClose && this.props.onClose();
};
/**
* IOS视频播放器
* 使用原生播放按钮
* @returns {*}
*/
renderIOSContent = () => {
const { width, src, poster, className, preload } = this.props;
return (
<div className={styles.video}>
<video
ref={(player) => {
this.player = player;
}}
width={width}
style={{ objectFit: 'fill' }}
controls={true}
poster={poster}
preload={preload}
className={className}
onPlay={this.handlePlay}
onPause={this.handlePause}
onPlaying={this.handlePlaying}
onEnded={this.handleEnded}
>
<source src={src} type="video/mp4" />
当前浏览器不支持最新的video播放
</video>
</div>
);
};
/**
* 安卓视频播放器
* @returns {*}
*/
renderAndroidContent = () => {
const { width, src, poster, className, preload } = this.props;
return (
<div className={styles.video}>
<video
ref={(player) => {
this.player = player;
}}
controls={false}
width={width}
style={{ objectFit: 'fill' }}
poster={poster}
src={src}
preload={preload}
className={className}
onPlay={this.handlePlay}
onPause={this.handlePause}
onPlaying={this.handlePlaying}
onEnded={this.handleEnded}
>
当前浏览器不支持最新的video播放
</video>
</div>
);
};
renderVideo = () => {
return (
<div className={styles.videoBox} id="video-box">
<div className={styles.close} onClick={this.handleClose}>
×
</div>
{this.isIOS ? this.renderIOSContent() : this.renderAndroidContent()}
</div>
);
};
changeViewHeight = (over, height) => {
document.getElementsByTagName('body')[0].style.overflow = over;
document.getElementsByTagName('body')[0].style.height = height;
};
render() {
this.changeViewHeight('hidden', '100px');
return ReactDOM.createPortal(this.renderVideo(), this.container);
}
}
/**
* 图片视频播放器
*/
class VideoImagePlayer extends PureComponent {
static propTypes = {
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
className: PropTypes.string,
preload: PropTypes.string,
poster: PropTypes.string, // 封面图片
src: PropTypes.string, // 视频资源地址
userAgent: PropTypes.string, // 服务端渲染时, 客户端浏览器请求的UA
onPlay: PropTypes.func, // 函数: 播放回调
onPlaying: PropTypes.func, // 函数:播放中回调
onPause: PropTypes.func, // 函数: 播放暂停
onEnd: PropTypes.func // 函数: 播放结束回调
};
static defaultProps = {
width: 300, // 视频宽高比为: 3:4
height: 400, // IOS、android 高度自适应
className: '',
preload: 'metadata', // auto: 默认下载视频 metadata: 只提取视频元数据 none: 不会预加载视频
poster: '',
src: '',
userAgent: '',
onPlay: () => {},
onPlaying: () => {},
onPause: () => {},
onEnd: () => {}
};
constructor(props) {
super(props);
const { userAgent } = this.props;
this.isIOS = tools.isIOS(userAgent); // 是否是iOS系统
this.isAndroid = tools.isAndroid(userAgent); // 是否是Android系统
this.status = ''; // 状态, reset 已重置, play 已播放, 空字符串/默认值 没有播放过
this.state = {
status: 'hide' // 是否显示视频播放
};
}
componentDidMount() {
this.clientWidth = document.body.clientWidth || document.documentElement.clientWidth;
}
/**
* 点击图片的播放按钮
*/
handleImagePlay = (event) => {
event.stopPropagation();
this.setState({
status: 'show'
});
};
/**
* 关闭视频播放层
*/
handleClose = () => {
this.setState({
status: 'jode'
});
};
render() {
const { status } = this.state;
const { poster } = this.props;
return (
<>
<div className={styles.imgBox}>
<img src={poster} className={classNames(styles.videoImage, 'lazyload')} />
<img src={playImgSrc} className={styles.playButton} onClick={this.handleImagePlay} />
</div>
{status === 'show' ? (
<InnerVideoPlayer onClose={this.handleClose} {...this.props} width={this.clientWidth} />
) : null}
</>
);
}
}
export default VideoImagePlayer;