這次來寫一個『視頻字幕截取器』

想必大家應該都見過類似這種字幕往下邊疊的視頻截圖

共產黨宣言

一般的做法都是自己把視頻某幾幀截圖,然後用作圖工具裁剪/覆蓋之類的方式來製作出來。但是作爲一個程序員,當然要思考一下效率更高的方式啊,於是就有了這篇博客。

功能目標:

直接本地選擇視頻,自定義字幕截取位置,自動添加字幕到第一幀的下方。

最終的頁面佈局如下:

技術要點:

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

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