2017-02-09 98 views
0

我有一種情況,我需要剪輯圖像或視頻。圖像或視頻需要能夠重疊。我們最初使用SVG嘗試過,但由於各種原因,這樣做效果不佳,所以現在我們在Canvas中進行。什麼是剪輯通過畫布運行的視頻的最有效方法

這對於圖像來說算得很好,但對於視頻來說,瀏覽器在大約2分鐘後幾乎被迫停下來。 (你會不會從示例代碼或鏈接看到的是,我們也暫停視頻,而它不是在視圖,而該標籤還沒有考慮。)

這裏是一個鏈接:http://codepen.io/paceaux/pen/egLOeR

主要關注的是這種方法:

drawFrame() { 
    if (this.isVideo && this.media.paused) return false; 

    let x = 0; 
    let width = this.media.offsetWidth; 
    let y = 0; 

    this.imageFrames[this.module.dataset.imageFrame](this.backContext); 
    this.backContext.drawImage(this.media, x, y, width, this.canvas.height); 

    this.context.drawImage(this.backCanvas, 0, 0); 

    if (this.isVideo) { 
     window.requestAnimationFrame(()=>{ 
      this.drawFrame(); 
     }); 
    } 
} 

您會發現立即拖慢瀏覽器。我不建議把這個codepen看得太長,因爲任何地方的事情都會變得非常慢。

我使用的是"backCanvas" technique,但這似乎讓事情變得更糟。

我也嘗試過使用Path2D()來保存剪輯路徑,但這似乎也沒什麼幫助。

 wedgeTop: (context, wedgeHeight = defaults.wedgeHeight) => { 
     var wedge = new Path2D(); 

     wedge.moveTo(this.dimensions.width, 0); 
     wedge.lineTo(this.dimensions.width, this.dimensions.height); 
     wedge.lineTo(0, this.dimensions.height); 
     wedge.lineTo(0, wedgeHeight); 
     wedge.closePath(); 
     context.clip(wedge); 
    }, 

是否還有其他優化我缺少? (除了視頻的大小)。

let imageFrames = function() { 
 
\t let defaults = { 
 
\t \t wedgeHeight: 50 
 
\t }; 
 
\t return { 
 
\t \t defaults: defaults, 
 

 
\t \t //all wedges draw paths clockwise: top right, bottom right, bottom left, top left 
 
\t \t wedgeTop: (context, wedgeHeight = defaults.wedgeHeight) => { 
 
\t \t \t var wedge = new Path2D(); 
 

 
\t \t \t wedge.moveTo(this.dimensions.width, 0); 
 
\t \t \t wedge.lineTo(this.dimensions.width, this.dimensions.height); 
 
\t \t \t wedge.lineTo(0, this.dimensions.height); 
 
\t \t \t wedge.lineTo(0, wedgeHeight); 
 
\t \t \t wedge.closePath(); 
 
\t \t \t context.clip(wedge); 
 
\t \t }, 
 

 
\t \t wedgeTopReverse: (context, wedgeHeight = defaults.wedgeHeight) => { 
 
\t \t \t var wedge = new Path2D(); 
 

 
\t \t \t wedge.moveTo(this.dimensions.width, wedgeHeight); 
 
\t \t \t wedge.lineTo(this.dimensions.width, this.dimensions.height); 
 
\t \t \t wedge.lineTo(0, this.dimensions.height); 
 
\t \t \t wedge.lineTo(0, 0); 
 
\t \t \t wedge.closePath(); 
 
\t \t \t context.clip(wedge); 
 

 
\t \t }, 
 

 
\t \t wedgeBottom: (context, wedgeHeight = defaults.wedgeHeight) => { 
 
\t \t \t var wedge = new Path2D(); 
 

 
\t \t \t wedge.moveTo(this.dimensions.width, 0); 
 
\t \t \t wedge.lineTo(this.dimensions.width, this.dimensions.height - wedgeHeight); 
 
\t \t \t wedge.lineTo(0, this.dimensions.height); 
 
\t \t \t wedge.lineTo(0,0); 
 
\t \t \t wedge.closePath(); 
 
\t \t \t context.clip(wedge); 
 
\t \t }, 
 

 
\t \t wedgeBottomReverse: (context, wedgeHeight = defaults.wedgeHeight) => { 
 
\t \t \t var wedge = new Path2D(); 
 

 
\t \t \t wedge.moveTo(this.dimensions.width, 0); 
 
\t \t \t wedge.lineTo(this.dimensions.width, this.dimensions.height); 
 
\t \t \t wedge.lineto(0, this.dimensions.height - wedgeHeight); 
 
\t \t \t wedge.lineTo(0, 0); 
 
\t \t \t wedge.closePath(); 
 
\t \t \t context.clip(wedge); 
 
\t \t } 
 
\t }; 
 
}; 
 

 
class ImageCanvasModule { 
 
\t constructor(module) { 
 
\t \t this.module = module; 
 
\t \t this.imageFrames = imageFrames.call(this); 
 

 
\t \t if(this.isVideo) { 
 
\t \t \t /*drawFrame has a check where it'll only draw on reqAnimationFrame if video.paused === false, 
 
\t \t \t so we need to fire drawFrame on both events because that boolean will be false when it's paused, thus cancelling the animation frame 
 
\t \t \t */ 
 
\t \t \t this.media.addEventListener('play',()=>{ 
 
\t \t \t \t this.drawOnCanvas(); 
 
\t \t \t }); 
 

 
\t \t \t this.media.addEventListener('pause',()=> { 
 
\t \t \t \t this.drawOnCanvas(); 
 
\t \t \t }); 
 
\t \t } 
 
\t } 
 

 
\t get isPicture() { 
 
\t \t return (this.module.nodeName === 'PICTURE'); 
 
\t } 
 

 
\t get isVideo() { 
 
\t \t return (this.module.nodeName === 'VIDEO'); 
 
\t } 
 

 
\t get media() { 
 
\t \t return this.isPicture ? this.module.querySelector('img') : this.module; 
 
\t } 
 

 
\t get context() { 
 
\t \t return this.canvas.getContext('2d'); 
 
\t } 
 

 
\t get dimensions() { 
 
\t \t return { 
 
\t \t \t width: this.module.offsetWidth, 
 
\t \t \t height: this.module.offsetHeight 
 
\t \t }; 
 
\t } 
 

 
\t createCanvas() { 
 
\t \t let canvas = document.createElement('canvas'); 
 

 
\t \t this.module.parentNode.insertBefore(canvas, this.module.nextSibling); 
 
\t \t canvas.className = this.module.className; 
 

 
\t \t this.canvas = canvas; 
 

 
\t \t this.createBackContext(); 
 
\t } 
 

 
\t createBackContext() { 
 
\t \t this.backCanvas = document.createElement('canvas'); 
 
\t \t this.backContext = this.backCanvas.getContext('2d'); 
 

 
\t \t this.backCanvas.width = this.dimensions.width; 
 
\t \t this.backCanvas.height = this.backCanvas.height; 
 
\t } 
 

 
\t sizeCanvas() { 
 
\t \t this.canvas.height = this.dimensions.height; 
 
\t \t this.canvas.width = this.dimensions.width; 
 

 
\t \t this.backCanvas.height = this.dimensions.height; 
 
\t \t this.backCanvas.width = this.dimensions.width; 
 
\t } 
 

 
\t drawFrame() { 
 
\t \t if (this.isVideo && this.media.paused) return false; 
 

 
\t \t let x = 0; 
 
\t \t let width = this.media.offsetWidth; 
 
\t \t let y = 0; 
 
\t \t 
 
\t \t this.imageFrames[this.module.dataset.imageFrame](this.backContext); 
 
\t \t this.backContext.drawImage(this.media, x, y, width, this.canvas.height); 
 

 
\t \t this.context.drawImage(this.backCanvas, 0, 0); 
 

 
\t \t if (this.isVideo) { 
 
\t \t \t window.requestAnimationFrame(()=>{ 
 
\t \t \t \t this.drawFrame(); 
 
\t \t \t }); 
 
\t \t } 
 
\t } 
 

 
\t drawOnCanvas() { 
 
\t \t this.sizeCanvas(); 
 
\t \t this.drawFrame(); 
 
\t } 
 

 
\t hideOriginal() { 
 
\t \t //don't use display: none .... you can't get image dimensions when you do that. 
 
\t \t this.module.style.opacity = 0; 
 
\t } 
 
} 
 
console.clear(); 
 

 
window.addEventListener('DOMContentLoaded',()=> { 
 
\t var els = document.querySelectorAll('.canvasify'); 
 
\t var canvasified = []; 
 

 
\t for (el of els) { 
 
\t \t if (el.dataset.imageFrame) { 
 
\t \t \t let imageModule = new ImageCanvasModule(el); 
 
\t \t \t imageModule.createCanvas(); 
 
\t \t \t imageModule.drawOnCanvas(); 
 
\t \t \t imageModule.hideOriginal(); 
 
\t \t \t canvasified.push(imageModule); 
 
\t \t } 
 

 
\t } 
 
\t console.log(canvasified); 
 
});
body { 
 
\t background-color: #333; 
 
} 
 

 
.container { 
 
\t height: 600px; 
 
\t width: 100%; 
 
\t position: relative; 
 
\t display: flex; 
 
\t flex-direction: column; 
 
\t justify-content: center; 
 
} 
 
.container + .container { 
 
\t margin-top: -150px; 
 
} 
 
.canvasify { 
 
\t position:absolute; 
 
\t top: 0; 
 
\t left: 0; 
 
\t right: 0; 
 
\t bottom: 0; 
 
\t width: 100%; 
 
\t z-index: -1; 
 
} 
 
video { 
 
\t width: 100% 
 
} 
 

 
h1 { 
 
\t font-size: 2em; 
 
\t color: #ddd; 
 
}
<div class="container"> 
 
\t <img class="canvasify" data-image-frame="wedgeTop" src="http://placekitten.com/1280/500" /> 
 
\t <h1>Kitty with a clipped top</h1> 
 
</div> 
 

 

 
<div class="container"> 
 
<video controls muted class="canvasify" loop autoplay data-image-frame="wedgeTop"> 
 
<source src="https://poc5.ssl.cdn.sdlmedia.com/web/635663565028367012PU.mp4"> 
 
</video> 
 
\t <h1>video with a clipped top that overlaps the image above</h1> 
 
</div>

的問題是codepen(和運行此代碼的其他頁面)是極其緩慢。我錯過了哪些優化,或者使用不正確?

The desired effect is an image in one container, a video in another, and they're both cropped

+0

「重疊」是什麼意思?不確定是什麼問題? – guest271314

+0

問題是頁面運行速度非常慢。 「重疊」意味着應該剪切來自視頻的圖像,並重疊位於其上方的圖像。使它看起來像視頻/圖像不是矩形的,而是以角度切出。 – paceaux

+0

仍然沒有。我正在尋找你在codepen中看到的內容。兩個獨立的容器。一個可能包含一個圖像。另一個可能包含一個視頻。每個容器都有視頻或圖像,並且會以一定角度「裁剪」。來自HTML的文本將位於圖像/視頻之上。因此,一個容器中圖像的底部邊緣將直觀地顯示在可能是視頻的頂部邊緣下方。 我不需要幫助,如何做到這一點(我已經完成了,它的工作原理)我需要幫助讓頁面更好地執行。 – paceaux

回答

1

從我的代碼比較其他人的代碼在這種情況下是如何工作的,我發現這個安全漏洞是在我drawFrame()方法,我用從視頻圖像實際繪製到畫布上。

有兩個基本問題:

  1. requestAnimationFrame()運行約60fps的,因爲這是視頻,需要不超過30
  2. 我正在畫在drawFrame每個實例的剪裁,以及我不需要那樣做。你可以夾在畫布一次然後運行​​

因此,新的並條機方法看起來像這樣

drawFrame() { 
    if (this.isVideo && this.media.paused) return false; 
    this.imageFrames[this.module.dataset.imageFrame](); 

    var _this = this; 
    var toggle = false; 

    (function loop() { 
     toggle= !toggle; 

     if (toggle) { 
      let x = 0; 
      let width = _this.media.offsetWidth; 
      let y = 0; 

     _this.context.drawImage(_this.media, 0, 0, width, _this.canvas.height); 
     } 

     if (_this.isVideo) { 
      window.requestAnimationFrame(loop); 
     } 

    })(); 
} 

問題1是使用toggle變量只繪製圖像每隔解決循環運行的時間。

問題2通過裁剪循環外部的圖像來解決。

這兩個更改在頁面上的其他元素如何加載,動畫和響應用戶方面產生了顯着差異。

現在看起來很明顯,但是剪輯視頻中的每一幀都比剪輯canvas昂貴得多。

非常感謝用戶K3N的代碼示例幫助我發現問題。

相關問題