美文网首页
如何使用rrweb 做一个录屏系统?

如何使用rrweb 做一个录屏系统?

作者: 前端蜗牛老师 | 来源:发表于2021-10-12 17:29 被阅读0次

背景:最近在做一个保险的项目 监管要求,需要对用户操作进行录屏保存。市面上有的录屏系统太大,收费的,也挺贵,大概20-30万左右,还不包括存储硬件资源,果断选择自己开发/
技术实现:使用rrweb + pako.js 压缩文件
技术架构: C端录制(vue+rrweb+pako.js) + java+磁盘存储 + 后端系统配置录制页面及展示视频

具体代码写起来也不麻烦,关键是如果封装,我使用的是rrweb结合 vue插件开发一套录屏系统

核心插件部分
vsr.js

import {checkRecording, setValue} from "../../api"; // 判断是否录屏后台配置那个页面需要录屏,保存数据接口
import router from '../../router' // 引入router 
import Cookies from 'js-cookie' 
import md5 from 'js-md5'; // md5加密
import {rrWebZip} from "../../utils"; // 压缩工具函数
class vsr {
  constructor() {
    // this.operation = null;
    // this.url = ''
    // this.timer = null
    // this.needRecord = false
    // this.events = [];
  }

  async vsrInit() {
    let events = []; // 定义一个空数组,保存录屏数据
    let _this = this

    rrweb.record({
      emit(event) {
        events.push(event);
      },
      sampling: {
        // 不录制鼠标移动事件
        // mousemove: false,
        // 不录制鼠标交互事件
        // mouseInteraction: false,
        // 设置滚动事件的触发频率
        scroll: 200, // 每 200ms 最多触发一次
        // 设置输入事件的录制时机
        // input: 'last', // 连续输入时,只录制最终值
        MouseUp: false,
        MouseDown: false,
        // Click: false,
        ContextMenu: false,
        // DblClick: false,
        // Focus: false,
        // Blur: false,
        // TouchStart: false,
        // TouchEnd: false,
      },
      inlineStylesheet: true,
      slimDOMOptions: {
        svg: true,
        script: true,
        comment: true,
        headFavicon: true,
        headWhitespace: true,
        headMetaDescKeywords: true,
        headMetaSocial: true,
        headMetaRobots: true,
        headMetaHttpEquiv: true,
        headMetaAuthorship: true,
        headMetaVerification: true,
      },
      packFn: rrweb.pack, // 压缩算法
      // recordLog: true
      // checkoutEveryNth: 200, // 每 200 个 event 重新制作快照
    });

    // save 函数用于将 events 发送至后端存入,并重置 events 数组
    async function save() {
      const timestamp = new Date().getTime()
      let body = ''
      if (events.length > 0) {
        // console.log(JSON.stringify(events), 'JSON.stringify(events)')
        // console.log(rrWebZip(JSON.stringify(events)), 'rrWebZip(JSON.stringify(events)')
        body =  timestamp + md5(timestamp + 'zbkl') + rrWebZip(JSON.stringify(events)) // 压缩传输
        events = [] // 每次调保存接口,立即清空当前录屏数据,保证每次保存为最新数据
        await setValue(body);
      }
    }


    router.beforeEach(async (to, from, next) => { // 监听页面跳转,即调是否录屏接口
      let url = _this.getDomain() + '/#' + to.path
      const timestamp = new Date().getTime()
      if(to.name === 'Insure' || to.name === 'PayWay' || to.name === 'Succeed' || to.name === 'PayError' || to.name === 'Payment' || to.name === 'Renewal') {
        let result = await checkRecording({
          "startTime": timestamp,
          "url": url,
          'agentID': to.query && to.query.agentID
        })
        if (result.result && result.result.needRecord) {
          Cookies.set('zbklTrackMark', result.result && result.result.eventsId, 'Infinity')
          save()
          if(to.name === 'Succeed' || to.name === 'PayError' || to.name === 'Payment') {
            setTimeout(() => {
              save()
            },2000)
          }
          this.timeSave = _this.throttling(save, 1000, true); //函数节流,保证每秒只调一次接口,以节省请求次数,减少带宽
          if(!result.result.isEndPage) {
            window.addEventListener('touchend', this.timeSave, false) // 移动端事件,点击事件结束即调板寸接口
          }else {
            window.removeEventListener('touchend', this.timeSave, false)
          }
        }else {
          window.removeEventListener('touchend', this.timeSave, false)
        }
      }else {
        events = []
        window.removeEventListener('touchend', this.timeSave, false)
      }
      next()
    })
  }

  throttling (fn, wait, immediate) {
    let timer;
    let context, args;

    let run = () => {
      timer=setTimeout(()=>{
        if(!immediate){
          fn.apply(context,args);
        }
        clearTimeout(timer);
        timer=null;
      },wait);
    }

    return function () {
      context=this;
      args=arguments;
      if(!timer){
        if(immediate){
          fn.apply(context,args);
        }
        run();
      }
    }
  }

  getDomain() { // 获取当前页面域名加页面路由名称,不带参数
    let url = window.location.href
    return url.split('://')[1].split('/#/')[0]
  }

}

export default vsr;

index.js

import Router from "../../router";
import vsr from "./vsr";

// const vsrKey = function (val) {
//   !sessionStorage.getItem('vsrKey') && sessionStorage.setItem('vsrKey', val)
// }

// 设置一个时间,长时间停留在页面上,请求发送取消
// const vsrKeyTime = function () {
//   let nowTime = new Date().getTime()
//   !sessionStorage.getItem('vsrKeyTime') && sessionStorage.setItem('vsrKeyTime', nowTime)
// }
// vsrKeyTime()

const vsrOpt = function (opt = {
  whiteList: [],
  version: '1.0.0'
}) {
  const VSR = new vsr(opt);
  VSR.vsrInit();
}

const install = function(Vue) {
  Vue.use(Router);
};



if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}

export default {
  install,
  vsrOpt,
  // vsrKey
};

代码
import VueScreenRecord from "./components/vue-screen-record";

if(finishTime() > 0) {
// VueScreenRecord.vsrOpt()
// Vue.use(VueScreenRecord);
}

直接引用即可实现,前端的录制。

剩下的就是后端保存了,然后需要B端配置需要录屏的页面,及回放了。

相关文章

网友评论

      本文标题:如何使用rrweb 做一个录屏系统?

      本文链接:https://www.haomeiwen.com/subject/uofjoltx.html