When publishing videos, we often need to upload thumbnails of videos at the same time. Recently, at the request of product managers, we need to do a dynamic function of publishing videos. My first reaction is to think of H5 tags vidio and canvas. Here I record the process of completing this function:
First, the overall idea is to create a video, then create a canvas and a brush, call the drawImage method of the brush, and take video as a parameter, you will draw a thumbnail of the video.
Say nothing about the code:
function creatImg() { const video = document.getElementById('videoPreview'); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const imgHeight = video.videoHeight; const imgWidth = video.videoWidth; ctx.drawImage(video, 0, 0, imgWidth, imgHeight); const imgSrc = canvas.toDataURL('image/png'); console.log(imgSrc); }
This method is called in the successful callback function of uploading video to send ajax.
// Upload video $('#video').on('change', (e) => { const video = e.currentTarget.files[0]; const formData = new FormData(); formData.append('file', video); $.ajax({ url: videourl, crossDomain: true, data: formData, dataType: 'json', method: 'POST', contentType: false, processData: false, timeout: 0, }).done((jsonData) => { //src operation for setting video label setTimeout(() => { creatImg(); }, 500); }).fail(() => { layer.alert('Upload failure', { shift: 5 }); }) });
That's the way it works... How can you make yourself feel better, kidding? After running, the following bug s were reported:
This bug means that dirty canvas will not be introduced. The reason for this problem is that the video objects referenced in canvas are cross-domain, which pollutes canvas. The solution is to introduce local video objects into canvas. The way to solve this problem is simply to change the location of the reference of creatImage, and get the video object in the input tag array of type file, where the V object is. Ideo objects are local, so cross-domain phenomena do not occur, as shown in the following code:
$('#video').on('change', (e) => { const video = e.currentTarget.files[0]; creatImg(video); // const videoName = video.name; const formData = new FormData(); formData.append('file', video); const index = layer.load(2); $.ajax({ url: `${upload_url}/toServer`, crossDomain: true, data: formData, dataType: 'json', method: 'POST', contentType: false, processData: false, timeout: 0, }).done((jsonData) => { //This is the relevant operation of the src attribute value of video }).fail(() => { layer.alert('Upload failure', { shift: 5 }); }) });
Const video = E. current Target. files [0]; here the video is the local video object. Next, let's optimize the creatImg function, as shown in the following code:
function creatImg(stream) { const video = document.createElement('video'); video.addEventListener('loadedmetadata', function loadedmetadata() { setTimeout(() => { const canvas = document.createElement('canvas'); canvas.width = this.videoWidth; canvas.height = this.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(this, 0, 0); const image = new Image(); image.src = canvas.toDataURL('image/png'); image.onload(() => { const formData = new FormData(); formData.append('file', dataURItoBlob(canvas.toDataURL('image/png')), `${+new Date()}.png`); $.ajax({ url: upload_url, crossDomain: true, data: formData, dataType: 'json', method: 'POST', contentType: false, processData: false, }).done((jsonData) => { //..... //Store the value in the value attribute in the input tag with id thumbnail const body = jsonData.body; $('#thumbnail').val(body); }).fail(() => { layer.alert('Upload failure'); }); }); }, 300); }, false); video.src = URL.createObjectURL(stream); video.play(); }
Create canvas, create brushes, and transfer video objects into and out of the picture. This series of operations are completed after the video is loaded. Therefore, all operations are done in the callback function of loademetadata monitored by videos. After the picture is painted, the picture needs to be uploaded to the server. FormData objects are used to generate the key value of name: value for ajax to send to the server. Yes, adding key-value pairs to FormData is the first parameter field name of the append method. The second parameter is the field value. The field value here is a Blob object. The third parameter is the file name. The method of creating a Blob here is the data URI to Blob method, as shown in the code below, because the method of canvas.toDataURL returns the encoding of Base64 of the picture. So the purpose of this function is to convert the encoding of Base64 into Blob objects, as shown in the code.
function dataURItoBlob(dataURI) { const binary = atob(dataURI.split(',')[1]); const array = []; for (let i = 0; i < binary.length; i += 1) { array.push(binary.charCodeAt(i)); } return new Blob([new Uint8Array(array)], { type: 'image/png' }); }
This paper first calls the method of atob of window s, decodes the compiled data of Base64 into binary data, then calls the charCodeAt method of binary object to unicode the binary data at the designated location, and stores all the encoded data in the array. Finally, an object of Unit8Array is created according to the array, which is unsigned. 8-bit integer, because the first parameter when initializing a Blob must be an array, the 8-bit unsigned integer is stored in the array.
Summary: In the process of completing this function, several important points of knowledge are involved. Listed below, I will introduce them one by one in the following related articles.
1. The videos of canvas must be of the same domain. If videos cross the domain, they will pollute canvas.
2. Several important objects and methods are needed when sending pictures to the server.
FormData objects: Objects that allow XMLHttpRequest to send key-value pairs
window.atob method: decode Base64 data into binary data
binary.charCodeAt method: unicode encoding binary data
Uint8Array object: Create an unsigned eight-digit integer by passing in an array
Blob objects: immutable, raw data-like file objects