Skip to content

Instantly share code, notes, and snippets.

@akellbl4
Last active October 31, 2019 11:36
Show Gist options
  • Save akellbl4/033502ce3218f40821a03394810c9685 to your computer and use it in GitHub Desktop.
Save akellbl4/033502ce3218f40821a03394810c9685 to your computer and use it in GitHub Desktop.
import React, { Component } from 'react'
import { createPortal } from 'react-dom'
import { fetchAndPlay, playVideo } from 'utils/video'
import throttle from 'lodash/throttle'
import PlayBG from 'views/NewApp/components/PlayBG'
import styles from './BGVideo.css'
class BGVideo extends Component {
componentDidMount() {
this.loadBackground()
window.addEventListener('resize', this.handleResize)
window.addEventListener('scroll', this.handleScroll)
this.lastSize = window.innerWidth
}
shouldComponentUpdate({ placeholder, showPlay }) {
return showPlay !== this.props.showPlay || placeholder !== this.props.placeholder
}
componendWillUnmount() {
window.removeEventListener('resize', this.handleResize)
window.removeEventListener('scroll', this.handleScroll)
}
lastSize = 0
handleResize = throttle(() => {
const { innerWidth } = window
if ((innerWidth < 768 && this.lastSize < 768) || (innerWidth >= 768 && this.lastSize >= 768)) {
this.lastSize = innerWidth
return
}
this.lastSize = innerWidth
this.loadBackground()
}, 800)
handleScroll = throttle(() => {
const { pageYOffset, innerHeight } = window
if (pageYOffset > innerHeight * 3) {
if (this.video) {
Object.assign(this.video.style, {
opacity: 0,
})
}
} else {
if (this.video) {
Object.assign(this.video.style, {
opacity: 1,
})
}
}
}, 200)
manualPlayVideo = async () => {
await playVideo({
video: this.video,
onSuccess: () => {
this.animateTransition(true)
this.props.setShowPlay(false)
},
})
}
animateTransition = (fast = false) => {
if (this.video) {
Object.assign(this.video.style, {
opacity: '1',
})
}
if (this.frame && fast) {
Object.assign(this.frame.style, {
opacity: '0',
transition: 'opacity 0.4s',
})
return
}
setTimeout(() => {
if (this.frame) {
Object.assign(this.frame.style, {
opacity: '0',
transition: 'opacity 0.4s',
})
}
}, 700)
}
loadBackground = () => {
const { desktopVideo, mobileVideo } = this.props
const url = this.props.mobile ? mobileVideo : desktopVideo
fetchAndPlay({
video: this.video,
url,
onSuccess: this.animateTransition,
onNotSupported: () => {},
onNotAllowed: () => {
this.props.setShowPlay(true)
},
})
}
setFrame = (ref) => {
this.frame = ref
}
setVideo = (ref) => {
this.video = ref
}
render() {
const { placeholder, showPlay, playIcon } = this.props
return (
<div className={styles.container} id="_bgvideo">
<div className={styles.frame} ref={this.setFrame} style={{ backgroundImage: `url(${placeholder})` }} />
<video ref={this.setVideo} className={styles.video} webkit-playsinline="true" muted loop playsInline>
Извините, ваш браузер не поддерживает видео
</video>
{showPlay &&
createPortal(
<PlayBG onClick={this.manualPlayVideo} icon={playIcon} className={styles.play} />,
document.getElementById('modal-root'),
)}
</div>
)
}
}
export default BGVideo
import getVideo from 'views/NewApp/getVideo'
interface VideoPlayArgs {
video: HTMLVideoElement
url: string
onSuccess?: () => void
onNotSupported: () => void
onNotAllowed: () => void
inline?: boolean
autoPlay?: boolean
}
interface PlayVideoProps {
video: HTMLVideoElement
onSuccess?: () => void
onNotSupported: () => void
onNotAllowed: () => void
}
export const playVideo = async ({ video, onSuccess, onNotSupported, onNotAllowed }: PlayVideoProps) => {
try {
await video.play()
if (onSuccess) onSuccess()
} catch (err) {
switch (err.name) {
case 'NotAllowedError':
onNotAllowed()
break
case 'NotSupportedError':
onNotSupported()
break
case 'AbortError':
default:
if (process.env.NODE_ENV === 'development') {
logger.error(err)
}
}
}
}
interface FetchAndPlayProps extends PlayVideoProps {
url: string
inline?: boolean
autoPlay?: boolean
}
let times = 0
export const fetchAndPlay = async (options: FetchAndPlayProps) => {
const { video, url, inline = true, autoPlay = true, ...props } = options
// retry if video elem is not rendered yet
if (times >= 3) return
if (!video) {
times += 1
setTimeout(fetchAndPlay, 300, options)
return
}
const blob = await getVideo(url)
video.src = blob
video.muted = true
video.disablePictureInPicture = true
if (inline) {
video.playsInline = true
video.webkitPlaysinline = true
}
if (autoPlay) {
await playVideo({ video, ...props })
}
return blob
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment