前端录屏工具开发--可用于工程化错误回放

rrweb

https://github.com/rrweb-io/rrweb/blob/master/guide.zh_CN.md

html页面直接引入,代码如下:

<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>rrweb demo web site</title>
    <script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
    <script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/record/rrweb-record.min.js"></script>
    <link rel="stylesheet" crossorigin="anonymous"
          href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css" />
    <script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
</head><body>
<div style="float: left;">
    <h1>这里模拟一些html代码:</h1>
    <label>用户名</label><input type="text" style="height: 30px;width: 200px;">
    <label>密码</label><input type="password" style="height: 30px;width: 200px;">
    <label>邮箱</label><input type="email" style="height: 30px;width: 200px;">
    <label>备注</label><textarea style="height: 80px;width: 200px;"></textarea>
</div>

<div style="float: left;">
    <h1>模拟背景颜色,用来检验录屏画质</h1>
    <div style="width: 100%;height: 50px;background-color: red;"></div>
    <div style="width: 100%;height: 50px;background-color: green;"></div>
    <div style="width: 100%;height: 50px;background-color: #ff50d0;"></div>
    <div style="width: 100%;height: 50px;background-color: #7eff1f;"></div>
</div>
<p></p>
<div style="float: left;">
    <h1>视频回放</h1>
    <div id="playback" style="width: 1000px;height: 500px;background-color: #cccccc"></div>
</div>
<button onclick="get_start()">点击录制</button>
<button onclick="playback()">点击播放</button>
</body><script>
    //1.存放DOM节点数据
    let events = [];
    //2.点击录制
    function get_start() {
        //rrwebMin 为启动对象
        //record() 方法启动录制
        //emit会监听所有的DOM的动作, 鼠标等,
        rrwebMin.record({
            emit(event) {
                // 用任意方式存  储 event
                console.log(event);
                events.push(event);
            },
        });
        alert('正在录制')
    }
    //3.这一步,应该把数据转为JSON ,然后发送到后台存储,可以写个循环,10S发送一次,再定义一个窗口关闭事件监听,最后关闭时发送一次数据
    //SON.stringify({ events });

    //4.点击回放
    function playback() {
        new rrwebPlayer({
            target: document.getElementById('playback'), // 可以自定义 DOM 元素
            data: {events,},
        });
    }
</script>
</html>
  • 打开index.html,点击录制开始录制
  • 点击播放开始回访
  • 这里关键数据就是events,他可以将操作动作回放出来,并且可以将它保存在数据库中

代码效果

在这里插入图片描述

vue

<template>
  <div class="main">
    <div class="container">
      <div class="row">
        <div class="col-25">
          <label for="fname">First Name</label>
        </div>
        <div class="col-75">
          <input type="text" name="firstname" placeholder="Your name.." />
        </div>
      </div>
      <div class="row">
        <div class="col-25">
          <label for="lname">Last Name</label>
        </div>
        <div class="col-75">
          <input type="text" name="lastname" placeholder="Your last name.." />
        </div>
      </div>
      <div class="row">
        <div class="col-25">
          <label for="country">Country</label>
        </div>
        <div class="col-75">
          <select id="country" name="country">
            <option value="australia">Australia</option>
            <option value="canada">Canada</option>
            <option value="usa">USA</option>
          </select>
        </div>
      </div>
      <div class="row">
        <div class="col-25">
          <label for="subject">Subject</label>
        </div>
        <div class="col-75">
          <textarea
                  id="subject"
                  name="subject"
                  placeholder="Write something.."
                  style="height: 200px"
          ></textarea>
        </div>
      </div>
      <div class="row">
        <input type="submit" value="Submit" />
      </div>
    </div>
    <div class="rr-block action">
      <div class="record">
        <button class="btn-record" @click="startRecord()">开始录屏</button>
        <button
                class="btn-replay"
                :disabled="events.length < 2"
                @click="playRecord()"
        >
          回放录屏
        </button>
      </div>
      <div class="reset">
        <button @click="resetRecord()">重置录屏</button>
         <button @click="saveRecord()">保存录屏</button>
<!--         <button @click="replaySavedRecord()">重置保存录屏</button>-->
      </div>
    </div>

    <div id="rrweb" class="replay"></div>
  </div>
</template>
<script>
  import RrwebReplay from 'rrweb-player'
  import { record } from 'rrweb'
  export default {
    data() {
      return {
        events: [],
        recordFlag: false,
      }
    },
    methods: {
      startRecord() {
        this.recordFlag = true
        const self = this
        record({
          emit(event) {
            if (self.recordFlag) {
              self.events.push(event)
            }
          },
        })
      },
      playRecord() {
        const rrwebEl = document.getElementById('rrweb')
        // eslint-disable-next-line no-new
        new RrwebReplay({
          target: rrwebEl,
          data: {
            events: this.events,
            autoPlay: true,
          },
        })
      },
      resetRecord() {
        this.recordFlag = false
        this.events = []
        const replayEl = document.getElementsByClassName('rr-player')[0]
        if (replayEl) replayEl.remove()
      },
      saveRecord() {
        const data = JSON.stringify(this.events)
        window.localStorage.setItem('rrweb', data)
      },
      replaySavedRecord() {
        const replayEl = document.getElementsByClassName('rr-player')[0]
        if (replayEl) replayEl.remove()
        const rrwebEl = document.getElementById('rrweb')
        const savedEvents = window.localStorage.getItem('rrweb')
        // eslint-disable-next-line no-new
        new RrwebReplay({
          target: rrwebEl,
          data: {
            events: JSON.parse(savedEvents),
            autoPlay: true,
          },
        })
      },
    },
  }
</script>
<style>
  .main {
    position: relative;
  }
  .replay {
    top: 0;
    left: 200px;
    position: absolute;
  }
  .action {
    padding-top: 20px;
  }
  .reset {
    padding-top: 20px;
  }
  .container {
    max-width: 1000px;
  }
  * {
    box-sizing: border-box;
  }

  input[type='text'],
  select,
  textarea {
    width: 100%;
    padding: 12px;
    border: 1px solid #ccc;
    border-radius: 4px;
    resize: vertical;
  }

  label {
    padding: 12px 12px 12px 0;
    display: inline-block;
  }

  input[type='submit'] {
    background-color: #4caf50;
    color: white;
    padding: 12px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    float: right;
  }

  input[type='submit']:hover {
    background-color: #45a049;
  }

  .container {
    border-radius: 5px;
    background-color: #f2f2f2;
    padding: 20px;
  }

  .col-25 {
    float: left;
    width: 25%;
    margin-top: 6px;
  }

  .col-75 {
    float: left;
    width: 75%;
    margin-top: 6px;
  }

  /* Clear floats after the columns */
  .row:after {
    content: '';
    display: table;
    clear: both;
  }

  .rr-controller.svelte-dxnc1j.svelte-dxnc1j {
    width: 100%;
    height: 80px;
    background: #fff;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    border-radius: 0 0 5px 5px;
  }
  .rr-timeline.svelte-dxnc1j.svelte-dxnc1j {
    width: 80%;
    display: flex;
    align-items: center;
  }
  .rr-timeline__time.svelte-dxnc1j.svelte-dxnc1j {
    display: inline-block;
    width: 100px;
    text-align: center;
    color: #11103e;
  }
  .rr-progress.svelte-dxnc1j.svelte-dxnc1j {
    flex: 1;
    height: 12px;
    background: #eee;
    position: relative;
    border-radius: 3px;
    cursor: pointer;
    box-sizing: border-box;
    border-top: solid 4px #fff;
    border-bottom: solid 4px #fff;
  }
  .rr-progress.disabled.svelte-dxnc1j.svelte-dxnc1j {
    cursor: not-allowed;
  }
  .rr-progress__step.svelte-dxnc1j.svelte-dxnc1j {
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    background: #e0e1fe;
  }
  .rr-progress__handler.svelte-dxnc1j.svelte-dxnc1j {
    width: 20px;
    height: 20px;
    border-radius: 10px;
    position: absolute;
    top: 2px;
    transform: translate(-50%, -50%);
    background: rgb(73, 80, 246);
  }
  .rr-controller__btns.svelte-dxnc1j.svelte-dxnc1j {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
  }
  .rr-controller__btns.svelte-dxnc1j button.svelte-dxnc1j {
    width: 32px;
    height: 32px;
    display: flex;
    padding: 0;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    border-radius: 50%;
    cursor: pointer;
  }
  .rr-controller__btns.svelte-dxnc1j button.svelte-dxnc1j:active {
    background: #e0e1fe;
  }
  .rr-controller__btns.svelte-dxnc1j button.active.svelte-dxnc1j {
    color: #fff;
    background: rgb(73, 80, 246);
  }
  .rr-controller__btns.svelte-dxnc1j button.svelte-dxnc1j:disabled {
    cursor: not-allowed;
  }
  .replayer-wrapper {
    position: relative;
  }
  .replayer-mouse {
    position: absolute;
    width: 20px;
    height: 20px;
    transition: 0.05s linear;
    background-size: contain;
    background-position: 50%;
    background-repeat: no-repeat;
    background-image: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMwMCIgd2lkdGg9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBkYXRhLW5hbWU9IkxheWVyIDEiIHZpZXdCb3g9IjAgMCA1MCA1MCI+PHBhdGggZD0iTTQ4LjcxIDQyLjkxTDM0LjA4IDI4LjI5IDQ0LjMzIDE4YTEgMSAwIDAwLS4zMy0xLjYxTDIuMzUgMS4wNmExIDEgMCAwMC0xLjI5IDEuMjlMMTYuMzkgNDRhMSAxIDAgMDAxLjY1LjM2bDEwLjI1LTEwLjI4IDE0LjYyIDE0LjYzYTEgMSAwIDAwMS40MSAwbDQuMzgtNC4zOGExIDEgMCAwMC4wMS0xLjQyem0tNS4wOSAzLjY3TDI5IDMyYTEgMSAwIDAwLTEuNDEgMGwtOS44NSA5Ljg1TDMuNjkgMy42OWwzOC4xMiAxNEwzMiAyNy41OEExIDEgMCAwMDMyIDI5bDE0LjU5IDE0LjYyeiIvPjwvc3ZnPg==');
  }
  .replayer-mouse:after {
    content: '';
    display: inline-block;
    width: 20px;
    height: 20px;
    border-radius: 10px;
    background: #4950f6;
    transform: translate(-10px, -10px);
    opacity: 0.3;
  }
  .replayer-mouse.active:after {
    animation: click 0.2s ease-in-out 1;
  }
  .replayer-mouse-tail {
    position: absolute;
    pointer-events: none;
  }
  @keyframes click {
    0% {
      opacity: 0.3;
      width: 20px;
      height: 20px;
      border-radius: 10px;
      transform: translate(-10px, -10px);
    }
    50% {
      opacity: 0.5;
      width: 10px;
      height: 10px;
      border-radius: 5px;
      transform: translate(-5px, -5px);
    }
  }
  .rr-player {
    position: relative;
    background: white;
    float: left;
    border-radius: 5px;
    box-shadow: 0 24px 48px rgba(17, 16, 62, 0.12);
  }
  .rr-player__frame {
    overflow: hidden;
  }
  .replayer-wrapper {
    float: left;
    clear: both;
    transform-origin: top left;
    left: 50%;
    top: 50%;
  }
  .replayer-wrapper > iframe {
    border: none;
  }
  .switch.svelte-1mmdovf.svelte-1mmdovf {
    height: 1em;
    display: flex;
    align-items: center;
  }
  .switch.disabled.svelte-1mmdovf.svelte-1mmdovf {
    opacity: 0.5;
  }
  .label.svelte-1mmdovf.svelte-1mmdovf {
    margin: 0 8px;
  }
  .switch.svelte-1mmdovf input[type='checkbox'].svelte-1mmdovf {
    position: absolute;
    opacity: 0;
  }
  .switch.svelte-1mmdovf label.svelte-1mmdovf {
    width: 2em;
    height: 1em;
    position: relative;
    cursor: pointer;
    display: block;
  }
  .switch.disabled.svelte-1mmdovf label.svelte-1mmdovf {
    cursor: not-allowed;
  }
  .switch.svelte-1mmdovf label.svelte-1mmdovf:before {
    content: '';
    position: absolute;
    width: 2em;
    height: 1em;
    left: 0.1em;
    transition: background 0.1s ease;
    background: rgba(73, 80, 246, 0.5);
    border-radius: 50px;
  }
  .switch.svelte-1mmdovf label.svelte-1mmdovf:after {
    content: '';
    position: absolute;
    width: 1em;
    height: 1em;
    border-radius: 50px;
    left: 0;
    transition: all 0.2s ease;
    box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3);
    background: #fcfff4;
    animation: switch-off 0.2s ease-out;
    z-index: 2;
  }
  .switch
  input[type='checkbox']:checked
  + label.svelte-1mmdovf.svelte-1mmdovf:before {
    background: rgb(73, 80, 246);
  }
  .switch
  input[type='checkbox']:checked
  + label.svelte-1mmdovf.svelte-1mmdovf:after {
    animation: switch-on 0.2s ease-out;
    left: 1.1em;
  }
</style>

这里保存录像,可以采用setInterval,每个多少秒上传一次

代码效果

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值