这次来写一个『视频字幕截取器』

想必大家应该都见过类似这种字幕往下边叠的视频截图

共产党宣言

一般的做法都是自己把视频某几帧截图,然后用作图工具裁剪/覆盖之类的方式来制作出来。但是作为一个程序员,当然要思考一下效率更高的方式啊,于是就有了这篇博客。

功能目标:

直接本地选择视频,自定义字幕截取位置,自动添加字幕到第一帧的下方。

最终的页面布局如下:

技术要点:

1、通过 bolbURL 播放本地视频;

2、通过 createImageBitmap 截取视频某部分

实现:

为了dom操作的方便,我这里就直接用vue来做了。

// 16进制字符串转rgb
function hex2rgb (hexStr) {
  const value = parseInt(hexStr.replace('#', ''), 16)
  const r = value >> 16 // 获取17~24位
  const g = value >> 8 & 0xff // 获取9~16位
  const b = value & 0xff // 获取0~8位
  return { r, g, b }
}

let $canvas
let $video
let ctx
let drawTimes = -1
let fullHeight = 0
const offscreenCvs = new OffscreenCanvas(0, 0)
const offscreenCtx = offscreenCvs.getContext('2d')

window.$vm = new Vue({
  el: '#app',
  data: function () {
    return {
      controls: true,
      video: {
        width: 200,
        height: 200,
      },
      videoHeight: 0,
      ccForceShow: true,
      ccShow: false,
      ccShowTimer: -1,
      exportScale: 1,
      cc: {
        left: 0,
        top: 0,
        width: 0,
        height: 0,
        color: '#009688',
      },
      fileName: '选择文件'
    }
  },
  computed: {
    canvasStyle () {
      return {
        width: `${this.video.width * this.exportScale}px`,
        height: 'auto',
      }
    },
    videoStyle () {
      return {
        width: `${this.video.width * this.exportScale}px`,
        height: `${this.video.height * this.exportScale}px`,
      }
    },
    ccStyle () {
      return {
        left: `${this.cc.left * this.exportScale}px`,
        top: `${this.cc.top * this.exportScale}px`,
        width: `${this.cc.width * this.exportScale}px`,
        height: `${this.cc.height * this.exportScale}px`,
        backgroundColor: `${this.ccColor}`,
      }
    },
    ccColor () {
      const rgb = hex2rgb(this.cc.color)
      return `rgba(${rgb.r},${rgb.g},${rgb.b},0.5)`
    },
  },
  mounted () {
    $video = this.$refs.video
    $canvas = this.$refs.canvas
    ctx = $canvas.getContext('2d')
  },
  methods: {
    onFileChange (e) {
      const $fileVideo = this.$refs.fileVideo
      const file = $fileVideo.files[0]
      this.fileName = file.name
      $video.src = URL.createObjectURL(file)
    },
    onCcChange (e) {
      this.ccShow = true
      clearTimeout(this.ccShowTimer)
      this.ccShowTimer = setTimeout(() => {
        this.ccShow = false
      }, 1000)

    },
    onVideoMetaLoad () {
      const $video = this.$refs.video
      const videoWidth = Math.floor($video.videoWidth)
      const videoHeight = Math.floor($video.videoHeight)
      this.video = {
        width: videoWidth,
        height: videoHeight,
      }
      const ccHeight = 50
      const ccLeft = 0
      const ccWidth = Math.floor(videoWidth)
      const ccTop = videoHeight - ccHeight - 15
      $canvas.width = videoWidth
      $canvas.height = videoHeight
      this.cc = {
        width: ccWidth,
        height: ccHeight,
        left: ccLeft,
        top: ccTop,
        color: this.cc.color,
      }
      drawTimes = 0
      fullHeight = videoHeight
      this.exportScale = 1
    },
    drawVideo () {
      const video = this.video
      const cc = {
        left: Math.floor(this.cc.left),
        top: Math.floor(this.cc.top),
        width: Math.floor(this.cc.width),
        height: Math.floor(this.cc.height),
      }
      if (drawTimes === -1) {
        return alert('请先导入视频')
      }
      if (drawTimes === 0) {
        ctx.drawImage($video, 0, 0, video.width, video.height)
        drawTimes++
      }
      else {
        const newHeight = fullHeight + cc.height
        offscreenCvs.width = video.width
        offscreenCvs.height = fullHeight
        offscreenCtx.drawImage($canvas, 0, 0, video.width, fullHeight)
        $canvas.height = newHeight
        ctx.drawImage(offscreenCvs, 0, 0, video.width, fullHeight)
        createImageBitmap($video, cc.left, cc.top, cc.width, cc.height)
          .then(res => {
            ctx.drawImage(res, cc.left, fullHeight, cc.width, cc.height)
            fullHeight = newHeight
            drawTimes++
          })
      }
    },
  }
})

 完整代码戳这里

在线演示1在线演示2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章