前端實現錄屏、跨頁面錄屏、截屏功能


錄屏實現方案:rrweb.js
官方文檔:https://github.com/rrweb-io/rrweb

截屏實現方案:html2canvas.js

官方文檔:http://html2canvas.hertzen.com/

rrweb錄屏實現的原理是記錄dom的變化,播放的時候重新執行dom操作,所以不支持生成mp4,只可以在web端重放

html2canvas截圖實現的原理是生成一個canvas畫布,畫布的內容是插件幫我們實現好的,我們需要做的是將canvas畫布保存成base64圖片,保存到本地或者上傳

服務端例子:express

需要引入的文件:

rrweb:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"/>
<script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
html2canvas(從官網下載到本地的文件)
<script src="/javascripts/html2canvas.js"></script>

下面用一個例子演示一下跨頁面錄屏和截屏的效果

前端完整代碼如下:

頁面1:

<!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">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"
    />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"
    />
  </head>
  <body>
    <!-- 不支持 IE11 以下的瀏覽器 -->
    <script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
    <script src="/javascripts/html2canvas.js"></script>
    
    <button class="btn">開始錄制</button>
    <button class="btn2">結束並播放</button>
    <button class="btn3" id="capture">截圖</button>
    <button class="btn4">下一步</button>
    <button class="btn5">清空服務器數據</button>
    <br>
    <br>
    <div>this is page1</div>
    <div>
      <input type="text" placeholder="請輸入信息">
    </div>
    <script>
      window.onload = function(){
        let el = document.getElementsByClassName("btn")[0];
        let el2 = document.getElementsByClassName("btn2")[0];
        let el3 = document.getElementById("capture")
        let el4 = document.getElementsByClassName("btn4")[0];
        let el5 = document.getElementsByClassName("btn5")[0];
        let events = []
        let r
        el.onclick = function(){
          alert("開始錄制")
          r = rrweb.record({
            emit(event) {
              // 將 event 存入 events 數組中
              events.push(event);
              console.log(events)
            },
          });
        }
        el2.onclick = function(){
          alert("播放")
          r()
          new rrwebPlayer({
                target: document.body, // 可以自定義 DOM 元素
                data: {
                    events,
                },
            });

        }
        el3.onclick = function(){
            html2canvas(document.querySelector("body")).then(canvas => {
                // document.body.appendChild(canvas)
                let pic = canvas.toDataURL("image/png");
                downloadFileByBase64(pic,'金融測試下載')
                // window.location.href = pic;
                
            });
        }
        el4.onclick = function(){
          // console.log(JSON.stringify(events))
          // console.log(events.length)
          uploadEvents(1,events)
          
          return false
         
        }
        el5.onclick = function(){
          // console.log(JSON.stringify(events))
          // console.log(events.length)
          ajax({
            url:'/clearEvents',
            type:'POST',
            dataType:'json',
            data:{},
            // data:{name:1},
            success:function(response,xml){
                //請求成功后執行的代碼
              alert(response.msg)
              // console.log(events)
              window.open("/page2.html")
            },
            error:function(status){
              alert(response.msg)
            }
          });
          
          return false
         
        }
        
      }
      // 分批上傳
      function uploadEvents(m,events){
        console.log(JSON.stringify(events).length)
        ajax({
            url:'/saveEvents',
            type:'POST',
            dataType:'json',
            data:{events:JSON.stringify(events),page:1},
            // data:{name:1},
            success:function(response,xml){
                //請求成功后執行的代碼
              response = JSON.parse(response);
              // console.log(events)
              window.open("/page2.html")
            },
            error:function(status){
              alert(response.msg)
            }
          });
        
      }

      // base64生成圖片方法
      function dataURLtoBlob(dataurl) {
          var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
              bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
          while (n--) {
              u8arr[n] = bstr.charCodeAt(n);
          }
          return new Blob([u8arr], { type: mime });
      }

        function downloadFile(url,name='What\'s the fuvk'){
            var a = document.createElement("a")
            a.setAttribute("href",url)
            a.setAttribute("download",name)
            a.setAttribute("target","_blank")
            let clickEvent = document.createEvent("MouseEvents");
            clickEvent.initEvent("click", true, true);  
            a.dispatchEvent(clickEvent);
        }

        function downloadFileByBase64(base64,name){
            var myBlob = dataURLtoBlob(base64)
            var myUrl = URL.createObjectURL(myBlob)
            downloadFile(myUrl,name)
        }
       

        // 原生ajax方法
        function ajax(options){
          var options=options||{};
          options.type=(options.type||'GET').toUpperCase();
          options.dataType=options.dataType||'json';
          var params=formatParams(options.data);

        //創建-第一步
        var xhr;
        //非IE6
        if(window.XMLHttpRequest){
            xhr=new XMLHttpRequest();
        }else{
            //ie6及其以下版本瀏覽器
            xhr=ActiveXObject('Microsoft.XMLHTTP');
        }

        //接收-第三步
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4){
                var status=xhr.status;
                if(status>=200&&status<300){
                    options.success&&options.success(xhr.responseText,xhr.responseXML);
                }else{
                    options.error&&options.error(status);
                }
            }
        }

        //連接和發送-第二步
        if(options.type=='GET'){
            xhr.open('GET',options.url+'?'+params,true);
            xhr.send(null);
        }else if(options.type=='POST'){
            xhr.open('POST',options.url,true);
            //設置表單提交時的內容類型
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(params);
        }
    }

    //格式化參數
    function formatParams(data){
        var arr=[];
        for(var name in data){
            arr.push(encodeURIComponent(name)+'='+encodeURIComponent(data[name]));
        }
        // arr.push(('v='+Math.random()).replace('.',''));
        return arr.join('&');
    }


// save 函數用於將 events 發送至后端存入,並重置 events 數組
  // function save() {
  //   const body = JSON.stringify({ events });
  //   events = [];
  //   fetch('http://YOUR_BACKEND_API', {
  //     method: 'POST',
  //     headers: {
  //       'Content-Type': 'application/json',
  //     },
  //     body,
  //   });


// 每 10 秒調用一次 save 方法,避免請求過多
// setInterval(save, 10 * 1000);
    </script>
  </body>
</html>

頁面2:

<!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">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.css"
    />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css"
    />
  </head>
  <body>
    <!-- 不支持 IE11 以下的瀏覽器 -->
    <script src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
    <script src="/javascripts/html2canvas.js"></script>
    
    <button class="btn">開始錄制2</button>
    <button class="btn2">結束並播放1和2</button>
    <button class="btn3" id="capture">截圖2</button>
    <br>
    <br>
    <div>this is page2</div>
    <div>
      <input type="text" placeholder="請輸入信息">
    </div>
    <script>
      window.onload = function(){
        let el = document.getElementsByClassName("btn")[0];
        let el2 = document.getElementsByClassName("btn2")[0];
        let el3 = document.getElementById("capture")
        console.log(el)
        let events = []
        let r
        el.onclick = function(){
          alert("開始錄制")
          r = rrweb.record({
            emit(event) {
              // 將 event 存入 events 數組中
              events.push(event);
              console.log(events)
            },
          });
        }
        el2.onclick = function(){
          alert("播放")
          
          if(r){
            r()
          }
          
          ajax({
            url:'/saveEvents',
            type:'POST',
            dataType:'json',
            data:{events:JSON.stringify(events),finished:false},
            // data:{name:1},
            success:function(response,xml){
                //請求成功后執行的代碼

              // console.log(events)
                ajax({
                    url:'/getEvents',
                    type:'POST',
                    dataType:'json',
                    data:{},
                    success:function(response,xml){
                        //請求成功后執行的代碼
                        response = JSON.parse(response);
                        events = response;
                        console.log(response)
                        new rrwebPlayer({
                            target: document.body, // 可以自定義 DOM 元素
                            data: {
                                events,
                            },
                        });
                    },
                    error:function(status){
                    alert(response.msg)
                    }
                });
            },
            error:function(status){
              alert(response.msg)
            }
          });
          
          
        }
        el3.onclick = function(){
            html2canvas(document.querySelector("body")).then(canvas => {
                // document.body.appendChild(canvas)
                let pic = canvas.toDataURL("image/png");
                downloadFileByBase64(pic,'金融測試下載')
                // window.location.href = pic;
                
            });
        }
      }

      function dataURLtoBlob(dataurl) {
          var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
              bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
          while (n--) {
              u8arr[n] = bstr.charCodeAt(n);
          }
          return new Blob([u8arr], { type: mime });
      }

        function downloadFile(url,name='What\'s the fuvk'){
            var a = document.createElement("a")
            a.setAttribute("href",url)
            a.setAttribute("download",name)
            a.setAttribute("target","_blank")
            let clickEvent = document.createEvent("MouseEvents");
            clickEvent.initEvent("click", true, true);  
            a.dispatchEvent(clickEvent);
        }

        function downloadFileByBase64(base64,name){
            var myBlob = dataURLtoBlob(base64)
            var myUrl = URL.createObjectURL(myBlob)
            downloadFile(myUrl,name)
        }
      

        // 原生ajax方法
        function ajax(options){
          var options=options||{};
          options.type=(options.type||'GET').toUpperCase();
          options.dataType=options.dataType||'json';
          var params=formatParams(options.data);

        //創建-第一步
        var xhr;
        //非IE6
        if(window.XMLHttpRequest){
            xhr=new XMLHttpRequest();
        }else{
            //ie6及其以下版本瀏覽器
            xhr=ActiveXObject('Microsoft.XMLHTTP');
        }

        //接收-第三步
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4){
                var status=xhr.status;
                if(status>=200&&status<300){
                    options.success&&options.success(xhr.responseText,xhr.responseXML);
                }else{
                    options.error&&options.error(status);
                }
            }
        }

        //連接和發送-第二步
        if(options.type=='GET'){
            xhr.open('GET',options.url+'?'+params,true);
            xhr.send(null);
        }else if(options.type=='POST'){
            xhr.open('POST',options.url,true);
            //設置表單提交時的內容類型
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.send(params);
        }
    }

    //格式化參數
    function formatParams(data){
        var arr=[];
        for(var name in data){
            arr.push(encodeURIComponent(name)+'='+encodeURIComponent(data[name]));
        }
        // arr.push(('v='+Math.random()).replace('.',''));
        return arr.join('&');
    }

// save 函數用於將 events 發送至后端存入,並重置 events 數組
  // function save() {
  //   const body = JSON.stringify({ events });
  //   events = [];
  //   fetch('http://YOUR_BACKEND_API', {
  //     method: 'POST',
  //     headers: {
  //       'Content-Type': 'application/json',
  //     },
  //     body,
  //   });


// 每 10 秒調用一次 save 方法,避免請求過多
// setInterval(save, 10 * 1000);
    </script>
  </body>
</html>

服務端代碼:

/*
 * @Author: your name
 * @Date: 2020-07-18 19:09:47
 * @LastEditTime: 2020-07-19 12:24:16
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /record-2/app.js
 */ 
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var fs = require('fs');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var bodyParser = require('body-parser');
var app = express();

app.use(bodyParser.urlencoded({
  extended:true
}));
let events = [];
// view engine setup
app.use(express.static(path.join(__dirname, 'public')));
// 服務端頁面
app.get('/', function(req, res, next) {
    // res.writeHead(200,{'Content-Type':'text/html'})
    fs.readFile('./html/index.html','utf-8',function(err,data){
        if(err){
        throw err ;
        }
        res.send(data);
        });
});
app.get('/page2.html', function(req, res, next) {
  // res.writeHead(200,{'Content-Type':'text/html'})
  fs.readFile('./html/page2.html','utf-8',function(err,data){
      if(err){
      throw err ;
      }
      res.send(data);
      });
});
app.post('/clearEvents',function(req, res, next) {
  // res.writeHead(200,{'Content-Type':'text/html'})
  console.log(req.body)
  events = [];
  res.send({msg:"操作成功"})
  // if(req.query.events.length>0){
  //   res.send({state:1,msg:"上傳成功"})
  // }else{
  //   res.send({state:0,msg:"參數錯誤,未獲取到錄屏信息"})
  // }
});
//
app.post('/saveEvents',function(req, res, next) {
  // res.writeHead(200,{'Content-Type':'text/html'})
  console.log(req.body)
  res.send({state:1,msg:"上傳成功"})
  // res.send(req.body.events)
  if(req.body.page==1){
    events = []
  }
  events = events.concat(JSON.parse(req.body.events))
  fs.writeFile("./data.txt",JSON.stringify(req.body.events),'utf-8',function(){
    
  })
  // if(req.query.events.length>0){
  //   res.send({state:1,msg:"上傳成功"})
  // }else{
  //   res.send({state:0,msg:"參數錯誤,未獲取到錄屏信息"})
  // }
});
//
app.post('/getEvents',function(req, res, next) {
  res.send(events);
  // fs.readFile('./data.txt','utf-8',function(err,data){
  //   if(err){
  //     throw err ;
  //   }
  //   console.log(JSON.parse(data))
  //   res.send(data);
  // });
  // if(req.query.events.length>0){
  //   res.send({state:1,msg:"上傳成功"})
  // }else{
  //   res.send({state:0,msg:"參數錯誤,未獲取到錄屏信息"})
  // }
});

module.exports = app;

上面的例子如果只想在前端看效果的話,只運行第一個頁面的前端代碼也能查看。

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM