react+video.js h5自定义视频暂停图标

news2024/11/23 9:41:11

目录

参考网址

效果图,暂停时显示暂停图标,播放时隐藏暂停图标

代码说明,代码传入url后,可直接复制使用

VideoPausedIcon.ts 组件

VideoCom.tsx

Video.module.less


参考网址

 在Video.js播放器中定制自己的组件 - acgtofe

效果图,暂停时显示暂停图标,播放时隐藏暂停图标

代码说明,代码传入url后,可直接复制使用

注意:videojs升级后,不能用extend创建组件,需要按下方代码建一个组件

VideoPausedIcon.ts 组件
import videojs from "video.js";

const Component: any = videojs.getComponent("Component");

export class VideoPausedIcon extends Component {
  constructor(player: any, options: any) {
    super(player, options);
    // 监听,暂停播放,显示播放按钮
    player.on("pause", () => {
      this.visible = true;
      const el = this.el();
      el.className = "vjs-define-paused";
    });
    // 监听,开始播放,隐藏播放按钮,通过videojs自带的 vjs-hidden 类
    player.on("play", () => {
      this.visible = false;
      const el = this.el();
      el.className = "vjs-define-paused vjs-hidden";
    });
    this.on("touchstart", this.handleTouchStart);
    this.on("touchend", this.handleTouchEnd);
    // 如果需要在web端使用,必须,不兼容的话,web端点击按钮就不会暂停/播放
    this.on("click", (e: any) => this.handleClick(e, player));
  }

  createEl() {
    let pauseIcon = videojs.dom.createEl("div", {
      className: "vjs-define-paused",
      tabIndex: -1,
    });
    !this.visible && videojs.dom.appendContent(pauseIcon, "");
    return pauseIcon;
  }

  handleTouchStart(e: any) {
    // 此处必须,不然点击按钮不能播放/暂停
    e.stopPropagation();
  }

  handleTouchEnd(e: any) {
    // 此处必须,不然点击按钮不能播放/暂停
    e.stopPropagation();
  }

  handleClick(e: any, player: any) {
    e.stopPropagation();
    if (!player) {
      return;
    }
    const paused = player.paused();
    if (paused) {
      player.play();
    } else {
      player.pause();
    }
  }
}
VideoCom.tsx
import React, { useEffect } from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";
import styles from "./Video.module.less";
import { VideoPausedIcon } from "./VideoPausedIcon";

interface IProps {
  url: string;
}

const VideoCom: React.FC<IProps> = (props: any) => {
  const videoRef = React.useRef<any>(null);
  const playerRef = React.useRef<any>(null);

  useEffect(() => {
    const player: any = playerRef && playerRef.current;
    return () => {
      if (player && !player.isDisposed()) {
        player.dispose();
        playerRef.current = null;
      }
    };
  }, [playerRef]);

  useEffect(() => {
    if (!props.url) {
      return;
    }
    let options: any = {
      controlBar: {
        fullscreenToggle: true,
        pictureInPictureToggle: false,
        volumePanel: false,
        playToggle: false,
      },
      muted: false,
      controls: true, //进度条
      autoplay: false, //自动播放
      loop: false, //是否循环
      languages: {
        "zh-CN": new URL(`video.js/dist/lang/zh-CN.json`, import.meta.url).href,
      },
      language: "zh-CN",
      preload: "auto",
      nodownload: true,
      sources: [{ src: props.url, type: "video/mp4" }],
    };

    // Make sure Video.js player is only initialized once
    if (!playerRef || !playerRef.current) {
      // The Video.js player needs to be _inside_ the component el for React 18 Strict Mode.
      const videoElement = document.createElement("video-js");
      videoRef &&
        videoRef.current &&
        videoRef.current.appendChild(videoElement);

      playerRef.current = videojs(videoElement, options, () => {
        console.log("player is ready");
        const player: any = playerRef.current;
        player.on("error", (err: any) => {
          console.log("source load fail");
          // message.error("视频源请求出错");
        });
        let touchStartTime = 0;
        let touchEndTime = 0;
        player.on("touchstart", (e: any) => {
          touchStartTime = new Date().getTime();
        });
        player.on("touchend", (e: any) => {
          touchEndTime = new Date().getTime();
          if (touchEndTime - touchStartTime < 300) {
            const paused = player.paused();
            if (paused) {
              player.play();
            } else {
              player.pause();
            }
          }
        });
      });
      createPausedIcon();
    } else {
      const player: any = playerRef.current;
      player.src(options.sources);
    }
  }, [
    props.url,
    playerRef,
    videoRef
  ]);

  const createPausedIcon = () => {
    const player = playerRef && playerRef.current;
    if (!player) {
      return;
    }
    const Component: any = videojs.getComponent("Component");
    Component.registerComponent("VideoPausedIcon", VideoPausedIcon);
    const options = {};
    const properIndex = player
      .children()
      .indexOf(player.getChild("BigPlayButton"));
    player.addChild("VideoPausedIcon", options, properIndex);
  };

  return (
    <div className={styles.container}>
      <div className={styles.videoBox} ref={videoRef}></div>
    </div>
  );
};

export default VideoCom;
Video.module.less
.container {
  width: 100%;
  height: 100%;

  .videoBox {
    width: 100%;
    height: 100%;

    :global {
      .video-js {
        width: 100%;
        height: 100%;

        .vjs-big-play-button {
          display: none;
        }

        .vjs-define-paused {
          width: 30px;
          height: 28px;
          background: rgba(43, 63, 46, 0.7);
          border: 1px solid rgb(0, 255, 140);
          border-radius: 10px;
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          transition: all .4s;

          &::after {
            display: block;
            content: '';
            position: absolute;
            top: 50%;
            left: calc(50% + 5px);
            transform: translate(-50%, -50%);
            border-top: 5px solid;
            border-bottom: 5px solid;
            border-left: 8px solid;
            border-right: 8px solid;
            border-color: transparent;
            border-left-color: rgb(0, 255, 140);
          }
        }
      }
    }
  }

}

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

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

相关文章

【vue2】前端如何播放rtsp 视频流,拿到rtsp视频流地址如何处理,海康视频rtsp h264 如何播放

文章目录 测试以vue2 为例新建 webrtcstreamer.js下载webrtc-streamervideo.vue页面中调用 最近在写vue2 项目其中有个需求是实时播放摄像头的视频&#xff0c;摄像头是 海康的设备&#xff0c;搞了很长时间终于监控视频出来了&#xff0c;记录一下&#xff0c;放置下次遇到。…

《QT从基础到进阶·三十》QVariant的基础用法

很多时候&#xff0c;需要几种不同的数据类型需要传递&#xff0c;如果用结构体&#xff0c;又不大方便&#xff0c;容器保存的也只是一种数据类型&#xff0c;而QVariant则可以统统搞定。 QVariant可以保存QT和C常用类型&#xff0c;如果是自定义类型&#xff0c;比如struct,c…

克鲁斯卡尔算法(C++)

目录 克鲁斯卡尔算法 ​编辑代码&#xff1a; 结果&#xff1a; 克鲁斯卡尔算法 克鲁斯卡尔算法是一种用于求解最小生成树的算法。最小生成树是指一棵包含了所有节点的连通图&#xff0c;并且边的权值之和最小。 克鲁斯卡尔算法的基本思想是&#xff0c;每次选择图中最小的…

【漏洞复现】OneThink前台注入漏洞

漏洞描述 OneThink 是一个基于 PHP 的开源内容管理框架&#xff0c;旨在简化和加速Web应用程序的开发过程。它提供了一系列通用的模块和功能&#xff0c;使开发者能够更轻松地构建具有灵活性和可扩展性的内容管理系统&#xff08;CMS&#xff09;和其他Web应用。 免责声明 …

nodejs+vue教室管理系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

用户 用户管理&#xff1a;查看&#xff0c;修改自己的个人信息 教室预约&#xff1a;可以预约今天明天的教室&#xff0c;按着时间段预约&#xff08;可多选&#xff09;&#xff0c;如果当前时间超过预约时间段不能预约该时间段的教室 预约教室的时候要有个预约用途&#xff…

Python爬虫教程:从入门到实战

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是涛哥&#xff0c;今天为大家分享 Python爬虫教程&#xff1a;从入门到实战&#xff0c;文章3800字&#xff0c;阅读大约15分钟&#xff0c;大家enjoy~~ 网络上的信息浩如烟海&#xff0c;而爬虫&#xff08;…

Javaweb之Vue指令的详细解析

2.3 Vue指令 在上述的快速入门中&#xff0c;我们发现了html中输入了一个没有学过的属性v-model&#xff0c;这个就是vue的指令。 指令&#xff1a;HTML 标签上带有 v- 前缀的特殊属性&#xff0c;不同指令具有不同含义。例如&#xff1a;v-if&#xff0c;v-for… 在vue中&a…

人工智能基础_机器学习039_sigmoid函数_逻辑回归_逻辑斯蒂回归_分类神器_代码实现逻辑回归图---人工智能工作笔记0079

逻辑斯蒂回归(Logistic Regression)是一种常用的分类算法,其基本思想是通过拟合一个逻辑斯蒂函数来预测样本所属的类别。它广泛应用于各个领域,如医学、金融、市场营销等,具有较好的解释性和可解释性。在逻辑斯蒂回归中,我们通常使用的是二分类问题,即样本只属于两个类别…

Vue+ElementUI技巧分享:自定义表单项label的文字提示

文章目录 概要在表单项label后添加文字提示1. 使用 Slot 自定义 Label2. 添加问号图标与提示信息 slot的作用详解1. 基本用法2. 具名插槽 显示多行文字提示的方法1. 问题背景2. 实现多行内容显示3. 样式优化 结语 概要 在Vue和ElementUI的丰富组件库中&#xff0c;定制化表单是…

01_SHELL编程之变量定义(一)

SHELL编程 该课程主要包括以下内容&#xff1a; ① Shell的基本语法结构 如&#xff1a;变量定义、条件判断、循环语句(for、until、while)、分支语句、函数和数组等&#xff1b; ② 基本正则表达式的运用&#xff1b; ③ 文件处理三剑客&#xff1a;grep、sed、awk工具的使用&…

确保人工智能的公平性:生成无偏差综合数据的策略

一、介绍 合成数据生成涉及创建密切模仿现实世界数据但不包含任何实际个人信息的人工数据&#xff0c;从而保护隐私和机密性。然而&#xff0c;至关重要的是&#xff0c;这些数据必须以公平、公正的方式生成&#xff0c;以防止人工智能应用中现有的偏见长期存在或扩大。 在数据…

R语言——taxize(第二部分)

taxize&#xff08;第二部分&#xff09; 3. taxize 文档中译3.10. classification&#xff08;根据类群ID检索分类阶元层级&#xff09;示例1&#xff1a;传递单个ID值示例2&#xff1a;传递多个ID值示例3&#xff1a;传递单个名称示例4&#xff1a;传递多个名称示例5&#xf…

Spring SPI

SPI 服务供给接口&#xff08;Service Provider Interface&#xff09;。是Java 1.5新添加的一个内置标准&#xff0c;允许不同的开发者去实现某个特定的服务。 1 SPI 介绍 一个接口&#xff0c;可能会有许多个实现&#xff0c;我们在编写代码时希望能动态切换具体实现&#…

微服务测试怎么做

开发团队越来越多地选择微服务架构而不是单体结构&#xff0c;以提高应用程序的敏捷性、可扩展性和可维护性。随着决定切换到模块化软件架构——其中每个服务都是一个独立的单元&#xff0c;具有自己的逻辑和数据库&#xff0c;通过 API 与其他单元通信——需要新的测试策略和新…

关系代数、SQL语句和Go语言示例

近些年&#xff0c;数据库领域发展日新月异&#xff0c;除传统的关系型数据库外&#xff0c;还出现了许多新型的数据库&#xff0c;比如&#xff1a;以HBase、Cassandra、MongoDB为代表的NoSQL数据库&#xff0c;以InfluxDB、TDEngine为代表的时序数据[1]库&#xff0c;以Neo4J…

设计模式-代理模式-笔记

动机&#xff08;Motivation&#xff09; 在面向对象系统中&#xff0c;有些对象由于某种原因&#xff08;比如对象创建的开销很大&#xff0c;或者某些操作需要安全控制&#xff0c;或者需要远程外的访问等&#xff09;&#xff0c;直接访问会给使用者、或者系统结构带来很多…

【Linux网络】工作环境救急——关于yum安装的5个花式操作

目录 1、只下载不安装&#xff0c;离线安装软件 2、自行打包创建元数据 第一步&#xff1a;先准备好nginx的软件包&#xff0c;放在一个文件夹下 第二步&#xff1a;在本地下载createrepo命令软件&#xff0c;用于创建元信息&#xff0c;这个一定是对包的上一级目录使用命令…

cpolar+LightPicture,将个人电脑改造成公网图床服务器

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

QNX Typed memory介绍

文章目录 前言一、什么是 Typed memory二、查看系统已有Typed memory 的方法三、Typed memory 的使用方法1.定义一个packet memory并从系统内存中分出它1.1 as_add()1.2 as_add_containing()2. 从 Typed memory 中申请内存2.1 POSIX method 申请内存2.2 QNX Neutrino method 申…

第9章 K8s进阶篇-持久化存储入门

9.1 k8s存储Volumes介绍 Container&#xff08;容器&#xff09;中的磁盘文件是短暂的&#xff0c;当容器崩溃时&#xff0c;kubelet会重新启动容器&#xff0c;但最初的文件将丢失&#xff0c;Container会以最干净的状态启动。另外&#xff0c;当一个Pod运行多个Container时&…