录像时间轴

基于大华、海康摄像头的录像,写一个录像时间轴,要能播放视频时,显示播放进度,要显示哪些区域有视频录像块:

vue组件部分:

<template>
  <div class="timeLine" v-if="timeVideoData !== null"
  	:style="'width:'+ timeLineWidth + 'px;'">
    <canvas :id="`timeline_${timeLineId}`"
      :width="timeLineWidth"
      height="62"
      style="border:1px solid #2b2f33;background-color: #2b2f33;"
      @dragstart="dragStart_">
    </canvas>
  </div>
</template>
<script>
import '@utils/jquery-1.10.2.js'
import '@utils/timeline-canvas1.js'

export default {
  name: '',
  data () {
    return {
      timeLineId_: '',
      style_: '',
      timecell: [
        {
          beginTime: new Date().getTime() - 6 * 3600 * 1000,
          endTime: new Date().getTime() - 4 * 3600 * 1000,
          style: {
            background: 'rgba(132, 244, 180, 0.498039)'
          }
        },
        {
          beginTime: new Date().getTime() - 9 * 3600 * 1000,
          endTime: new Date().getTime() - 5 * 3600 * 1000,
          style: {
            background: 'darkcyan'
          }
        }
      ]
    }
  },

  methods: {
    initTimeLine (data) {
      $('#' + this.timeLineId_).TimeSlider({
        init_cells: data
      })
    },
    dragStart_ () {
      return false
    },
    getAlarmTypeColor (executorName) {
      return this.alarmTypeArr.find((item) => {
        return item.text === executorName
      }).colorType
    }
  },
  props: [
    'timeVideoData',
    'alarmTypeArr',
    'timeLineId',
    'timeLineWidth',
    'backWinName'
  ],
  watch: {
    timeLineWidth (newVal, oldVal) {
      $('#' + this.timeLineId_).TimeSlider('timeLineWidthChange', newVal)
    },
    timeVideoData (newVal, oldVal) {
      if (newVal !== null) {
        this.initTimeLine(newVal.map((it) => {
          return {
            startTime: it.startTime,
            endTime: it.endTime,
            style: {
              background: it.type ? '#13e4bd' : this.getAlarmTypeColor(it.executorName) // type 1 代表设备录像
            },
            type: it.type
          }
        }))
      }
    }
  },

  mounted () {
    // 画布id
    this.timeLineId_ = 'timeline_' + this.timeLineId
    // 进度条对应的 视频窗口
    $('#' + this.timeLineId_).TimeSlider('timeLineOfBackWinName', this.backWinName)
    // 初始化刻度
    this.initTimeLine([])
    // tiemLine 画布宽度
    $('#' + this.timeLineId_).TimeSlider('timeLineWidthChange', this.timeLineWidth)
    // 录像时间段
    this.initTimeLine(this.timeVideoData.map((it) => {
      return {
        startTime: it.startTime,
        endTime: it.endTime,
        style: {
          background: it.type ? '#13e4bd' : this.getAlarmTypeColor(it.executorName) // type 1 代表设备录像
        },
        type: it.type
      }
    }))
    // 录像进度
    this.Bus.$on('sylan_back_prograss', (obj) => {
      if (obj.name === this.backWinName) {
        $('#' + this.timeLineId_).TimeSlider('set_time_to_middle', obj.prograss)
      }
    })
    // 录像下载
    this.Bus.$on('sylanVideoDown', () => {
      $('#' + this.timeLineId_).TimeSlider('sylanVideoDown')
    })
    // 录像停止下载
    this.Bus.$on('sylanVideoStop', () => {
      $('#' + this.timeLineId_).TimeSlider('sylanVideoStop')
    })
  },

  beforeDestroyed () {
    this.Bus.$off('sylan_back_prograss')
    this.Bus.$off('sylanVideoDown')
    this.Bus.$off('sylanVideoStop')
  }
}
</script>

timeline-canvas1.js:

import Bus from '@utils/bus'

var TimeSlider = function (elementId, options) {
  this.canvasId = elementId.substr(elementId.indexOf('_') + 1, elementId.length)
  this.canvas = document.getElementById(elementId)
  this.canvasOffSetLeft = 0
  this.offsetComputed(this.canvas)
  this.ctx = this.canvas.getContext('2d')
  this.canvansW = this.canvas.width
  this.canVansH = this.canvas.height
  this.timecell = options.init_cells

  this.h_per_step = 72 // 时间轴显示24小时
  this.m_per_step = 12 // 时间轴显示24小时

  this.minTime_ = this.canvas.width // 最小时间刻(长度)
  this.maxTime_ = 0 // 最大时间刻(长度)

  this.g_isMousedown = false // 拖动mousedown标记
  this.g_isMouseRightdown = false // 右键拖动mousedown标记
  this.g_isMousemove = false // 拖动mousemove标记
  this.g_isMouseRightmove = false // 拖动mouseRightmove标记
  this.g_isMouseEntermove = false // 在时间段上移动

  this.winBackName = null
  this.moveEnterTime = null
  this.g_mouseenterCursor = null
  this.g_mousedownCursor = null // 拖动mousedown的位置
  this.g_mousedownRightCursor = null
  this.g_mousedownRightOffset = null

  this.ifDoubleDown = null // 防止多次点击 
  this.ifDoubleUp = null

  this.on_before_click_ruler_callback = null

  this.add_events()

  return this
}

/**
 * 初始化
 * @param {*} timecell 录像段数组
 * @param {*} redrawFlag 是否重绘标记
 */
TimeSlider.prototype.init = function (timecell, redrawFlag) {
  this.minTime_ = this.canvas.width // 最小时间刻(长度)
  this.maxTime_ = 0 // 最大时间刻(长度)

  this.timecell = timecell
  this.add_graduations()
  this.drawCellBg()
  this.drawLine(0, this.canVansH, this.canvansW, this.canVansH, 'rgb(151, 158, 167)', 1) // 底线
  this.add_cells(timecell)
  if (!redrawFlag) { // 只有第一次进入才需要添加事件
    this.add_events()
  }
  // 右键选择下载区域
  if (this.g_mousedownRightCursor && this.g_mousedownRightOffset) {
    this.ctx.fillStyle = 'rgba(135, 216, 152, 0.4)'
    this.ctx.fillRect(this.g_mousedownRightCursor, 0, this.g_mousedownRightOffset, this.canVansH)
  }
}

// 计算dom的offset
TimeSlider.prototype.offsetComputed = function (domObj) {
  let _this = this
  if (domObj.offsetParent !== null) {
    _this.canvasOffSetLeft += domObj.offsetLeft - 0
    _this.offsetComputed(domObj.offsetParent)
  } else {
    return false
  }
}
/**
 * 绘制添加刻度
 */
TimeSlider.prototype.add_graduations = function () {
  var _this = this
  // ************ 绘制小时刻度
  // _this.h_per_step = (_this.canvansW - 2) / 24 // 小时
  for (var i = 0; i < 24; i++) {
    _this.drawLine(i * _this.h_per_step + 1, 0, i * _this.h_per_step + 1, 25, 'rgba(151,158,167,1)', 1)
    _this.ctx.fillText(i + ':00', i * _this.h_per_step - 10, 35)
    _this.ctx.fillStyle = 'rgba(151, 158, 167, 1)'
  }

  // _this.m_per_step = (_this.canvansW - 2) / 144 // 10分钟一次
  for (var i = 0; i < 144; i++) {
    if (i % 6) {
      _this.drawLine(i * _this.m_per_step + 1, 0, i * _this.m_per_step + 1, 20, 'rgba(151,158,167,1)', 1)
    }
    _this.ctx.fillStyle = 'rgba(151,158,167,1)'
  }
  if (this.m_per_step > 12) {
    for (var i = 0; i < 144; i++) {
      _this.ctx.fillText(parseInt(i / 6) + ':' + (i % 6) * 10, i * _this.m_per_step - 10, 35)
      _this.ctx.fillStyle = 'rgba(151,158,167,1)'
    }
  }
  if (this.m_per_step > 60) {
    for (var i = 0; i < 1440; i++) {
      _this.drawLine(i * _this.m_per_step / 10 + 1, 0, i * _this.m_per_step / 10 + 1, 20, 'rgba(151,158,167,1)', 1)
      _this.ctx.fillStyle = 'rgba(151,158,167,1)'
    }
  }
}

/**
 * 绘制线
 * @param {*} beginX
 * @param {*} beginY
 * @param {*} endX
 * @param {*} endY
 * @param {*} color
 * @param {*} width
 */
TimeSlider.prototype.drawLine = function (beginX, beginY, endX, endY, color, width) {
  this.ctx.beginPath()
  this.ctx.moveTo(beginX, beginY)
  this.ctx.lineTo(endX, endY)
  this.ctx.strokeStyle = color
  this.ctx.lineWidth = width
  this.ctx.stroke()
}

/**
 * 添加录像段
 * @param {*} cells 录像数组
 */
TimeSlider.prototype.add_cells = function (cells) {
  var _this = this
  cells.forEach(cell => {
    _this.draw_cell(cell)
  })
}

/**
 * 绘制录像块
 * @param {*} cell cell包括beginTime ms;endTime ms;style
 */
TimeSlider.prototype.draw_cell = function (cell) {
  let end_
  // 如果是00 :00 :00
  if (cell.endTime.substr(8, 2) === '00' && cell.endTime.substr(10, 2) === '00' && cell.endTime.substr(12, 2) === '00') {
    end_ = this.canvansW - 2
  } else {
    end_ = (cell.endTime.substr(8, 2) - 0) + (cell.endTime.substr(10, 2) * 60 + (cell.endTime.substr(12, 2) - 0)) / 3600
  }
  var _this = this
  let begin_ = (cell.startTime.substr(8, 2) - 0) + (cell.startTime.substr(10, 2) * 60 + (cell.startTime.substr(12, 2) - 0)) / 3600
  var beginX = _this.h_per_step * begin_ + 1
  var endX = _this.h_per_step * end_ + 1
  var cell_width = _this.h_per_step * (end_ - begin_) + 1
  // 取到最大的时间(长度)
  _this.maxTime_ = endX > _this.maxTime_ ? endX : _this.maxTime_
  // 判断是设备录像 (如果设备录像清掉了,之前的告警录像不可选中)
  if (cell.type) {
    // 取到最小的时间(长度)
    _this.minTime_ = beginX < _this.minTime_ ? beginX : _this.minTime_
  }
  _this.ctx.fillStyle = cell.style.background
  _this.ctx.fillRect(beginX, 0, cell_width, 15)
}

/**
 * 绘制录像块背景
 */
TimeSlider.prototype.drawCellBg = function () {
  this.ctx.fillStyle = 'rgba(69, 72, 76, 0.5)'
  this.ctx.fillRect(0, 0, this.canvansW, 15)
}

/**
 * 时间轴事件
 */
TimeSlider.prototype.add_events = function () {
  var _this = this
  if (_this.canvas.addEventListener) {
    // _this.canvas.addEventListener('mousewheel', _this.mousewheelFunc.bind(_this))
    _this.canvas.addEventListener('mousedown', _this.mousedownFunc.bind(_this))
    _this.canvas.addEventListener('mousemove', _this.mousemoveFunc.bind(_this))
    _this.canvas.addEventListener('mouseup', _this.mouseupFunc.bind(_this))
    _this.canvas.addEventListener('mouseout', _this.mouseoutFunc.bind(_this))
  }
}

TimeSlider.prototype.remove_events = function () {
  var _this = this
  if (_this.canvas.addEventListener) {
    _this.canvas.removeEventListener('mousedown', _this.mousedownFunc.bind(_this), false)
    _this.canvas.removeEventListener('mousemove', _this.mousemoveFunc.bind(_this), false)
    _this.canvas.removeEventListener('mouseup', _this.mouseupFunc.bind(_this), false)
    _this.canvas.removeEventListener('mouseout', _this.mouseoutFunc.bind(_this), false)
  }
}

/**
 * 拖动/点击 mousedown事件
 */
TimeSlider.prototype.mousedownFunc = function (e) {
  let _this = this
  if (this.timecell.length !== 0) {
    if (this.ifDoubleDown) {
      clearTimeout(this.ifDoubleDown)
    }
    this.ifDoubleDown = setTimeout(() => {
      // 确保点击 在时间条范围之内
      let downPosition = this.get_cursor_x_position(e) - this.canvasOffSetLeft
      if (downPosition >= this.minTime_ && downPosition <= this.maxTime_) {
        if (e.which === 1) {
          this.g_isMousedown = true
          this.ctx.clearRect(0, 0, this.canvansW, this.canVansH)
          this.init(this.timecell, true)
          this.g_mousedownCursor = this.get_cursor_x_position(e) - this.canvasOffSetLeft // 记住mousedown的位置
          Bus.$emit('videoTimeOfSelect', {
            id: this.canvasId,
            time: this.computedTime(_this.g_mousedownCursor)
          })
          this.drawLine(this.g_mousedownCursor, 0, this.g_mousedownCursor, 33, 'rgb(64, 196, 255', 2)
        } else if (e.which === 3) {
          this.g_mousedownRightCursor = downPosition // 记住mousedown的位置
          this.g_isMouseRightdown = true
        }
      }
    }, 300)
  }
}

/**
 * 拖动/鼠标hover显示 mousemove事件
 */
TimeSlider.prototype.mousemoveFunc = function (e) {
  if (this.g_isMousedown) {
    this.g_isMousemove = true
    this.ctx.clearRect(0, 0, this.canvansW, this.canVansH)
    this.init(this.timecell, true)
    this.g_mousedownCursor = this.get_cursor_x_position(e) - this.canvasOffSetLeft // 记住mousedown的位置
    this.drawLine(this.g_mousedownCursor, 0, this.g_mousedownCursor, 33, 'rgb(64, 196, 255', 1) // 中间播放点时间线
  } else if (this.g_isMouseRightdown) {
    this.g_isMouseRightmove = true
  } else {
    this.g_isMouseEntermove = true
    // if (this.timecell.length !== 0) {
    this.ctx.clearRect(0, 0, this.canvansW, this.canVansH)
    this.init(this.timecell, true)
    this.g_mouseenterCursor = this.get_cursor_x_position(e) - this.canvasOffSetLeft // 记住mousedown的位置
    this.moveEnterTime = this.computedTime(this.get_cursor_x_position(e) - this.canvasOffSetLeft)

    // 画当前鼠标移上去显示的时间刻 时间指针
    this.tip_of_time()
    // 当前如果正在播放视频
    if (this.currentPlayTime) {
      this.draw_middle_time(this.currentPlayTimePointer, this.currentPlayTime)
    }
    // }
  }
}
/**
 * 计算时间
 */
TimeSlider.prototype.computedTime = function (w) {
  this.h_ = parseInt(w / this.h_per_step)
  this.allSecond = Math.floor(((w % this.h_per_step) * 3600) / this.h_per_step)
  this.m_ = parseInt(this.allSecond / 60)
  this.s_ = this.allSecond % 60
  return (this.h_ < 10 ? '0' + this.h_ : JSON.stringify(this.h_)) + (this.m_ < 10 ? '0' + this.m_ : JSON.stringify(this.m_)) + (this.s_ < 10 ? '0' + this.s_ : JSON.stringify(this.s_))
}
/**
 * 拖动/点击 mouseup事件
 */
TimeSlider.prototype.mouseupFunc = function (e) {
  if (this.timecell.length !== 0) {
    setTimeout(() => {
      var _this = this
      // 确保up 在时间条范围之内
      let upPosition = this.get_cursor_x_position(e) - this.canvasOffSetLeft
      if (upPosition >= this.minTime_ && upPosition <= this.maxTime_) {
        if (_this.g_isMousemove) { // 拖动 事件
        } else if (this.g_isMouseRightmove) {
          // 获取下载区间
          _this.g_mousedownRightOffset = (_this.get_cursor_x_position(e) - _this.canvasOffSetLeft) - _this.g_mousedownRightCursor
          // 渲染
          _this.ctx.fillStyle = 'rgba(135, 216, 152, 0.4)'
          _this.ctx.fillRect(_this.g_mousedownRightCursor, 0, Math.abs(_this.g_mousedownRightOffset), _this.canVansH)
          // 下载
          Bus.$emit('videoTimeDownSelect', {
            id: this.canvasId,
            // 判断往左拉还是往右拉
            startTime: _this.g_mousedownRightOffset > 0 ? _this.computedTime(_this.g_mousedownRightCursor) : _this.computedTime(_this.get_cursor_x_position(e) - _this.canvasOffSetLeft),
            endTime: _this.g_mousedownRightOffset > 0 ? _this.computedTime(_this.get_cursor_x_position(e) - _this.canvasOffSetLeft) : _this.computedTime(_this.g_mousedownRightCursor),
            // 判断是否大于0.5h
            times_: (Math.abs(this.g_mousedownRightOffset) / this.h_per_step) > 0.5
          })
        } else { // up 事件
          // if (this.ifDoubleUp) {
          //   clearTimeout(this.ifDoubleUp)
          // }
          // this.ifDoubleUp = setTimeout(() => {
          //   Bus.$emit('videoTimeOfSelect', {
          //     id: this.canvasId,
          //     time: this.computedTime(_this.g_mousedownCursor)
          //   })
          // }, 300)
        }
      }
      _this.g_isMousemove = false
      _this.g_isMousedown = false
      _this.g_isMouseRightmove = false
      _this.g_isMouseRightdown = false
    }, 400) // 延时器等到down事件处理完毕
  }
}

/**
 * 鼠标移出隐藏时间 mouseout事件
 * @param {*} e
 */
TimeSlider.prototype.mouseoutFunc = function (event) {

  this.g_isMousedown = false
  this.g_isMousemove = false

  this.g_isMouseEntermove = false
  this.g_isMouseRightdown = false
  this.g_isMouseRightmove = false

  this.ctx.clearRect(0, 0, this.canvansW, this.canVansH)
  this.init(this.timecell, true)
  // 当前如果正在播放视频
  if (this.currentPlayTime) {
    this.draw_middle_time(this.currentPlayTimePointer, this.currentPlayTime)
  }
}

/**
 * 滚轮放大缩小,以时间轴中心为准 mousewheel事件
 */
TimeSlider.prototype.mousewheelFunc = function (event) {
  this.clearCanvas()
  this.init(this.timecell, true)
}

/**
 *  画布宽度
 *  @param {*} time
 */
TimeSlider.prototype.timeLineWidthChange = function (val) {
  this.clearCanvas()
  this.canvansW = val

  this.h_per_step = val / 24 // 时间轴显示24小时
  this.m_per_step = val / 24 / 6 // 时间轴显示24小时
  setTimeout(() => {
    this.init(this.timecell, true)
  }, 0)
}

/**
 *  画布对应的视频窗口
 *  @param {*} time
 */
TimeSlider.prototype.timeLineOfBackWinName = function (winBackName) {
  this.winBackName = winBackName
}

/**
 * 播放,指针移动
 *  @param {*} time 单位ms
 */
TimeSlider.prototype.set_time_to_middle = function (time_, time) {
  this.clearCanvas()
  this.init(this.timecell, true)
  // 画当前播放时间刻 时间指针
  this.draw_middle_time(time_, time)
  if (this.g_isMouseEntermove) {
    // 画当前鼠标移上去显示的时间刻 时间指针
    this.tip_of_time()
  }
}

/**
 * 画当前播放时间刻 时间指针
 *  @param {*} time 单位ms
 */
TimeSlider.prototype.draw_middle_time = function (time_, time) {
  // 画指针
  this.drawLine(time_ * this.h_per_step, 0, time_ * this.h_per_step, 33, 'rgb(64, 196, 255', 2) // 中间播放点时间线
  // 画时间
  this.ctx.fillStyle = 'rgb(194, 202, 215)'
  this.ctx.fillText(time.substr(8, 2) + ':' + time.substr(10, 2) + ':' + time.substr(12, 2), time_ * this.h_per_step - 15, 55)
}

/**
 * 画当前鼠标移上去显示的时间刻 时间指针
 *  @param {*} time 单位ms
 */
TimeSlider.prototype.tip_of_time = function () {
  // 展示覆盖的时间线
  this.drawLine(this.g_mouseenterCursor, 0, this.g_mouseenterCursor, 33, 'rgb(255, 255, 255', 1)
  this.ctx.fillStyle = 'rgb(194, 202, 215)'
  // 展示覆盖的时间
  this.ctx.fillText(this.moveEnterTime.substr(0, 2) + ':' + this.moveEnterTime.substr(2, 2) + ':' + this.moveEnterTime.substr(4, 2), this.g_mouseenterCursor - 15, 55)
}

/**
 * 清空下载区域
 */
TimeSlider.prototype.sylanVideoDown = function () {
  this.g_mousedownRightCursor = null
  this.g_mousedownRightOffset = null
  this.ctx.clearRect(0, 0, this.canvansW, this.canVansH)
  this.init(this.timecell, true)
  // this.drawLine(this.g_mousedownCursor, 0, this.g_mousedownCursor, 33, 'rgb(64, 196, 255', 2)
}

/**
 * 清空光标
 */
TimeSlider.prototype.sylanVideoStop = function () {
  this.ctx.clearRect(0, 0, this.canvansW, this.canVansH)
  this.init(this.timecell, true)
}

/**
 * 获取鼠标posx
 * @param {*} e
 */
TimeSlider.prototype.get_cursor_x_position = function (e) {
  var posx = 0

  if (!e) {
    e = window.event
  }

  if (e.pageX || e.pageY) {
    posx = e.pageX
  } else if (e.clientX || e.clientY) {
    posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft
  }

  return posx + document.getElementsByClassName('timeLine_')[0].scrollLeft
}

/**
 * 返回时间轴上刻度的时间
 * @param {*} datetime new Date 格式
 */
TimeSlider.prototype.graduation_title = function (datetime) {
  if (datetime.getHours() == 0 && datetime.getMinutes() == 0 && datetime.getMilliseconds() == 0) {
    return ('0' + datetime.getDate().toString()).substr(-2) + '.' +
      ('0' + (datetime.getMonth() + 1).toString()).substr(-2) + '.' +
      datetime.getFullYear()
  }
  return datetime.getHours() + ':' + ('0' + datetime.getMinutes().toString()).substr(-2)
}

/**
 * 清除canvas 每次重新绘制需要先清除
 */
TimeSlider.prototype.clearCanvas = function () {
  this.ctx.clearRect(0, 0, this.canvansW, this.canVansH)
}

/**
 * 插件设置
 * @param {*} options
 */
function Plugin(options, val, cell, callback) {
  return this.each(function () {
    var _this = $(this)
    var _thisId = this.id
    var data = _this.data('timeslider')
    if (!data) {
      _this.data('timeslider', new TimeSlider(_thisId, options))
    } else {
      if (typeof options === 'string') {
        switch (options) {
          case 'timeLineWidthChange':
            data.timeLineWidthChange(val)
            break
          case 'timeLineOfBackWinName':
            data.timeLineOfBackWinName(val)
            break
          case 'set_time_to_middle':
            data.currentPlayTimePointer = (val.substr(8, 2) - 0) + val.substr(10, 2) / 60 + val.substr(12, 2) / 3600
            data.currentPlayTime = val
            data.set_time_to_middle(data.currentPlayTimePointer, val)
            break
          case 'sylanVideoDown':
            data.sylanVideoDown()
            break
          case 'sylanVideoStop':
            data.sylanVideoStop()
            break
          case 'init':
            break
        }
      } else {
        data.clearCanvas()
        data.init(options.init_cells, true)
      }
    }
  })
}

var old = $.fn.TimeSlider

$.fn.TimeSlider = Plugin

$.fn.TimeSlider.noConflict = function () {
  $.fn.TimeSlider = old
  return this
}

写的时间有点久远,参考了网上一些资料,改进成需求需要的功能。写的不好的地方多多包涵~

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值