적절한 크기의 사진을 업로드하면 사용자는 렌더링 애니메이션의 효과와 음악을 선택하여 슬라이드쇼와 유사한 효과를 미리 볼 수 있으며 마지막으로 클릭하여 확인하면 헤드라인이나 Douyin에서 재생할 수 있는 비디오가 생성됩니다.
비디오 생성을 위한 가능한 솔루션순수 프런트엔드 비디오 인코딩 변환(예: WebM Encoder Whammy)
구현을 위해 이미지의 각 프레임을 백엔드에 전달하고, 백엔드는 비디오 트랜스코딩을 위해 FFmpeg를 호출합니다.
이미지 생성은 캔버스 기본 인터페이스 toDataURL을 통해 수행할 수 있으며 궁극적으로 이미지 데이터를 base64 형식으로 반환합니다.
function generatePng() { var canvas = document.createElement('canvas'); let icavas = '#canvas' //애니메이션 렌더링을 위한 캔버스 ID if (wrapWidth == 2) { icavas = '#verticalCanvas' } var canvasNode = document .querySelector(icavas) canvas.width = canvasNode.width; canvas.height = canvasNode.height = canvas.getContext('2d'); ctx.drawImage(canvasNode, 0, 0); var imgData = canvas.toDataURL(image/png); 캔버스 애니메이션의 스크린샷을 찍는 방법setInterval을 사용하여 이미지 생성 방법을 정기적으로 실행합니다. 물론 requestAnimationFrame을 사용할 수도 있습니다.
setInterval(function() { imgsTemp.push(generatePng())}, 1000/60) 백엔드에서 각 이미지 프레임을 가져오는 방법해결 방법 1: 헤드리스 브라우저는 프런트 엔드 캔버스 애니메이션 js를 실행한 다음 js의 스크린샷을 찍습니다.
초기 아이디어:
스크린샷은 console.log를 사용하여 인쇄됩니다. 캔버스 스크린샷은 15초 애니메이션인 base64 형식이며, 서버 충돌(거부)을 직접적으로 초래한 스크린샷이 100개 이상 있습니다.
시험 실행 계획:
스크린샷은 js 변수에 저장되며, 애니메이션이 재생된 후 페이지에 로고가 추가되고 백엔드에서 해당 변수를 가져옵니다.
const 페이지 = { imageZoomOut: import ('./image_zoom_inout.js'), //이미지 아트 확대/축소: import ('./image_art.js'), //이미지 그리드 지우기: import ('./image_grid.js'), / /Grid imageRotate: import ('./image_rotate.js'), //imageFlash 열기 및 닫기: import ('./image_flash.js'), //이미지 및 텍스트 플래시 imageVerticalArt: import ('./image_vertical_art.js'), //세로 삭제 imageVerticalGrid: import ('./image_vertical_grid.js'), //세로 그리드 imageVerticalRotate: import ('. /image_vertical_rotate.js '), //세로 열기 및 닫기 imageVerticalFlash: import ('./image_vertical_flash.js'), //세로 이미지 및 텍스트 플래시 imageVerticalZoomOut: import ('./image_vertical_zoom_inout.js'), //세로 확대 imageVertical: import ('./image_vertical.js'), //세로 버전 일반};var isShow = falsevar imgsBase64 = []var imgsTemp = []var cutInter = nullvar imgsTimeLong = 0function getQuerys(tag) { let queryStr = window.location.search.slice(1); let queryArr = queryStr.split('&'); let spec = {} for (let i = 0, len = queryArr.length; i < len ; i++) { let queryItem = queryArr[i].split('='); let qitem = decodeURIComponent(queryItem[1]) if (queryItem[0] == tag) { query.push(qitem); } else { spec[queryItem[0]] = qitem } } return { list: query, spec: spec };}var getQuery = getQuerys('images')var effectTag = getQuery.spec. tidvar WrapWidth = getQuery.spec.templateTypelet num = 0let imgArr = []function creatImg() { var 이미지 = getQuery.list let newImg = [] let vh = WrapWidth == 1 ? 360 : 640 let vw = WrapWidth == 1 ? 640 : 360 if ( effectTag.indexOf('Flash') > -1) { Images.map(function (항목, 색인) { if (11 === 색인 || 13 === 색인 || 16 === 색인) { var temp = new Image(vw, vh) temp.setAttribute('crossOrigin', 'anonymous'); temp.src = item; newImg.push(temp) } else { newImg.push(item) } }) imgArr = newImg renderAnimate( effectTag) } else { image.map(function(item) { var temp = new Image(vw, vh) temp.setAttribute('crossOrigin', 'anonymous'); temp.src = item; temp.onload = function() { num++ if (num == Images.length) { renderAnimate(효과Tag) } } newImg.push(temp) }) imgArr = newImg }}async function renderAnimate(page) { //creatImg() 기다림 let me = this const pageA = 페이지 대기[페이지]; let oldDate = new Date().getTime() let icavas = '#canvas' if (wrapWidth == 2) { icavas = '#verticalCanvas' } let innerCanvas = document.querySelector(icavas) isShow = false pageA[page].render(null, { canvas: innerCanvas, Images: imgArr }, function() { //애니메이션 재생 후 isShow = true; imgsTemp.push(generatePng()) imgsBase64.push(imgsTemp) let now = new Date().getTime() window.imgsTimeLong = now - oldDateclearInterval(cutInter) document.getElementById('cutImg').innerHTML = ' done'//페이지 식별}) cutInter = setInterval(function() { imgsTemp.push(generatePng()) if (imgsTemp.length >= 50) { imgsBase64.push(imgsTemp) imgsTemp = [] } }, 130)}function getImgs() { return imgsBase64}function generatePng() { var canvas = document.createElement('canvas'); let icavas = '#canvas' if (wrapWidth == 2) { icavas = '#verticalCanvas' } var canvasNode = document.querySelector(icavas) canvas.width = canvasNode.width; canvas.height = canvasNode.height; ;ctx.drawImage(canvasNode, 0, 0); canvas.toDataURL(image/png); return imgData;}window.imgsBase64 = imgsBase64 //스크린샷 저장 변수 creatImg()시범 운영 계획의 단점:
var temp = new Image(vw, vh)temp.setAttribute('crossOrigin', 'anonymous'); 최종 솔루션: NODE 측에서 애니메이션 실행 node-canvas를 사용하여 fs.writeFile
const { createCanvas, loadImage} = require(canvas); const 페이지 = { imageZoomOut: require('./image_zoom_inout.js'), //이미지 아트 확대: require('./image_art.js'), //이미지 그리드 삭제: require('./image_grid.js'), //그리드 imageRotate: require('./image_rotate.js'), //imageFlash 열기 및 닫기: require('./image_flash.js'), //이미지 및 텍스트 플래시 imageVerticalArt: require('./image_vertical_art.js'), //세로 삭제 imageVerticalGrid: require('./image_vertical_grid . js'), //수직 그리드 imageVerticalRotate: require('./image_vertical_rotate.js'), //수직 그리드 imageVerticalFlash: require('./image_vertical_flash.js'), //세로 이미지 및 텍스트 플래시 imageVerticalZoomOut: require('./image_vertical_zoom_inout.js'), //세로 확대 imageVertical: require('./image_vertical.js'), // 수직 버전의 일반 사항};const fs = require(fs);const querystring = require('querystring');let args = process.argv && process.argv[2]letparse = querystring.parse(args)let vh =parse.templateType == 1 ? 720 : 1280 //캔버스 높이 let vw =parse.templateType == 1280 : 720 //캔버스 너비 let imgSrcArray = pars.images //이미지 배열 let effectTag = pars.tid //애니메이션 효과 let saveImgPath = process.argv && process.argv[3]let loadArr = []imgSrcArray.forEach(element => { if (//.(jpg|jpeg|png|JPG|PNG)$/.test(element)) { loadArr.push(loadImage(element)) } else { loadArr.push(element) }});const 캔버스 = createCanvas(vw, vh);const ctx = canvas.getContext(2d);Promise.all(loadArr) .then((images) => { //애니메이션 초기화 console.log('Start animation') let oldDate = new Date ().getTime() 페이지[효과Tag].render(null, { 캔버스: 캔버스, 이미지: 이미지 }, function() { clearInterval(interval) let now = new Date().getTime() console.log(now - oldDate, '애니메이션 종료') }) const 간격 = setInterval( (function() { let x = 0; return () => { x += 1; ctx. canvas.toDataURL('image/jpeg', function(err, png) { if (err) { console.log(err); return; } let data = png.replace(/^data:image///w+;base64,/, ''); let buf = new Buffer(data, 'base64'); ${saveImgPath}${x}.jpg `, buf, {}, (err) => { console.log(x, err) }) }); 1000 / 60 ); }) .catch(e => { console.log(e); });iterm에서 다음 명령을 실행하십시오.
노드 testCanvas.js 'tid=imageArt&templateType=1&images=../assets/imgs/8.png&images=../assets/imgs/6.png&images=../assets/imgs/7.png&images=../assets/imgs/6.png&images =../엉덩이 ets/imgs/8.png&images=../assets/imgs/7.png&images=../assets/imgs/4.png&images=../assets/imgs/6.png&images=../assets/imgs/8. png&이미지=../assets/imgs/7.png' './이미지/'
매개변수 설명:
1) tid는 애니메이션 이름입니다.
2) 템플릿 유형은 크기: 1:1280*720; 2:720*1280
3) 이미지는 이미지 주소입니다.
4) './images/' 변수는 스크린샷이 저장된 주소이고,
NODE 환경에서 실행할 때의 단점다음 그림은 13초마다 반복됩니다.
for (var A = 0; 50 > A; A++) p.beginPath(), p.globalAlpha = 1 - A / 49, p.save(), p.arc(180,320,P + 2 * A, 0, 2 * Math.PI), p.clip(), p.drawImage(x[c], 0, 0, y.width, y.height), p.restore(), p.closePath(); for (var S = 0; 50 > S; S++) p.beginPath(), p.globalAlpha = 1 - S / 49, p.save( ), p.ret(0, 0, d + P + 2 * S, g + b + 2 * S), p.clip(), p.drawImage(x[c], 0, 0, y.너비, y.높이), p.restore(), p.closePath();
Node.js의 이벤트 루프 모델 때문에 Node.js의 사용은 Node.js의 주기가 항상 실행될 수 있도록 보장해야 합니다. 시간이 많이 걸리는 함수가 나타나면 이벤트 루프가 멈춰 처리할 수 없게 됩니다. 시간이 지나면 다른 작업이 완료되므로 결과적으로 일부 애니메이션은 여전히 느립니다.
추후 최적화 가능성Go 언어를 사용하여 스크린샷을 찍어보세요.
캔버스 애니메이션을 다시 작성합니다.
추가의 비디오 비트 전송률비디오 비트 전송률은 데이터 전송 중 단위 시간당 전송되는 데이터 비트 수입니다. 일반적으로 우리가 사용하는 단위는 초당 수천 비트입니다. 간단히 이해하면 샘플링 속도는 단위 시간당 샘플링 속도가 클수록 정확도가 높아지고 처리된 파일이 원본 파일에 가까워집니다. 예를 들어, 오디오의 경우 비트 전송률이 높을수록 압축률은 낮고 음질 손실은 작아지며 음질이 오디오 소스에 가까워집니다.
초당 FPS 프레임(초당 프레임 수)
FPS는 그래픽 분야의 정의로, 초당 전송되는 프레임 수를 의미합니다. 일반적으로 애니메이션이나 동영상의 프레임 수를 말합니다. FPS는 동적 비디오를 저장하고 표시하는 데 사용되는 정보의 양을 측정한 것입니다. 초당 프레임 수가 많을수록 동작이 더 부드럽게 표시됩니다. 일반적으로 갑작스러운 움직임을 방지하기 위한 최소값은 30입니다. 예를 들어, 영화는 초당 24프레임의 속도로 재생됩니다. 이는 1초에 24개의 스틸 프레임이 화면에 연속적으로 투사된다는 의미입니다.
위 내용은 이 기사의 전체 내용입니다. 모든 분들의 학습에 도움이 되기를 바랍니다. 또한 모든 분들이 VeVb Wulin Network를 지지해 주시길 바랍니다.