视频转gif需求全流程分析✨

1. 背景

基本需求是这样的,在一个固定宽高的视频播放器中截取一块区域,选择视频的开始和结束位置,设置帧率、速度、预览等信息转成GIF。

2.分析

首先我们先确定是前端转gif还是后端转,然后具体问题具体分析,我们采用的是后端转gif,需要传给后端视频源、截取的开始,结束时间、缩放后的x,y值、截取框的宽高、帧率、速度等信息。

分析难点!

  1. 传参最复杂的就是计算比例,视频源可能是1080也可能720等等视频原始尺寸,放到视频播放器中会按照长或宽等比缩放。宽高比不同的话,如果是竖屏视频会有左右黑边,横屏会有上下黑边。所以要算出蒙版层mask的缩放比例,才能拿到真实的x,y值
  2. 视频截取框需要能拖拽,截选区域为透明色,四周为黑灰色,可截选区域为去除左右黑边或者上下黑边后的真实播放区域

设计效果比如这是左右黑边,还有横屏的上线黑边 image.png

3.实现

难点一,通过分析宽高比,播放器宽度/视频宽度 = 缩放比,用这个缩放比乘原视频高度,如果小于播放器高度则是上下留有黑边,反之则是左右有黑边,等比的话就是刚好与播放器比例一致。算出的缩放比例ratio,再把这个比例用于蒙版层mask。

3.1难点一 核心代码

<my-video-player>...播放器代码省略</my-video-player>
<div class="mask">
  ...拖拽代码
</div>
// 860 490是播放器宽高
let l = document.getElementsByClassName('mask')[0] // 蒙版层,放在视频播放器上
let ratio = 860 / this.originalWidth  // originalWidth 原视频宽度  originalHeight 原视频高度
l.style.transformOrigin = '0 0'
if (ratio * this.originalHeight < 490) { // 上下黑边
    this.radio = 860 / this.originalWidth
} else if (ratio * this.originalHeight > 490) { // 上下左右
    this.radio = 490 / this.originalHeight
} else { // 等比
    this.radio = 490 / this.originalHeight
}
l.style.transform = `scale(${this.radio})` // 缩放比

3.1难点二 核心代码

这里用了vue-draggable-resizable包来实现可拖拽框,在难点一中我们拿到了radio,接下来计算拖拽框的可移动区域,以及初始值

let initXY = this.calculateCanvasImageParam(this.originalWidth, this.originalHeight)
calculateCanvasImageParam(videoWidth, videoHeight) {
  let height = 490
  let width = 860
  if (videoWidth === 0 || videoHeight === 0) {
    return {
      x: 0,
      y: 0,
      width: width,
      height: height
    }
  }
  // 计算比例
  if (videoHeight / videoWidth > (height / width)) {
    const realW = height * videoWidth / videoHeight
    return {
      x: (width - realW) / 2,
      y: 0,
      width: realW,
      height: height
    }
  } else {
    const realH = width * videoHeight / videoWidth
    return {
      x: 0,
      y: (height - realH) / 2,
      width: width,
      height: realH
    }
  }
}

这样initXY这个对象里的,x、y就是传入vue-draggable-resizable组件的初始x、y值

<div class="mask">
  <vue-draggable-resizable
      :w="width"
      :h="height"
      :x="x"
      :y="y"
      @dragging="onDrag"
      @resizing="onResize"
      :parent="true"
      class="innerCheck"
      :scale='radio'>
  </vue-draggable-resizable>
</div>

接下来就是截图效果的实现,这里我们用了css属性mix-blend-mode,他是用来控制元素的混合模式,可以混合叠加产生不一样的效果,我们先来看看兼容性 image.png 还不错,没多少用户还在用IE吧,贴代码:

.mask {
  position: absolute;
  opacity: .9;
  top: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.5);
  mix-blend-mode: hard-light;
  pointer-events: auto;

  .innerCheck {
    border: 3px solid #FC4F08;
    //top: 50px;
    //left: 50px;
    background-color: gray;

    .dingwei {
      ...
    }

    .dingwei2 {
      ...
    }

    .dingwei3 {
      ...
    }

    .dingwei4 {
      ...
    }
  }
}

之前项目离职不在上家公司环境打不开了,实现的效果类似这样:

image.png 四周黑灰色,中间透明可拖拽

鄙人文笔有限,写的代码也许还不够优雅,见谅啦

全部评论

相关推荐

有趣的牛油果开挂了:最近这个阶段收到些杂七杂八的短信是真的烦
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务