截取视频帧

方式:多媒体 + 画布 video+canvas

  1. function captureVideo() {
  2. const video:any = document.getElementById('video');
  3. const canvas = document.createElement('canvas');
  4. const ctx:any = canvas.getContext('2d');
  5. ctx.drawImage(video, 0, 0, video.clientHeight, video.clientWidth);
  6. // 获取 base64
  7. const dataUrl = canvas.toDataURL('image/png');
  8. // 处理成File对象
  9. const file = dataUrlToFile(dataUrl, '封面图.png');
  10. file.then((res) => uploadFile(res));
  11. }
  12. // 上传文件对象到服务器
  13. const uploadFile = async (file) => {
  14. try {
  15. // 创建FormData对象
  16. const form = new FormData();
  17. // 文件对象
  18. form.append('fileData', file);
  19. const data = await request(‘xxxx.json’, {
  20. method: 'post',
  21. name: "Filedata",
  22. data: form,
  23. });
  24. console.log(data);
  25. } catch (error) {
  26. message.error(error.message || '上传失败,请重试');
  27. }
  28. }
  29. // base64转成File对象
  30. export async function dataUrlToFile(dataUrl: string, fileName: string): Promise {
  31. const res: Response = await fetch(dataUrl);
  32. const blob: Blob = await res.blob();
  33. return new File([blob], fileName, { type: 'image/png' });
  34. }

canvas.toDataURL()报错

Uncaught DOMException Failed to execute 'toDataURL' on 'HTMLCanvasElement Tainted canvases may not be exported_副本.png
原因:受限于 CORS 策略,会存在跨域问题,虽然可以使用图像(比如append到页面上)但是绘制到画布上会污染画布,一旦一个画布被污染,就无法提取画布的数据,比如无法使用使用画布toBlob(),toDataURL(),或getImageData()方法;当使用这些方法的时候 会抛出一个上面的安全错误

解决方式:给img或者video添加属性 crossOrigin:’anonymous’
img.setAttribute(‘crossOrigin’, ‘anonymous’);
Or
video.setAttribute(‘crossOrigin’, ‘anonymous’);

preload = ‘auto’

最好给video元素加上preload属性

视频裁剪

除了截取视频帧之外,还需根据特定比例对视频的进行裁剪

功能点:
1、裁剪实现
2、裁剪后预览

裁剪比例:
3 / 4 = 0.75

裁剪工具选择

react-rnd:https://github.com/bokuweb/react-rnd

在使用该工具过程中遇到了一个问题:如果使用缩放功能,边界控制会有问题(如下图);固定选择框宽高则无此问题,使用时需注意
3A4C4170-CFA5-457D-8457-503BE054F15B.png

裁剪后预览

通过reactRnd可以很方便的拿到裁剪参数,然后要根据这些参数,将裁剪后的区域在页面上展示

处理方案:CSS -> transform

使用transform后,裁剪区域的展示不成问题,但会有另一个问题出现:
多媒体播放器控制按钮的位置会随着裁剪区域变化而变化
目标:不管裁剪区域怎么变化,控制按钮位置不变,这就需要对控制按钮样式进行处理

  1. /* step1:给父元素设置overflow:hidden样式,隐藏video超出区域 */
  2. /* 父元素样式 */
  3. .videoBox {
  4. width: 256px;
  5. display: flex;
  6. justify-content: center;
  7. align-items: center;
  8. overflow: hidden;
  9. :global {
  10. video {
  11. width: 100%;
  12. max-height: 341px;
  13. }
  14. }
  15. }
  1. // halfScreenVideo: 获取多媒体播放器
  2. const halfScreenVideo = document.querySelector('.half_screen');
  3. // startY: 裁剪区y坐标
  4. const { startY } = videoCutInfo;
  5. /**
  6. * 1.33: 缩放比 1 / 0.75
  7. * 341: 预览高度,按宽度计算得到的值 256 / 0.75
  8. * 35: 控制播放控制器位置
  9. */
  10. // step2:改变转换原点 -> top
  11. halfScreenVideo.style.transformOrigin = 'top';
  12. // step3:根据比例先放大,根据裁剪区y坐标调整位置
  13. halfScreenVideo.style.transform = `
  14. translateY(${-(341 * startY * 1.33)}px) scale(1.33)`;
  15. // step4:每次插入都会保留上一次插入的,因此需要先删除上一次插入的,保证只有一条样式规则
  16. document.styleSheets[0].cssRules.length && document.styleSheets[0].deleteRule(0);
  17. // step5:缩放到正常大小,设置多媒体控制器位置始终不变
  18. document.styleSheets[0].insertRule(
  19. `.half_screen::-webkit-media-controls{transform: translateY(
  20. ${341 * startY - 35}px) scale(0.75)}`,
  21. 0,
  22. );