Ao enviar uma imagem de tamanho apropriado, os usuários podem visualizar o efeito semelhante a uma apresentação de slides, selecionando o efeito e a música da animação renderizada e, por fim, clicar para confirmar para gerar um vídeo, que pode ser reproduzido em manchetes ou Douyin.
Possíveis soluções para geração de vídeosConversão de codificação de vídeo front-end pura (como WebM Encoder Whammy)
Passe cada quadro de imagem para o back-end para implementação, e o back-end chama o FFmpeg para transcodificação de vídeo.
A geração de imagens pode ser obtida por meio da interface nativa do canvas toDataURL e, por fim, retorna dados de imagem no formato base64.
function generatePng() { var canvas = document.createElement('canvas'); let icavas = '#canvas' //ID da tela para renderização de animação 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);Como fazer capturas de tela da animação em tela
Use setInterval para executar o método de geração de imagem regularmente. Claro, você também pode usar requestAnimationFrame.
setInterval(function() { imgsTemp.push(generatePng())}, 1000/60)Como obter cada quadro de imagem no backend
Solução 1: o navegador headless executa o js de animação de tela de front-end e, em seguida, faz uma captura de tela do js
Ideia inicial:
As capturas de tela são impressas usando console.log As capturas de tela da tela estão no formato base64, uma animação de 15 segundos, e há mais de 100 capturas de tela, que causaram diretamente o travamento do servidor (rejeitado);
Plano de teste:
A captura de tela é armazenada em uma variável js. Após a reprodução da animação, um logotipo é adicionado à página e o backend obtém a variável.
const pages = { imageZoomOut: import ('./image_zoom_inout.js'), //Zoom imageArt: import ('./image_art.js'), //Apagar imageGrid: import ('./image_grid.js'), / /Grid imageRotate: import ('./image_rotate.js'), //Abrir e fechar imageFlash: import ('./image_flash.js'), //Imagem e texto flash imageVerticalArt: import ('./image_vertical_art.js'), //Apagamento vertical imageVerticalGrid: import ('./image_vertical_grid.js'), //Grade vertical imageVerticalRotate: import ('. /image_vertical_rotate.js '), //Abertura e fechamento vertical imageVerticalFlash: import ('./image_vertical_flash.js'), //Imagem vertical e texto flash imageVerticalZoomOut: import ('./image_vertical_zoom_inout.js'), //Zoom vertical imageVertical: import ('./image_vertical.js'), //Versão vertical geral};var isShow = falsevar imgsBase64 = []var imgsTemp = []var cutInter = nullvar imgsTimeLong = 0function getQuerys(tag) { let queryStr = window.location.search.slice(1); deixe queryArr = queryStr.split('&'); ; 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 images = getQuery.list let newImg = [] let vh = wrapWidth == 1? 360: 640 let vw = wrapWidth == 1? (item, índice) { if (11 === índice || 13 === índice || 16 === índice) { var temp = new Image(vw, vh) temp.setAttribute('crossOrigin', 'anonymous'); effectTag) } else { images.map(function(item) { var temp = new Image(vw, vh) temp.setAttribute('crossOrigin', 'anonymous'); }) imgArr = newImg }}async function renderAnimate(page) { //await creatImg() deixe-me = this const pageA = aguardar páginas[página]; false pageA[page].render(null, { canvas: innerCanvas, images: imgArr }, function() { //Após a reprodução da animação isShow = verdadeiro; imgsTemp.push(generatePng()) imgsBase64.push(imgsTemp) deixe agora = new Date().getTime() window.imgsTimeLong = agora - oldDate clearInterval(cutInter) document.getElementById('cutImg').innerHTML = ' pronto'//Identificação da página}) 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 //Variável de armazenamento de captura de tela creatImg()
Desvantagens do plano de operação experimental:
var temp = new Image(vw, vh)temp.setAttribute('crossOrigin', 'anonymous');Solução final: execute animação no lado NODE
Use node-canvas para gravar cada captura de tela do quadro na pasta especificada usando fs.writeFile
const { createCanvas, loadImage} = require(canvas const pages = { imageZoomOut: require('./image_zoom_inout.js'), //Zoom imageArt: require('./image_art.js'), //Apagar imageGrid: require('./image_grid.js'), //Grade imageRotate: require('./image_rotate.js'), //Abrir e fechar imageFlash: require('./image_flash.js'), //Imagem e texto flash imageVerticalArt: require('./image_vertical_art.js'), //Apagamento vertical imageVerticalGrid: require('./image_vertical_grid . js'), //grade vertical imageVerticalRotate: require('./image_vertical_rotate.js'), //grade vertical imageVerticalFlash: require('./image_vertical_flash.js'), //Imagem vertical e texto flash imageVerticalZoomOut: require('./image_vertical_zoom_inout.js'), //Zoom vertical imageVertical: require('./image_vertical.js'), // Geral para versão vertical};const fs = require(fs);const querystring = require('querystring');let args = process.argv && process.argv[2]let parse = querystring.parse(args)let vh = parse.templateType == 1 ? 720 : 1280 //altura da tela let vw = parse.templateType == 1280 : 720 //largura da tela let imgSrcArray = parse.images //Matriz de imagem let effectTag = parse.tid //Efeito de animação 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 canvas = createCanvas(vw, vh);const ctx = canvas.getContext(2d);Promise.all(loadArr) .then((images) => { //Inicializar animação console.log('Iniciar animação') let oldDate = new Date ().getTime() páginas[efeitoTag].render(null, { canvas: canvas, images: images }, function() { clearInterval(interval) let now = new Date().getTime() console.log(now - oldDate, 'Animação termina') }) const interval = 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,/, ''); deixe buf = new Buffer(data, 'base64'); `, buf, {}, (err) => { console.log(x, err } }); 1000/60 }) .catch(e => { console.log(e); });
Execute o seguinte comando no iterm
nó 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 =../bunda ets/imgs/8.png&images=../assets/imgs/7.png&images=../assets/imgs/4.png&images=../assets/imgs/6.png&images=../assets/imgs/8. png&images=../assets/imgs/7.png' './imagens/'
Descrição do parâmetro:
1) tid é o nome da animação
2) templateType é tamanho: 1:1280*720;
3) imagens é o endereço da imagem
4) A variável './images/' é o endereço onde a captura de tela é salva,
Desvantagens de rodar em ambiente NODEO desenho a seguir é repetido a cada 13 segundos:
para (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.largura, y.height), p.restore(), p.closePath(); for (var S = 0; 50 > S; S++) p.beginPath(), p.globalAlpha = 1 - S / 49, p.save( ), p.rect(0, 0, d + P + 2 * S, g + b + 2 * S), p.clip(), p.drawImage(x[c], 0, 0, y.largura, y.altura), p.restore(), p.closePath();
Por causa do modelo de loop de eventos do Node.js, o uso do Node.js deve garantir que o ciclo do Node.js possa ser executado o tempo todo. Se uma função muito demorada aparecer, o loop de eventos ficará preso e não poderá ser manipulado. outras tarefas no tempo, portanto, como resultado, algumas animações ainda são lentas.
Possibilidade de otimização posteriorTente usar a linguagem Go para fazer capturas de tela;
Reescrever a animação da tela;
extra Taxa de bits de vídeoA taxa de bits de vídeo é o número de bits de dados transmitidos por unidade de tempo durante a transmissão de dados. Geralmente, a unidade que usamos é kbps, que é milhares de bits por segundo. Um entendimento simples é a taxa de amostragem. Quanto maior a taxa de amostragem por unidade de tempo, maior será a precisão e mais próximo o arquivo processado estará do arquivo original. Por exemplo, para um áudio, quanto maior a taxa de bits, menor a taxa de compressão, menor a perda de qualidade do som e mais próxima a qualidade do som está da fonte de áudio.
Quadros FPS por segundo (quadros por segundo)
FPS é uma definição na área gráfica, que se refere ao número de frames transmitidos por segundo. De modo geral, refere-se ao número de frames de animação ou vídeo. FPS é uma medida da quantidade de informações usadas para salvar e exibir vídeo dinâmico. Quanto mais quadros por segundo, mais suave será a ação exibida. Normalmente, o mínimo para evitar movimentos bruscos é 30. Por exemplo, um filme é reproduzido a uma velocidade de 24 quadros por segundo, o que significa que 24 quadros estáticos são projetados continuamente na tela em um segundo.
O texto acima é todo o conteúdo deste artigo. Espero que seja útil para o estudo de todos. Também espero que todos apoiem a Rede VeVb Wulin.