У меня есть ситуация, когда мне нужно обрезать изображения или видео. Изображения или видеоролики должны быть перекрыты. Мы попробовали это первоначально с помощью 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();
});
}
}
Вы будете наблюдать браузер сразу замедляющими. Я не рекомендую смотреть на этот кодекс на слишком страшно, потому что повсюду все будет ужасно медленным.
Я использую "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 (и другие страницы, бегущие этот код) очень медленно. Какую оптимизацию я пропускаю или неправильно использую?
Что вы подразумеваете под «перекрытиями»? Не знаете, в чем проблема? – guest271314
Проблема в том, что страница работает очень медленно. «Overlaps» означает, что изображение из видео должно быть обрезано и перекрывать изображение, которое находится над ним. заставляя его выглядеть как видео/изображение не прямоугольное, а вырезанное под углами. – paceaux
По-прежнему нет. Я ищу именно то, что вы видите в кодефене. Два отдельных контейнера. один может содержать изображение. Другой может содержать видео. Каждый контейнер будет иметь либо видео, либо изображение и будет «обрезано» под углом. Текст, похожий на HTML, будет сидеть над изображением/видео. Таким образом, нижний край изображения в одном контейнере будет визуально отображаться под верхним краем того, что может быть видео. Мне не нужна помощь в том, как это сделать (я уже это сделал, и это работает) Мне нужна помощь, чтобы страница работала лучше. – paceaux