视频播放时间轴

import moment from "moment";

export default class timeAxis {

  constructor(options) {

    if (!options.dom) {

      return "";

    }

    for (let option in options) {

      if (options.hasOwnProperty(option)) {

        this[option] = options[option];

      }

    }

    if (this.timecell && this.timecell.length) {

      this.currentTimestamp = this.timecell[0].begin;

    }

    if (options.cellStyle && options.cellStyle.background) {

      this.cellStyle = options.cellStyle;

    } else {

      this.cellStyle = {

        background: "rgba(24,208,217,0.5)",

        borderBottomColor: "yellow", // 添加这一行

        borderBottomWidth: 10, // 添加这一行

      };

    }

    this.isClick = false;

    this.button = 0; // 0:左键, 2:右键

    this.isPressDown = false; // 是否按下

    this.isDragPointer = false;

    this.currentChecked = "";

    this.zoomHours = 4;

    this.graduationMinStep = 10;

    this.minutesPerStep = [

      1, 2, 5, 10, 15, 20, 30, 60, 120, 180, 240, 360, 720, 1440,

    ];

    this.pointerOptions = {

      beginX: 0,

      beginY: 0,

      endX: 0,

      endY: 30,

      color: "rgb(255, 255, 255)",

      width: 2,

    };

    this.initCanvas();

    this.initCanvasCtx();

  }

  initCanvas() {

    this.canvas = document.createElement("canvas");

    this.canvas.width = this.dom.clientWidth;

    this.canvas.height = this.dom.clientHeight;

    this.canvas.style.backgroundColor = this.bg;

    this.canvasWidth = this.canvas.width;

    this.canvasHeight = this.canvas.height;

    this.dom.appendChild(this.canvas);

    this.canvasCtx = this.canvas.getContext("2d");

    this.canvasGraduationsLinesCtx = this.canvas.getContext("2d");

    this.canvasPlayPointerCtx = this.canvas.getContext("2d");

    this.initEvents();

  }

  resize() {

    this.canvas.width = this.dom.clientWidth;

    this.canvas.height = this.height;

    this.canvas.style.backgroundColor = this.bg;

    this.canvasWidth = this.canvas.width;

    this.canvasHeight = this.canvas.height;

    this.clearCanvas();

    this.initCanvasCtx();

  }

  initCanvasCtx() {

    this.pxPerMs = this.canvasWidth / (this.zoomHours * 60 * 60 * 1000);

    this.initBorders();

    this.initTimeCells(this.timecell || []);

    // 渲染时间轴

    this.initGraduationsLines(this.date);

    if (this.isClick) {

      // 渲染播放指针

      this.initPlayPointerCtx();

    }

  }

  // 初始化边框

  initBorders() {

    let borderColor = "rgb(151, 158, 167)";

    //顶线

    this.drawLine({

      beginX: 0,

      beginY: 0,

      endX: this.canvasWidth,

      endY: 0,

      color: borderColor,

      width: 1,

    });

    //底线

    this.drawLine({

      beginX: 0,

      beginY: this.canvasHeight,

      endX: this.canvasWidth,

      endY: this.canvasHeight,

      color: borderColor,

      width: 1,

    });

  }

  // 画线

  drawLine({ beginX, beginY, endX, endY, color, width }) {

    this.canvasCtx.beginPath();

    this.canvasCtx.moveTo(beginX, beginY);

    this.canvasCtx.lineTo(endX, endY);

    this.canvasCtx.strokeStyle = color;

    this.canvasCtx.lineWidth = width;

    this.canvasCtx.stroke();

  }

  // 有效时间区域

  initTimeCells(datas) {

    // 分段填充时间段

    datas.forEach((cell, index) => {

      this.drawCell(cell, index);

    });

    this.canvasCtx.font = "normal normal 12px Arial,sans-serif";

    this.canvasCtx.fillStyle = "#fff";

  }

  // 填充时间段

  drawCell(data, index) {

    let fillColor = this.cellStyle.background;

    let borderBottomColor = this.cellStyle.borderBottomColor; // 获取边框颜色

    let borderBottomWidth = this.cellStyle.borderBottomWidth || 2; // 获取边框宽度

    if (data.style && data.style.background) {

      fillColor = data.style.background;

    }

    let cellBeginX = this.pxPerMs * (data.begin - this.date);

    let cellWidth = (data.end - data.begin) * this.pxPerMs;

    this.canvasCtx.fillStyle = fillColor;

    this.canvasCtx.font = "normal normal 12px Arial,sans-serif";

    this.canvasCtx.fillRect(cellBeginX, 0, cellWidth, this.canvas.height);

    this.canvasCtx.fillStyle = "#fff";

     // 先绘制填充区域

  this.canvasCtx.fillStyle = fillColor;

  this.canvasCtx.fillRect(

    cellBeginX,

    0,

    cellWidth,

    this.canvas.height - borderBottomWidth

  );

     // 绘制底部边框

  if (borderBottomColor) {

    this.canvasCtx.fillStyle = borderBottomColor;

    this.canvasCtx.fillRect(

      cellBeginX,

      this.canvas.height - borderBottomWidth,

      cellWidth,

      borderBottomWidth

    );

  }

}

  // 初始化刻度线

  initGraduationsLines(date) {

    let widthTotal = this.canvasWidth;

    // 一分钟转换为px

    let pxPerMinute = widthTotal / (this.zoomHours * 60);

    // 一毫秒转换为px

    let pxPerMs = widthTotal / (this.zoomHours * 60 * 60 * 1000);

    // 一格转换为分钟

    let StepPerMinute = this.graduationMinStep / pxPerMinute;

    let minutePerStep = this.minutesPerStep.find(

      (item) => item > StepPerMinute

    );

    // 每格的宽度

    let stepPerPx = minutePerStep * pxPerMinute;

    //总格数(每格{minutePerStep}分钟)

    let stepTotalNum = widthTotal / stepPerPx;

    let msOffset = this.ms_to_next_step(date, minutePerStep * 60 * 1000); //开始的偏移时间 ms

    let pxOffset = msOffset * pxPerMs; //开始的偏移距离 px

    for (let i = 0; i <= stepTotalNum; i++) {

      let graduationTime = date + msOffset + i * (stepPerPx / pxPerMs); //时间=左侧开始时间+偏移时间+格数*ms/格

      let leftPx = pxOffset + i * stepPerPx;

      let lineH = 10;

      let width = 1;

      if (this.formatDate("hh:mm:ss", graduationTime) === "00:00:00") {

        // 0点

        lineH = 20;

        width = 3;

        let bigDateTitle = this.formatDate(" ", graduationTime);

        this.canvasGraduationsLinesCtx.textAlign = "center";

        this.canvasGraduationsLinesCtx.fillStyle = "#ffffff";

        this.canvasGraduationsLinesCtx.font =

          "normal normal 12px Arial,sans-serif";

        this.canvasGraduationsLinesCtx.fillText(bigDateTitle, leftPx, 30);

      } else if ((graduationTime / (60 * 6 * 1000)) % minutePerStep === 0) {

        // 整小时

        lineH = 15;

        width = 2;

        let middleDateTitle = this.formatDate("hh:mm", graduationTime);

        this.canvasGraduationsLinesCtx.fillStyle = "#ffffff";

        this.canvasGraduationsLinesCtx.textAlign = "center";

        this.canvasGraduationsLinesCtx.font =

          "normal normal 12px Arial,sans-serif";

        this.canvasGraduationsLinesCtx.fillText(middleDateTitle, leftPx, 25);

      } else {

        lineH = 10;

        width = 1;

      }

      let options = {

        beginX: leftPx,

        beginY: 0,

        endX: leftPx,

        endY: lineH,

        color: "rgba(151,158,167,1)",

        width: 1,

      };

      // 刻度线

      this.drawLine(options);

    }

  }

  // 初始化指针

  initPlayPointerCtx() {

    if (this.currentTimestamp) {

      this.pointerOptions.beginX =

        this.pxPerMs * (this.currentTimestamp - this.date);

      this.pointerOptions.endX =

        this.pxPerMs * (this.currentTimestamp - this.date);

    }

    this.canvasPlayPointerCtx.beginPath();

    // 开始位置 (0,0)

    this.canvasPlayPointerCtx.moveTo(

      this.pointerOptions.beginX,

      this.pointerOptions.beginY

    );

    // 结束位置 (0,30)

    this.canvasPlayPointerCtx.lineTo(this.pointerOptions.endX, 50);

    this.canvasPlayPointerCtx.strokeStyle = this.pointerOptions.color;

    this.canvasPlayPointerCtx.lineWidth = this.pointerOptions.width;

    this.canvasPlayPointerCtx.stroke();

    // 正方形

    this.canvasPlayPointerCtx.beginPath();

    const squareSize = 8; // 正方形的边长

    this.canvasPlayPointerCtx.rect(

      this.pointerOptions.beginX - squareSize / 2,

      0,

      squareSize,

      squareSize

    );

    let grd = this.canvasPlayPointerCtx.createLinearGradient(0, 0, 200, 0); // 使用渐变颜色填充,从(0,0)到(200,0) (左到右)

    grd.addColorStop(0, this.pointerOptions.color); // 起始颜色

    grd.addColorStop(1, this.pointerOptions.color); // 终点颜色

    this.canvasPlayPointerCtx.fillStyle = grd; // 以上面定义的渐变填充

    this.canvasPlayPointerCtx.fill(); // 填充颜色

    this.canvasPlayPointerCtx.closePath();

  }

  ms_to_next_step(timestamp, step) {

    let remainder = timestamp % step;

    return remainder ? step - remainder : 0;

  }

  // 注册事件

  initEvents() {

    if (this.canvas) {

      this.canvas.addEventListener("contextmenu", (e) => {

        e.preventDefault();

      });

      this.canvas.addEventListener(

        "mousemove",

        this.canvasMousemoveFunc.bind(this)

      );

      // 滚轮事件

      this.canvas.addEventListener(

        "mousewheel",

        this.mousewheelFunc.bind(this)

      );

      this.canvas.addEventListener("mousedown", this.mousedownFunc.bind(this));

      this.canvas.addEventListener("mouseup", this.mouseupFunc.bind(this));

      this.canvas.addEventListener("mouseout", this.mouseoutFunc.bind(this));

      this.canvas.addEventListener("click", this.clickFunc.bind(this));

    }

  }

  // 判断能否拖动轴

  canDragAxis() {}

  canDragPointer(ev) {

    let x = ev.offsetX;

    let y = ev.offsetY;

    return this.canvasPlayPointerCtx.isPointInPath(x, y);

  }

  setPointer(ev) {

    if (this.canDragPointer(ev)) {

      this.dom.style.cursor = "pointer";

      this.isDragPointer = true;

      this.isMoveTriangle = true;

    } else if (!this.isPressDown || this.button) {

      // 指针没在拖动过程中

      if (!(this.isPressDown && this.isMoveTriangle)) {

        this.isDragPointer = false;

        this.isMoveTriangle = false;

        this.dom.style.cursor = "";

      }

    }

  }

  // 鼠标点击(按下)

  mousedownFunc(e) {

    console.log("---------", e);

    if (e.button === 0) {

      // 左键按下

      this.isPressDown = true;

      this.button = e.button;

      if (this.canDragPointer(e)) {

        this.currentChecked = "pointer";

      } else {

        this.currentChecked = "axis";

      }

      // 鼠标移动事件

      document.onmousemove = (e) => {

        this.mousemoveFunc(e);

      };

      document.onmouseup = (e) => {

        this.mouseupFunc(e);

        document.onmousemove = null;

        document.onmouseup = null;

      };

      this.isDown = true;

      this.g_mousedownCursor = this.getCursorPositionX(e); // 记住mousedown的位置

    } else if (e.button === 2) {

      // 右键按下

    }

  }

  // canvas 鼠标移动

  canvasMousemoveFunc(e) {

    let posX = this.getCursorPositionX(e);

    this.setPointer(e);

    this.clearCanvas();

    // 只鼠标移动

    let timestamp = this.date + posX / this.pxPerMs;

    if (!this.isMoveTriangle) {

      this.drawLine({

        beginX: posX,

        beginY: 0,

        endX: posX,

        endY: this.canvasHeight,

        color: "rgb(0, 255, 0,0)",

        width: 1,

      });

    }

    this.initCanvasCtx();

    this.canvasCtx.fillStyle = "rgb(0, 255, 0,0)";

    this.canvasCtx.textAlign = "center";

    this.canvasCtx.fillText(this.formatDate("hh:mm", timestamp), posX, 18);

  }

  isValidTime(newDate) {

    const today = new Date();

    const minDate = new Date(

      today.getFullYear(),

      today.getMonth(),

      today.getDate(),

      0,

      0,

      0

    );

    const maxDate = new Date(

      today.getFullYear(),

      today.getMonth(),

      today.getDate(),

      20,

      0,

      0

    );

    return newDate >= minDate && newDate <= maxDate;

  }

  convertTimestampToDate(timestamp) {

    const date = new Date(timestamp);

    return date.toLocaleString();

  }

    // 鼠标指针Y位置

    getCursorPositionY(e) {

      let posy = 0;

      if (!e) {

        e = window.event;

      }

      if (e.pageY) {

        posy = e.pageY;

      } else if (e.clientY) {

        posy =

          e.clientY +

          document.body.scrollTop +

          document.documentElement.scrollTop;

      }

      posy -= this.canvas.getBoundingClientRect().top;

      return posy;

    }

  // 鼠标移动

  mousemoveFunc(e) {

    let posX = this.getCursorPositionX(e);

    let posY = this.getCursorPositionY(e);

    this.setPointer(e);

   

    if (posX < 0 || posX > this.canvasWidth || posY < 0 || posY > this.canvasHeight) {

      // 当鼠标超出画布时,不执行拖动逻辑

      this.clearCanvas();

      this.initCanvasCtx();

      return;

    }

    this.clearCanvas();

    this.isMove = true;

    if (this.currentChecked === "axis") {

      // 拖动时间轴

      let diffX = posX - this.g_mousedownCursor;

      let maxDiffX = 5; // 限制每次更新的最大距离,可以自行调整

      if (Math.abs(diffX) > maxDiffX) {

        diffX = maxDiffX * Math.sign(diffX);

      }

      let newDate = this.date - Math.round(diffX / this.pxPerMs);

      let newDateString = this.convertTimestampToDate(newDate);

      if (this.isValidTime(new Date(newDateString))) {

        this.date = newDate;

        console.log("拖动", newDateString);

      } else {

        // 当新日期超出范围时,更新g_mousedownCursor的值,使其不再移动

        this.g_mousedownCursor = posX;

      }

      this.initCanvasCtx();

    } else if (this.currentChecked === "pointer") {

      let diffX = posX - this.g_mousedownCursor;

      let maxDiffX = 5;

      if (Math.abs(diffX) > maxDiffX) {

        diffX = maxDiffX * Math.sign(diffX);

      }

      this.currentTimestamp = this.date + diffX / this.pxPerMs;

      this.g_mousedownCursor = posX;

      this.initCanvasCtx();

      this.canvasCtx.fillStyle = "rgb(0, 255, 0)";

      this.canvasCtx.textAlign = "center";

      this.canvasCtx.fillText(

        this.formatDate("yyyy-MM-dd hh:mm:ss", this.currentTimestamp),

        posX,

        18

      );

      }

  }

  // 鼠标抬起

  mouseupFunc(e) {

    if (this.isPressDown && this.button === 0) {

      // 左键

      this.isPressDown = false;

      if (this.isMove) {

        // 拖动

        this.isMove = false;

        if (this.currentChecked === "axis") {

          // 拖动轴

        } else if (this.currentChecked === "pointer") {

          // 拖动指针

          this.returnTime = this.currentTimestamp;

          this.returnCheckTime("darg", this.returnTime);

        }

        this.currentChecked = "";

      } else {

        // 点击

        if (!this.isClick) {

          this.isClick = true;

        }

        // let posX = this.getCursorPositionX(e);

        let posX = this.getCursorPositionX(e);

        this.currentTimestamp = this.returnTime =

          this.date +

          (posX * (this.zoomHours * 3600 * 1000)) / this.canvasWidth;

        let timestamp = this.date + posX / this.pxPerMs;

        const time = this.formatDate("hh:mm", timestamp);

        this.returnCheckTime("click", time);

        this.clearCanvas();

        this.initCanvasCtx();

      }

    }

  }

  // 鼠标点击

  clickFunc(e) {

    let posX = this.getCursorPositionX(e);

    let timestamp = this.date + posX / this.pxPerMs;

    const time = this.formatDate("hh:mm", timestamp);

    console.log("查看点击内容", time);

  }

  // 滚轮事件

  mousewheelFunc(event) {

      if (event && event.preventDefault) {

      event.preventDefault()

    } else {

      window.event.returnValue = false;

      return false;

    }

    let e = window.event || event;

    let delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));

    let posX = this.getCursorPositionX(e);

    let middle_time = this.date + posX * (this.zoomHours * 3600 * 1000) / (this.canvasWidth); //ms 记住当前中间的时间

    if (delta < 0) {

      this.zoomHours = this.zoomHours - delta;

      if (this.zoomHours >= 24) {

        this.zoomHours = 24;//放大最大24小时

      }

    } else if (delta > 0) {// 放大

      this.zoomHours = this.zoomHours - delta;

      if (this.zoomHours <= 1) {

        this.zoomHours = 1;//缩小最小1小时

      }

    }

    this.clearCanvas();

    this.date = middle_time - posX * (this.zoomHours * 3600 * 1000) / (this.canvasWidth);

    this.initCanvasCtx()

    console.log('1111111111111111',this.zoomHours)

  }

  mouseoutFunc(e) {

    this.clearCanvas();

    this.initCanvasCtx();

  }

  // 鼠标指针X位置

  getCursorPositionX(e) {

    let 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;

    }

    posx -= this.canvas.getBoundingClientRect().left;

    return posx;

  }

  // 清除canvas 每次重新绘制需要先清除

  clearCanvas() {

    this.canvasCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);

  }

  /**

   * 返回点击或者拖动的时间点

   * @param handle 触发方式{click,darg}

   * @param currentTime 选中时间戳{1637164800000}

   */

  returnCheckTime(handle, currentTime) {

    console.log("查看选择的时间", currentTime);

    // 将 currentTime 转换为 Date 对象

    let currentDate = new Date(currentTime);

    // 转换回一个适当的时间戳(毫秒)

    let adjustedTimestamp = currentDate.getTime();

    // 向下取整

    let formatCurrentTime = Math.floor(adjustedTimestamp);

    // 判断时间是否有效

    let isIndex = this.timecell.findIndex((item) => {

      return formatCurrentTime >= item.begin && formatCurrentTime <= item.end;

    });

    // if (isIndex >= 0) {

    // 有效区域

    if (handle === "darg") {

      this.callback({

        mode: "darg", // 触发方式

        timestamp: formatCurrentTime, // 时间戳

        valid: true,

      });

    } else if (handle === "click") {

      this.callback({

        mode: "click",

        timestamp: currentTime,

        valid: true

      });

    }

  }

  formatDate(fmt, date) {

    let myDate = date ? new Date(date) : new Date();

    fmt = fmt || "yyyy-MM-dd";

    const o = {

      "M+": myDate.getMonth() + 1, // 月份

      "d+": myDate.getDate(), // 日

      "h+": myDate.getHours(), // 小时

      "m+": myDate.getMinutes(), // 分

      "s+": myDate.getSeconds(), // 秒

      "q+": Math.floor((myDate.getMonth() + 3) / 3), // 季度

      S: myDate.getMilliseconds(), // 毫秒

    };

    if (/(y+)/.test(fmt)) {

      fmt = fmt.replace(

        RegExp.$1,

        (myDate.getFullYear() + "").substr(4 - RegExp.$1.length)

      );

    }

    for (const k in o) {

      if (new RegExp("(" + k + ")").test(fmt)) {

        fmt = fmt.replace(

          RegExp.$1,

          RegExp.$1.length == 1

            ? o[k]

            : ("00" + o[k]).substr(("" + o[k]).length)

        );

      }

    }

    return fmt;

  }

  updateDatas(options) {

    this.zoomHours = options.zoomHours;

    this.date = options.date;

    this.timecell = options.times;

    if (options.times.length) {

      // this.currentTimestamp = options.times[0].begin

    }

    this.clearCanvas();

    this.initCanvasCtx();

  }

  // 更新时间 m

  updateTime(time) {

    if (this.returnTime) {

      this.returnTime = time;

      this.currentTimestamp = time;

    }

    this.clearCanvas();

    this.initCanvasCtx();

  }

  setCurrentTimestamp(timestamp) {

    this.currentTimestamp = timestamp;

    this.initCanvasCtx();

    this.canvasCtx.fillStyle = "rgb(0, 255, 0)";

    this.canvasCtx.textAlign = "center";

    let posX = (timestamp - this.date) * this.pxPerMs;

    this.canvasCtx.fillText(

      this.formatDate("yyyy-MM-dd hh:mm:ss", this.currentTimestamp),

      posX,

      18

    );

  }

  setPointer(timestamp) {

    this.setCurrentTimestamp(timestamp);

    this.callback({ timestamp: timestamp });

  }  

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt 是一个跨平台的应用程序开发框架,它包含丰富的功能模块,其中也包括了用于处理视频的模块。视频时间轴是一种常见的功能,它在处理视频时非常有用。 在 Qt 中,可以使用 QML(Qt中的标记语言)和 C++ 来创建具有视频时间轴的应用程序。下面是一个简单的示例来说明如何使用 Qt 实现视频时间轴功能: 1. 首先,导入 Qt 的相关模块和库,如 QtQuick、QtMultimedia 和 QtQuick.Controls。 2. 创建一个包含视频视图和时间轴的用户界面。可以使用 QML 来定义界面的外观和布局,并将其与后端的 C++ 代码连接起来。 3. 使用 QMediaPlayer 类来加载和播放视频文件。可以设置视频文件的 URL、音量和其他属性。 4. 使用 QML 创建一个时间轴组件,例如使用 Slider 控件。将 Slider 绑定到视频播放位置,使它能够通过拖动来调整视频播放进度。 5. 配置时间轴的各种属性,例如设置最小值和最大值、步长和初始值。还可以添加其他样式和行为,如滑块样式、间隔线和提示信息。 6. 将时间轴的值连接到 QMediaPlayer 的 setPosition() 方法,以便在滑块被拖动时调整视频播放位置。 7. 将播放按钮和暂停按钮与 QMediaPlayer 的 play() 和 pause() 方法连接起来,以便控制视频播放和暂停。 8. 可以根据需要添加其他功能,如快进、快退、重新播放和全屏等。 通过使用这些步骤,可以在使用 Qt 开发的应用程序中实现视频时间轴功能。这个示例只是一个简单的起点,开发者可以根据实际的需求和设计来扩展和定制。Qt 的强大功能可以帮助开发者轻松实现各种视频处理需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值