【html】利用生成器函数和video元素,取出指定时间的视频画面

news2025/2/23 10:18:42

简言

有的时候想截取视频某一秒的视频画面。
手动截取操作麻烦,还得时刻关注视频播放时间。
于是,我搞出来了一个根据视频自动截取特定时间描述的页面。

效果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

实现步骤

  1. 获取视频对象
  2. 根据视频时长生成时间选择表单
  3. 根据表单选择的时间和视频地址,利用canvas和vido元素生成某一帧的视频画面图片
  4. 图片实现下载

源码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>处理视频</title>
  <style>
    .container {
      margin: 50px;
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="upload__box">
      <input id="upVideo" name="upVideo" type="file" accept="video/*" placeholder="上传视频" />

    </div>
    <div class="video__box">
      <video width="800" height="450" id="video" src="" controls></video>
    </div>
    <div class="select__box">

    </div>
    <div class="analyze__text">

      <ul>

      </ul>
    </div>
  </div>

  <script>
    const upVideoDom = document.getElementById("upVideo")
    const videoDom = document.getElementById('video')
    const selectBoxDom = document.querySelector('.select__box')
    const TextListDom = document.querySelector('.analyze__text')
    upVideoDom.addEventListener("change", (e) => {
      const file = e.target.files[0];
      let videoURL = URL.createObjectURL(file)
      videoDom.src = videoURL

      videoDom.onloadedmetadata = (e) => {
        //  视频时长
        let videoDuration = e.target.duration
        createTimeSelectDom(videoDuration)
      }

    })
    //  根据时长创建dom选择器
    function createTimeSelectDom(duration) {
      //  生成可选秒数列表
      let fragDom = document.createDocumentFragment()
      let form = document.createElement("form")
      form.id = 'selectForm'
      form.name = 'selectForm'
      if (duration < 60) {
        let selectDom = document.createElement("select")
        selectDom.multiple = true
        selectDom.form = "selectForm"
        selectDom.name = "seconds"
        let optionDom = document.createElement("option")
        optionDom.innerText = '--选择秒数--'
        optionDom.value = ''
        selectDom.appendChild(optionDom)
        for (let i = 0; i < duration; i++) {
          let item = i.toString().padStart(2, '0')
          let optionDom = document.createElement("option")
          optionDom.innerText = item
          optionDom.value = item
          selectDom.appendChild(optionDom)
        }
        form.appendChild(selectDom)
      } else if (duration < 3600) {
        let minute = duration / 60
        let seconds = duration % 60
        let miuteSelectDom = document.createElement("select")
        miuteSelectDom.form = "selectForm"
        miuteSelectDom.name = "mintue"
        let optionDom = document.createElement("option")
        optionDom.innerText = '--选择分--'
        optionDom.value = ''
        miuteSelectDom.appendChild(optionDom)
        for (let i = 0; i < minute; i++) {
          let item = i.toString().padStart(2, '0')
          let optionDom = document.createElement("option")
          optionDom.innerText = item
          optionDom.value = item
          miuteSelectDom.appendChild(optionDom)
        }
        let secondsSelectDom = document.createElement("select")
        secondsSelectDom.multiple = true
        secondsSelectDom.form = "selectForm"
        secondsSelectDom.name = "seconds"
        let optionDom2 = document.createElement("option")
        optionDom2.innerText = '--选择秒数--'
        optionDom2.value = ''
        secondsSelectDom.appendChild(optionDom2)
        for (let i = 0; i < 59; i++) {
          let item = i.toString().padStart(2, '0')
          let optionDom = document.createElement("option")
          optionDom.innerText = item
          optionDom.value = item
          secondsSelectDom.appendChild(optionDom)
        }
        form.appendChild(miuteSelectDom)
        form.appendChild(secondsSelectDom)

      } else {

      }
      let submitInput = document.createElement("input")
      submitInput.type = 'submit'
      submitInput.value = '确定'
      form.onsubmit = formSubmit
      form.appendChild(submitInput)
      let resetInput = document.createElement("input")
      resetInput.type = 'reset'
      resetInput.value = '重置'
      form.appendChild(resetInput)
      fragDom.appendChild(form)
      selectBoxDom.appendChild(fragDom)
    }
    function formSubmit(e) {
      e.preventDefault()
      let mintueV = '00'
      if (selectForm.mintue) {
        mintueV = selectForm.mintue.value
      }
      let timeList = []
      for (let v of selectForm.seconds.options) {
        if (v.selected) {
          timeList.push(`${mintueV}:${v.value}`)
        }
      }
      //  生成帧图片
      TextListDom.children[0].innerHTML = ''
      createFramepicture({
        url: videoDom.src, timeList
      })
    }
    //  生成帧图片列表
    function createFramepicture({ url, timeList, }) {
      if (!url || !timeList) return
      let time;

      const video = document.createElement('video')
      video.width = 160
      video.height = 90
      video.crossorigin = "anonymous"
      video.src = url

      let canvas = document.createElement("canvas"),
        width = video.width,
        height = video.height;
      canvas.width = width
      canvas.height = height

      function* createTime() {
        for (let k = 0; k < timeList.length; k++) {
          time = timeList[k]


          let timeArr = time.split(':')
          let currentTime = Number(timeArr[0]) * 60 + Number(timeArr[1])

          video.currentTime = currentTime //  会导致 oncanplaythrough重新触发
          yield true


        }
      }
      let a = createTime()
      video.oncanplaythrough = () => {

        if (time) {
          //  当time存在时再渲染,避开video第一次加载成功
          analyzeVideo(video, time, canvas)
        }
        a.next()

      }


    }
    function analyzeVideo(video, time, canvas) {
      const ctx = canvas.getContext('2d')

      // ctx.clearRect(0, 0, video.width, video.height)
      ctx.drawImage(video, 0, 0, video.width, video.height);
      let dataURL = canvas.toDataURL("image/png")
      let li = document.createElement('li')

      let img = document.createElement('img')
      img.src = dataURL
      let p = document.createElement('p')
      p.innerText = `${time}`

      let btn = document.createElement('button')
      btn.innerText = '下载'
      btn.onclick = () => {
        downloadImg(dataURL, time)
      }
      li.appendChild(p)
      li.appendChild(img)
      li.appendChild(btn)

      const fragDom = document.createDocumentFragment()
      fragDom.appendChild(li)

      TextListDom.children[0].appendChild(fragDom)


    }


    function downloadImg(src, time) {
      let a = document.createElement('a')
      a.href = src
      a.download = `frame picture`
      a.click()
    }
  </script>
</body>

</html>

结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1088198.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

钢筋智能测径仪 光圆与带肋钢筋均可检测!

在一个大规模、高效、连续的工业生产中&#xff0c;制造业正朝着自动化方向快速优化发展&#xff0c;这种自动化的生产需要快速、准确地分析控制生产工艺中的参数&#xff0c;超差及时提示&#xff0c;为操作工对工厂的运行和自我调节做出快速反应&#xff0c;人工操作越来越不…

智能井盖传感器扣好“城市纽扣”,让市民脚下更有安全感

随着城市化进程的快速推进&#xff0c;城市基础设施的维护和管理面临着日益严峻的挑战。作为城市生命线的重要组成部分&#xff0c;城市井盖在保障城市安全和稳定运行方面具有举足轻重的地位。然而&#xff0c;日益繁重的城市交通压力使得井盖的维护和管理问题逐渐显现。 城市井…

【网络编程】Linux网络编程基础与实战第一弹——网络基础

这里写目录标题 网络基础什么是协议典型协议 分层模型OSI七层模型TCP/IP四层模型 网络应用程序设计模式优缺点具体体现&#xff1a; 网络基础 什么是协议 从应用的角度出发&#xff0c;协议可理解为“一组规则”&#xff0c;是数据传输和数据的解释的规则。 假设&#xff0c;…

(echarts)折线图封装相关总结及使用

(echarts)折线图封装相关总结及使用 一、封装组件lineCharts.vue <template><div :id"id" :class"className" :style"{ height: height, width: width }" /> </template><script> import echarts from "echarts&q…

python自动化办公之文件整理脚本详解

今天讲解文件整理脚本的实现过程。这是一个很有用的技能&#xff0c;可以帮助你管理你的电脑上的各种文件。需求如下&#xff1a; 需求内容&#xff1a;给定一个打算整理的文件夹目录&#xff0c;这个脚本可以将该目录下的所有文件都揪出来&#xff0c;并且根据后缀名归类到不同…

苍穹外卖(三) 员工分页及技术实现细节

2. 员工分页查询 2.1 需求分析和设计 2.1.1 产品原型 2.1.2 接口设计 2.2 代码开发 2.2.1 设计DTO类 根据请求参数进行封装 2.2.2 封装PageResult 后面所有的分页查询&#xff0c;统一都封装为PageResult对象。 员工信息分页查询后端返回的对象类型为: Result 2.…

【Redis】渐进式遍历

scan命令渐进式遍历 Redis使⽤scan命令进⾏渐进式遍历键&#xff0c;进⽽解决直接使⽤keys获取键时可能出现的阻塞问题。每次scan命令的时间复杂度是O(1)&#xff0c;但是要完整地完成所有键的遍历&#xff0c;需要执⾏多次scan。 SCAN 以渐进式的⽅式进⾏键的遍历。 SCAN…

C# Onnx Yolov8 Detect 涉黄检测

效果 项目 检测类别 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; usi…

Python批量测试IP端口GUI程序(Tkinter)

一、实现样式 批量IP与端口中间用“,”分割&#xff0c;点击Telnet进行测试&#xff0c;前提是你电脑安装了telnet客户端&#xff0c;Clear按钮用来清空文本框。 二、核心点 1、使用Tkinter来制作桌面GUI页面 2、使用telnetlib模块测试telnet端口 三、困难点 1、测试结果…

SpringBootCMS漏洞复现分析

SpringBootCMS&#xff0c;极速开发&#xff0c;动态添加字段&#xff0c;自定义标签&#xff0c;动态创建数据库表并crud数据&#xff0c;数据库备份、还原&#xff0c;动态添加站点(多站点功能)&#xff0c;一键生成模板代码&#xff0c;让您轻松打造自己的独立网站&#xff…

2023年淘宝天猫双11活动时间什么时候开始到几月几号结束?

2023年淘宝天猫双11超级红包领取时间 第一阶段&#xff1a;2023年10月24日20:00 至11月03日23:59 第二阶段&#xff1a;2023年11月04日00:00 至 11月11日23:59 2023年淘宝天猫双11超级红包使用时间 第一阶段&#xff1a;2023年10月31日20:00 至11月03日23:59 第二阶段&…

Semantic Segmentation

目录 1.可视化数据集 2.标签中像素类别的对应 3.预处理数据 4.定义数据集类 5.创建模型 5.1 固定模型参数 5.2 修改成FCN&#xff08;全卷积层&#xff09; 5.3 初始化转置卷积层 6.训练模型 6.1 optim.lr_scheduler.StepLR 6.2 torch.set_grad_enabled(bool:) 6.3…

【Vue基础-数字大屏】加载动漫效果

一、需求描述 当网页正在加载而处于空白页面状态时&#xff0c;可以在该页面上显示加载动画提示。 二、步骤代码 1、全局下载npm install -g json-server npm install -g json-server 2、在src目录下新建文件夹mock&#xff0c;新建文件data.json存放模拟数据 {"one&…

1 随机事件与概率

首先声明【这个括号内的都是批注】 文章目录 1 古典概型求概率1.1 随机分配问题【放球】例子 1.2 简单随机抽样问题【取球】例子 2 几何概型求概率例子 3 重要公式求概率3.1 对立3.2 互斥3.3 独立3.4 条件&#xff08;要做分母的必须大于0&#xff09;例子 3.5 不等式或包含例…

为什么曾经一马当先的C语言,如今却开始出现骂声一片?

今日话题&#xff0c;为什么曾经一马当先的C语言&#xff0c;如今却开始出现各种骂声&#xff1f;C语言的发展历程可以追溯到20世纪70年代初期&#xff0c;它的设计理念、简洁性、可移植性以及对底层硬件的直接控制能力使其在计算机科学领域逐渐受到重视从而成为了天王搬到存在…

msvcr120.dll丢失怎样修复?总结msvcr120.dll丢失的5修复方法

在使用计算机的过程中&#xff0c;我们常常会遇到各种问题&#xff0c;其中之一就是“计算机丢失msvcr120.dll丢失的困扰”。这个问题可能对一些人来说并不陌生&#xff0c;但是对于初次遇到这个问题的人来说&#xff0c;可能会感到无所适从。因此&#xff0c;小编将详细探讨这…

好用的跨平台同步笔记工具,手机和电脑可同步的笔记工具

在这个快节奏的工作环境中&#xff0c;每个人都在寻找一种方便又高效的方式来记录工作笔记。记录工作笔记可以帮助大家统计工作进展&#xff0c;了解工作进程&#xff0c;而如果工作中常在一个地方办公&#xff0c;直接选择电脑或者手机中笔记工具来记录即可&#xff0c;但是对…

Docker学习之路

#一、Docker的作用 #二、Docker的常用命令 查看docker中正在运行的项目 sudo docker ps如果提示这个错误&#xff0c;可能是没有权限&#xff0c;需要找运维同学加权限 如下如所示&#xff0c;可以看到当前服务器上正在运行的项目信息 进入docker sudo docker exec -it…

李雪琴出任滴滴亲友守护产品推荐官

曾经调侃一手带大妈妈的脱口秀演员李雪琴&#xff0c;最近又成了妈妈的“守护人”。 铁岭市民贾女士和李雪琴一起参加过多档综艺节目&#xff0c;母女俩幽默直率的性格&#xff0c;轻松的相处方式&#xff0c;收获了很多网友的喜爱。 妈妈退休后经常和家人朋友一起逛街、小聚…

echarts实现横轴刻度名倾斜展示,并且解决文字超出部分消失问题

需求背景&#xff1a; xAxis.axisLabel. interval如果不手动设值的话&#xff0c;默认就是‘auto’&#xff0c;会采用标签不重叠的策略间隔显示标签。当数据量特别大的时候&#xff0c;展示出来的刻度标签就会很少&#xff0c;导致用户体验不好。如下图所示&#xff1a; 如果…