適切なサイズの写真をアップロードすると、ユーザーはレンダリング アニメーションの効果と音楽を選択してスライドショーと同様の効果をプレビューでき、最後にクリックして確認すると、見出しや Douyin で再生できるビデオが生成されます。
ビデオを生成するために考えられるソリューション純粋なフロントエンドビデオエンコーディング変換 (WebM Encoder Whammy など)
画像の各フレームを実装のためにバックエンドに渡し、バックエンドはビデオのトランスコーディングのために FFmpeg を呼び出します。
画像の生成は、DataURL へのキャンバス ネイティブ インターフェイスを通じて実現でき、最終的には画像データを Base64 形式で返します。
function generatedPng() { var Canvas = document.createElement('canvas'); let icavas = '#canvas' //アニメーションをレンダリングするためのキャンバス ID if (wrapWidth == 2) { icavas = '#verticalCanvas' } var CanvasNode = document .querySelector(icavas) Canvas.width = CanvasNode.width; var ctx; 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 を使用して出力されます。キャンバスのスクリーンショットは、base64 形式の 15 秒のアニメーションであり、100 枚を超えるスクリーンショットが直接サーバーのクラッシュ (拒否) の原因となりました。
試運転プラン:
スクリーンショットは js 変数に保存され、アニメーションが再生された後、バックエンドが変数を取得します。
const pages = { imageZoomOut: import ('./image_zoom_inout.js'), //imageArt をズーム: import ('./image_art.js'), //imageGrid を消去: 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 = []varcutInter = nullvar imgsTimeLong = 0function getQuerys(tag) { let queryStr = window.location.search.slice(1); let queryArr = queryStr.split('&'); let query = []; 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] == タグ) { query.push(qitem); } else { spec[queryItem[0]] = qitem } } return { list: クエリ、spec: spec };}var getQuery = getQuerys('images')var effectTag = getQuery.spec。 tidvar WrapWidth = getQuery.spec.templateTypelet num = 0let imgArr = []function creatImg() { var image = getQuery.list let newImg = [] let vh = WrapWidth == 1 ? 360 : 640 let vw = WrapWidth == 1 ? 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(効果タグ) } else {images.map(function(item) { var temp = new Image(vw, vh) temp.setAttribute('crossOrigin', 'anonymous'); temp.src = item; temp.onload = function() { num++ if (num == image.length) { renderAnimate(effectTag) } } newImg.push(temp) }) imgArr = newImg }}async function renderAnimate(page) { //creatImg() を待ってください = 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 - oldDate clearInterval(cutInter) document.getElementById('cutImg').innerHTML = '完了'//ページ識別}) CutInter = setInterval(function() { imgsTemp.push(generatePng()) if (imgsTemp.length >= 50) { imgsBase64.push(imgsTemp) imgsTemp = [] } }, 130)}function getImgs() { return imgsBase64}functiongeneratePng() { var Canvas = document.createElement('canvas'); icavas = '#canvas' にします。 (wrapWidth == 2) { icavas = '#verticalCanvas' } var CanvasNode = document.querySelector(icavas) Canvas.width = CanvasNode.width; var ctx = Canvas.getContext('2d') ; 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 pages = { imageZoomOut: require('./image_zoom_inout.js'), //imageArt をズームする: require('./image_art.js'), //imageGrid を消去する : 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]let parse = querystring.parse(args)let vh = parse.templateType == 1 ? 720 : 1280 //キャンバスの高さ let vw = parse.templateType == 1280 : 720 //キャンバスの幅let imgSrcArray = parse.images //画像配列 leteffectTag = parse.tid //アニメーション効果 let saveImgPath = process.argv && process.argv[3]letloadArr = []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) => { //アニメーションを初期化します console.log('アニメーションの開始') let oldDate = new Date ().getTime() ページ[effectTag].render(null, { キャンバス: キャンバス, 画像: 画像 }, function() { clearInterval(interval) let now = new Date().getTime() console.log(now - oldDate, 'アニメーション終了') }) 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,/, ''); let buf = new Buffer(data, 'base64'); fs.writeFile(`${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&images=../assets/imgs/7.png' './画像/'
パラメータの説明:
1) tid はアニメーション名です
2) templateType のサイズは 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.rect(0, 0, d + P + 2 * S, g + b + 2 * S)、p.clip()、p.drawImage(x[c], 0, 0, y.width, y.height)、p.restore()、p.closePath();
Node.js のイベント ループ モデルのため、Node.js を使用するには、Node.js のサイクルが常に実行できるようにする必要があります。非常に時間のかかる関数が出現すると、イベント ループがスタックして処理できなくなります。他のタスクが時間内に実行されるため、結果として一部のアニメーションがまだ遅い
後の最適化の可能性Go 言語を使用してスクリーンショットを撮ってみてください。
キャンバスアニメーションを書き換えます。
余分なビデオのビットレートビデオ ビット レートは、データ送信中の単位時間あたりに送信されるデータ ビット数で、通常、単位は kbps、つまり 1 秒あたり数千ビットです。簡単に理解すると、サンプリング レートは単位時間あたりのサンプリング レートが大きいほど精度が高く、処理されたファイルは元のファイルに近づきます。たとえば、オーディオの場合、ビット レートが高くなるほど圧縮率が小さくなり、音質の劣化が少なくなり、音質が音源に近づきます。
1 秒あたりの FPS フレーム数 (1 秒あたりのフレーム数))
FPS はグラフィックスの分野での定義で、1 秒あたりに送信されるフレーム数を指します。一般的には、アニメーションまたはビデオのフレーム数を指します。 FPS は、ダイナミック ビデオの保存と表示に使用される情報量の尺度です。 1 秒あたりのフレーム数が多いほど、表示されるアクションはよりスムーズになります。一般に、ぎくしゃくした動きを避けるための最小値は 30 です。たとえば、映画は 24 フレーム/秒の速度で再生されます。これは、1 秒間に 24 枚の静止画フレームが連続してスクリーンに投影されることを意味します。
以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。また、VeVb Wulin Network をご支援いただければ幸いです。