欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React如何實現(xiàn)視頻旋轉(zhuǎn)縮放

 更新時間:2024年11月18日 08:48:48   作者:王耀  
這篇文章主要為大家詳細介紹了React如何實現(xiàn)視頻旋轉(zhuǎn)縮放功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

一、背景

原本我們預覽視頻僅僅是簡單的video標簽實現(xiàn)就可以滿足業(yè)務的需求了,但是某一天,產(chǎn)品說:”業(yè)務的視頻是用手機拍的,方向不一定是正的,還有視頻的寬高太小了看不清,所以希望我們能讓視頻做到旋轉(zhuǎn)跳躍(不是)旋轉(zhuǎn)+按比例縮小放大“。不過吐槽歸吐槽,既然需求合理該做還是得做啊。

二、實現(xiàn)

這個功能初時看起來很頭疼,在細細思考下來后發(fā)現(xiàn),實現(xiàn)思路其實并不復雜,可以通過transform去輾轉(zhuǎn)騰挪,最終完成我們想要的效果。

1. 原始效果

我們最初始的效果就是如同圖片預覽一般,點擊彈窗播放視頻,所以這里僅僅使用了video標簽。

2. 還原原生video控制欄功能

如果我們直接去旋轉(zhuǎn)video標簽,那么他的控制欄也會跟著旋轉(zhuǎn),這并不符合我們的期望,所以我們需要在video標簽外加一層容器,然后自定義控制欄和viode同級,做到只旋轉(zhuǎn)video標簽。

但是這里我嫌自定義控制欄太麻煩,于是這里選擇了一個自帶UI的三方視頻組件進行改造。這里采用的是vime,有興趣了解的可以去vimejs.com/。最終是展示這個樣子

3. 旋轉(zhuǎn)

以上前置工作做完之后,接下來就進入正題,添加我們的旋轉(zhuǎn)功能。利用transform對video進行旋轉(zhuǎn),同時寬高數(shù)值對換。由于vime中video標簽設(shè)置了position: absolute,所以這里我們還要調(diào)整初始的topleft

import { Player, Video, DefaultUi, Settings, MenuItem } from '@vime/react';

// 初始寬度
const INIT_WIDTH = 370;
// 初始高度
const INIT_HEIGHT = 658;

const Demo = ({ src }) => {
  const [visible, setVisible] = useState(false);
  // vime組件寬高比例
  const [aspectRatio, setAspectRatio] = useState('9:16');
  // 容器寬度,vime組件會根據(jù)容器寬高自適應
  const [width, setWidth] = useState<string | number>(INIT_WIDTH);
  // 旋轉(zhuǎn)角度
  const degRef = useRef(0);

  useEffect(() => {
    if (src) {
      setVisible(true);
    } else {
      setVisible(false);
    }
  }, [src]);

  const closeVideoPreview = () => {
    setVisible(false);
  };

  const onRotate = () => {
    const videoEl: HTMLVideoElement | null = document.querySelector('.sc-vm-file');
    if (!videoEl) return;
    const vWidth = INIT_WIDTH;
    const vHeight = INIT_HEIGHT;
    const deg = degRef.current < 270 ? degRef.current + 90 : 0;
    // 旋轉(zhuǎn)后樣式
    let newRatio = '16:9';
    let newWidth = vHeight;
    let resWidth = `${vWidth}px`;
    let resHeight = `${vHeight}px`;
    let top = (vWidth - vHeight) / 2;
    let left = (vHeight - vWidth) / 2;
    // 水平角度復原處理
    if (deg === 0 || deg === 180) {
      newWidth = INIT_WIDTH;
      newRatio = '9:16';
      resWidth = '100%';
      resHeight = '100%';
      top = 0;
      left = 0;
    }

    videoEl.style.width = resWidth;
    videoEl.style.height = resHeight;
    videoEl.style.transform = `rotate(${deg}deg)`;
    videoEl.style.top = `${top}px`;
    videoEl.style.left = `${left}px`;
    setWidth(newWidth);
    setAspectRatio(newRatio);
  };

  return (
    <>
      {visible ? (
        <div className='video-preview'>
          <div className='video-preview-mask' onClick={() => closeVideoPreview()} />
          <div className="video-preview-content" onClick={() => closeVideoPreview()}>
            <div
              className="video-preview-box"
              style={{ width }}
              onClick={(event) => {
                event.stopPropagation();
              }}
            >
              <Player
                icons="custom"
                aspectRatio={aspectRatio}
              >
                <Video>
                  <source data-src={src} />
                </Video>

                <DefaultUi noControls>
                  // 。。。
                  <Settings active={openMenu}>
                    <MenuItem label="旋轉(zhuǎn)" onClick={onRotate} />
                  </Settings>
                </DefaultUi>
              </Player>
            </div>
          </div>
        </div>
      ) : null}
    </>
  );
};

此時就能實現(xiàn)以下效果:

4. 全屏

上面雖然已經(jīng)實現(xiàn)了旋轉(zhuǎn)效果,但是并沒有結(jié)束,當我們點擊全屏功能之后,此時旋轉(zhuǎn)的樣式,就變得奇怪了

這是由于我們先前寫死了寬高數(shù)值,導致旋轉(zhuǎn)后video寬高沒有適應全屏,將全屏的寬高設(shè)置為100vh跟100vw即可兼容全屏狀態(tài)下的旋轉(zhuǎn)。

const player = useRef<HTMLVmPlayerElement>(null);

const changeStyle = (deg: number) => {
  const videoEl: HTMLVideoElement | null = document.querySelector('.sc-vm-file');
  if (!videoEl) return;
  // 獲取窗口的寬度
  const screenWidth = window.innerWidth;
  // 獲取整個屏幕的高度
  const screenFullHeight = screen.height;
  const vWidth = INIT_WIDTH;
  const vHeight = INIT_HEIGHT;
  // 旋轉(zhuǎn)后樣式
  // ...
  // 當全屏旋轉(zhuǎn)90/270度時,寬高處理
  if (player.current?.isFullscreenActive) {
    newWidth = 'auto';
    resWidth = '100vh';
    resHeight = '100vw';
    top = (screenFullHeight - screenWidth) / 2;
    left = (screenWidth - screenFullHeight) / 2;
  }
  // ...
};

// 旋轉(zhuǎn)
const onRotate = () => {
  setOpenMenu(false);
  const newDeg = degRef.current < 270 ? degRef.current + 90 : 0;
  degRef.current = newDeg;
  changeStyle(newDeg);
};

// 全屏
const onVmFullscreenChange = () => {
  if (degRef.current === 90 || degRef.current === 270) {
    changeStyle(degRef.current);
  }
};

return (
  ...
  <Player
    ref={player}
    aspectRatio={aspectRatio}
    onVmFullscreenChange={onVmFullscreenChange}
  >
      ...
  </Player>
)

全屏+旋轉(zhuǎn) 效果展示

5. 比例縮放

走到這里,旋轉(zhuǎn)功能原本已經(jīng)完成,但是不要忘記還有一個縮小放大功能,從全屏旋轉(zhuǎn)的例子來看,縮放功能也會影響到旋轉(zhuǎn)效果,還要對旋轉(zhuǎn)再做兼容處理。

我們先實現(xiàn)縮放功能:

// 比例
const [scale, setScale] = useState('1');

// 比例縮放
const changeScale = (event: Event) => {
  const radio = event.target as HTMLVmMenuRadioElement;
  const scaleVal = Number(radio.value);
  setScale(radio.value);
  setOpenMenu(false);
  const videoEl: HTMLVideoElement | null = document.querySelector('.sc-vm-file');
  if (!videoEl) return;
  // 寬高 * 選中比例
  const vWidth = INIT_WIDTH * scaleVal;
  const vHeight = INIT_HEIGHT * scaleVal;
  // 視頻設(shè)置新寬高
  videoEl.style.width = `${vWidth}px`;
  videoEl.style.height = `${vHeight}px`;
  setWidth(vWidth);
};

return (
  ...
    <Submenu label="縮放比例" hint={scale}>
      <MenuRadioGroup value={scale} onVmCheck={changeScale}>
        <MenuRadio label="1" value="1" />
        <MenuRadio label="1.2" value="1.2" />
        <MenuRadio label="1.4" value="1.4" />
        <MenuRadio label="1.6" value="1.6" />
        <MenuRadio label="1.8" value="1.8" />
        <MenuRadio label="2" value="2" />
      </MenuRadioGroup>
    </Submenu>
  ...
)

我們在縮放時改變了寬高,但是旋轉(zhuǎn)使用的依然是初始寬高,那么在縮放之后旋轉(zhuǎn),會變回初始大小,而旋轉(zhuǎn)后再縮放,則會導致樣式錯誤。所以,我們在縮放和旋轉(zhuǎn)方法中需要拿到上一次變換過的樣式,再進行新的變換。

const scaleRef = useRef(1);

const changeStyle = (deg: number) => {
  const videoEl: HTMLVideoElement | null = document.querySelector('.sc-vm-file');
  if (!videoEl) return;
  // 獲取窗口的寬度
  const screenWidth = window.innerWidth;
  // 獲取整個屏幕的高度
  const screenFullHeight = screen.height;
  const vWidth = INIT_WIDTH * scaleRef.current;
  const vHeight = INIT_HEIGHT * scaleRef.current;
  // 旋轉(zhuǎn)后樣式
  // ...
  // 水平角度處理
  if (deg === 0 || deg === 180) {
    newWidth = INIT_WIDTH * scaleRef.current;
    // ...
  }
  // ...
};

// 比例縮放
const changeScale = (event: Event) => {
  const radio = event.target as HTMLVmMenuRadioElement;
  scaleRef.current = Number(radio.value);
  setScale(radio.value);
  changeStyle(degRef.current);
};

最終效果展示

三、總結(jié)

實現(xiàn)視頻的旋轉(zhuǎn)縮放功能實際上并不復雜,主要還是通過調(diào)整css樣式做到我們想要的效果。我們在業(yè)務中遇到這種“沒做過,看起來很麻煩”的問題時,始終應該還是抱著“只有你想不到,沒有我做不到”的態(tài)度,只要深入思考一下或者動手嘗試一下,大概就會發(fā)出“哦,原來這么簡單”的感嘆。有道是:山重水復疑無路,柳暗花明又一村。

到此這篇關(guān)于React如何實現(xiàn)視頻旋轉(zhuǎn)縮放的文章就介紹到這了,更多相關(guān)React視頻旋轉(zhuǎn)縮放內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • react 實現(xiàn)圖片正在加載中 加載完成 加載失敗三個階段的原理解析

    react 實現(xiàn)圖片正在加載中 加載完成 加載失敗三個階段的原理解析

    這篇文章主要介紹了react 實現(xiàn)圖片正在加載中 加載完成 加載失敗三個階段的,通過使用loading的圖片來占位,具體原理解析及實現(xiàn)代碼跟隨小編一起通過本文學習吧
    2021-05-05
  • React實現(xiàn)圖片懶加載的常見方式

    React實現(xiàn)圖片懶加載的常見方式

    圖片懶加載是一種優(yōu)化網(wǎng)頁性能的技術(shù),它允許在用戶滾動到圖片位置之前延遲加載圖片,通過懶加載,可以在用戶需要查看圖片時才加載圖片,避免了不必要的圖片加載,本文給大家介紹了React實現(xiàn)圖片懶加載的常見方式,需要的朋友可以參考下
    2024-01-01
  • react封裝Dialog彈框的方法

    react封裝Dialog彈框的方法

    這篇文章主要為大家詳細介紹了react封裝Dialog彈框的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 在?React?中使用?Context?API?實現(xiàn)跨組件通信的方法

    在?React?中使用?Context?API?實現(xiàn)跨組件通信的方法

    在React中,ContextAPI是一個很有用的特性,可用于組件間的狀態(tài)共享,它允許跨組件傳遞數(shù)據(jù)而無需通過每個組件手動傳遞props,本文給大家介紹在?React?中如何使用?Context?API?來實現(xiàn)跨組件的通信,感興趣的朋友一起看看吧
    2024-09-09
  • 如何用react優(yōu)雅的書寫CSS

    如何用react優(yōu)雅的書寫CSS

    這篇文章主要介紹了如何用react優(yōu)雅的書寫CSS,幫助大家更好的理解和學習使用react,感興趣的朋友可以了解下
    2021-04-04
  • React如何使用refresh_token實現(xiàn)無感刷新頁面

    React如何使用refresh_token實現(xiàn)無感刷新頁面

    本文主要介紹了React如何使用refresh_token實現(xiàn)無感刷新頁面,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04
  • React拆分窗格組件的兩種方法

    React拆分窗格組件的兩種方法

    這篇文章主要介紹了React拆分窗格組件的兩種方法,使用第三方庫react-split-pane適用于快速實現(xiàn)拆分窗格功能,并且對功能和樣式的要求較為簡單的場景,本文結(jié)合示例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • React?中使用?RxJS?優(yōu)化數(shù)據(jù)流的處理方案

    React?中使用?RxJS?優(yōu)化數(shù)據(jù)流的處理方案

    這篇文章主要為大家介紹了React?中使用?RxJS?優(yōu)化數(shù)據(jù)流的處理方案示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • Reactjs?+?Nodejs?+?Mongodb?實現(xiàn)文件上傳功能實例詳解

    Reactjs?+?Nodejs?+?Mongodb?實現(xiàn)文件上傳功能實例詳解

    今天是使用?Reactjs?+?Nodejs?+?Mongodb?實現(xiàn)文件上傳功能,前端我們使用?Reactjs?+?Axios?來搭建前端上傳文件應用,后端我們使用?Node.js?+?Express?+?Multer?+?Mongodb?來搭建后端上傳文件處理應用,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2022-06-06
  • react-router-dom的使用說明

    react-router-dom的使用說明

    這篇文章主要介紹了react-router-dom的使用說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03

最新評論