HTML 视频标签 - 在没有媒体属性的情况下更改视口上的来源

HTML video tag - change source on viewport without media attribute

我正在尝试使用 React 在 HTML 中实现一个视频。因此,如果视口宽度小于 750px,我想将视频源更改为更小的分辨率/更小的文件(因为高数据传输等)。

所以在一个完美的 HTML 世界中,你会构建这个:

<video controls> 
   <source src="video-small.mp4" type="video/mp4" media="all and (max-width: 750px)"> 
   <source src="video.mp4" type="video/mp4"> 
</video>

由于 media 不是(或不再是)specified in the source tag if it's inside of a video tag,我不能再使用它了。因为Chrome显示的是第一个来源,即手机视频。无论是台式机还是移动设备。

第二种解决方案 是包裹一个容器并将 display: none; 设置为不可见视频:

import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import LazyLoad from 'react-lazyload'

const MobileVideo = styled.div`
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: none;
  ${({theme}) => theme.media.mobile`
    display: block;
  `}
`

const DesktopVideo = styled.div`
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  ${({theme}) => theme.media.mobile`
    display: none;
  `}
`

const VideoComponent = ({
  srcWebm,
  srcMp4,
  mobileSrcWebm,
  mobileSrcMp4,
  poster,
  className,
  forwardedRef,
  ...rest
}) => (
  <React.Fragment>
    <MobileVideo>
      <video
        playsInline
        poster={poster.url}
        className={className}
        ref={forwardedRef}
        {...rest}
      >
        {mobileSrcWebm.url !== '' && (
          <source src={mobileSrcWebm.url} type="video/webm" />
        )}
        {mobileSrcMp4.url !== '' && (
          <source src={mobileSrcMp4.url} type="video/mp4" />
        )}
      </video>
    </MobileVideo>
    <DesktopVideo>
      <video
        playsInline
        poster={poster.url}
        className={className}
        ref={forwardedRef}
        {...rest}
      >
        {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
        {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
      </video>
    </DesktopVideo>
  </React.Fragment>
)

在这种情况下,显示的是正确的视频,但两个视频都已下载(在 Chrome 中)。隐藏不会阻止浏览器下载。悲伤!

第三个解决方案是使用第二个解决方案并从 DOM:

中删除不可见的组件
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import LazyLoad from 'react-lazyload'

import {sizes} from '../../lib/ThemeProvider/media'

const MobileVideo = styled.div`
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: none;
  ${({ theme }) => theme.media.mobile`
    display: block;
  `}
`

const DesktopVideo = styled.div`
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  ${({ theme }) => theme.media.mobile`
    display: none;
  `}
`

class VideoComponent extends React.Component {
  state = {
    showMobileSrc: true
  }

  componentDidMount() {
    this.resize()
    window.addEventListener('resize', this.resize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }

  resize = () => {
    if (window.innerWidth >= sizes.mobile) {
      this.setState({ showMobileSrc: false })
    } else {
      this.setState({ showMobileSrc: true })
    }
  }

  render() {
    const { srcWebm,
      srcMp4,
      mobileSrcWebm,
      mobileSrcMp4,
      poster,
      className,
      forwardedRef,
      ...rest
    } = this.props
    const {showMobileSrc} = this.state
    return (
      <React.Fragment>
        {showMobileSrc && <MobileVideo>
          <video
            playsInline
            poster={poster.url}
            className={className}
            ref={forwardedRef}
            {...rest}
          >
            {mobileSrcWebm.url !== '' && (
              <source src={mobileSrcWebm.url} type="video/webm" />
            )}
            {mobileSrcMp4.url !== '' && (
              <source src={mobileSrcMp4.url} type="video/mp4" />
            )}
          </video>
        </MobileVideo>}
        {!showMobileSrc && <DesktopVideo>
          <video
            playsInline
            poster={poster.url}
            className={className}
            ref={forwardedRef}
            {...rest}
          >
            {srcWebm.url !== '' && <source src={srcWebm.url} type="video/webm" />}
            {srcMp4.url !== '' && <source src={srcMp4.url} type="video/mp4" />}
          </video>
        </DesktopVideo>}
      </React.Fragment>
    )
  }
}

但是 chrome 正在下载这两个视频。 HTML 似乎是正确的。不知道 chrome 在那里做什么..

首先,我真的不明白为什么他们从视频标签内的源标签中删除了媒体属性。这并没有始终如一地实施。

无论如何:我怎样才能在定义的视口宽度更改源并防止下载两个视频?

简单 JavaScript 解决方案(不是特定于 ReactJS,但请参阅 this component 了解纯 ReactJS 解决方案)

if (matchMedia) {
    var mq = window.matchMedia("(min-width: 600px)");
    mq.addListener(WidthChange);
}

function WidthChange(mq) {
    if (mq.matches) {
    // set source to desktop
    } else {
    // set source to mobile
}
}