Screen recording implementation: rrweb.js Official document: github.com/rrweb-io/rr…

Screenshot implementation scheme: HTML2Canvas.js

Official document: html2canvas.hertzen.com/

Rrweb recording implementation of the principle is to record the changes in the DOM, the playback of dom operations, so it does not support the generation of MP4, can only be replayed in the Web end

The principle of html2Canvas screenshot implementation is to generate a canvas canvas, and the content of the canvas is realized by the plug-in. What we need to do is to save the canvas canvas as a Base64 picture and save it locally or upload it

Server example: Express

Files to be imported:

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>
Copy the code

Html2canvas (downloaded from the official website)

<script src="/javascripts/html2canvas.js"></script>
Copy the code

Here is an example of cross-page recording and capturing

The complete front-end code is as follows:

Page 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 %> <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> <! - don't support the IE11 below -- -- > < 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" > start recording < / button > < button class = "btn2" > end and play < / button > <button class="btn3" id="capture"> </button> <button class="btn4"> </button> <br> <br> <div>this is page1</div> <div> <input type="text" placeholder=" please input information "> </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(" start recording ") r = rrweb.record({emit(event) {// Put events into the events array events.push(event); console.log(events) }, }); } el2.onclick = function(){alert(" play ") r() new rrwebPlayer({target: document.body, // can define DOM element data: { events, }, }); } el3.onclick = function(){ html2canvas(document.querySelector("body")).then(canvas => { // document.body.appendChild(canvas) let pic = canvas.toDataURL("image/png"); DownloadFileByBase64 (PIC,' financial Test download ') // 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}, Function (response, XML){alert(response.msg) // console.log(events) window.open("/page2.html")}, error:function(status){ alert(response.msg) } }); 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) } }); 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)} / / native function ajax ajax methods (options) {var options = options | | {}; options.type=(options.type||'GET').toUpperCase(); options.dataType=options.dataType||'json'; var params=formatParams(options.data); Var XHR; // non-ie6 if(window.xmlhttprequest){XHR =new XMLHttpRequest(); }else{// Ie6 and later 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); }}} / / connection and send - the second step the if (options. Type = = 'GET') {XHR. Open (' GET 'options. The 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"); // Set the Content Type 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('&'); } // function save() {// const body = json.stringify ({events}); // events = []; // fetch('http://YOUR_BACKEND_API', { // method: 'POST', // headers: { // 'Content-Type': 'application/json', // }, // body, // }); // Call save every 10 seconds to avoid too many requests // setInterval(save, 10 * 1000); </script> </body> </html>Copy the code

Page 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 %> <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> <! - don't support the IE11 below -- -- > < 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" > start recording 2 < / button > < button class = "btn2" > end and 1 and 2 < / button > </button> <br> <br> <div> This is page2</div> <div> <input type="text" Placeholder = "please enter the information" > < / div > < script > window. The 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 (" start recording ") r = rrweb.record({emit(event) {// Store event in events array events.push(event); console.log(events) }, }); } el2.onclick = function(){alert(" play ") if(r){r()} ajax({url:'/saveEvents', type:'POST', dataType:'json', data:{events:JSON.stringify(events),finished:false}, // data:{name:1}, 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, // you can customize DOM elements 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,' financial Test download ') // 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)} / / native function ajax ajax methods (options) {var options = options | | {}; options.type=(options.type||'GET').toUpperCase(); options.dataType=options.dataType||'json'; var params=formatParams(options.data); Var XHR; // non-ie6 if(window.xmlhttprequest){XHR =new XMLHttpRequest(); }else{// Ie6 and later 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); }}} / / connection and send - the second step the if (options. Type = = 'GET') {XHR. Open (' GET 'options. The 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"); // Set the Content Type 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('&'); } // function save() {// const body = json.stringify ({events}); // events = []; // fetch('http://YOUR_BACKEND_API', { // method: 'POST', // headers: { // 'Content-Type': 'application/json', // }, // body, // }); // Call save every 10 seconds to avoid too many requests // setInterval(save, 10 * 1000); </script> </body> </html>Copy the code

Server code:

/* * @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. The send ({MSG: "successful"}) / / if (the req. Query. Events. Length > 0) {/ / res. Send ({state: 1, MSG: "uploaded successfully"}) / /} else {/ / Res. send({state:0, MSG :" parameter error, no message received "}) //}}); / / save app. Post ('/saveEvents' function (the req, res, Next) {// res.writehead (200,{' content-type ':'text/ HTML '}) console.log(req.body) res.send({state:1, MSG :" Upload successful "}) // 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. The send ({state: 1, MSG: "uploaded successfully"}) / /} else {/ / res. Send ({state: 0, MSG: "parameter error, not to record the screen information"}) / /}}); // Get app.post('/getEvents',function(req, res, next) {res.send(events); // Get 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 the req. Query. Events. Length > 0) {/ / res. Send ({state: 1, MSG: "uploaded successfully"}) / /} else {/ / res. Send ({state: 0, MSG: "parameter errors, "}) //}}); module.exports = app;Copy the code

If you just want to see the effect on the front end, just run the front end of the first page to see it.