Skip to content

Instantly share code, notes, and snippets.

@dmail
Created October 5, 2015 06:50
Show Gist options
  • Save dmail/dda5adf57c8d66b73dd6 to your computer and use it in GitHub Desktop.
Save dmail/dda5adf57c8d66b73dd6 to your computer and use it in GitHub Desktop.
object-fit:cover polyfill on video element using canvas
// http://stackoverflow.com/questions/21961839/simulation-background-size-cover-in-canvas
function drawImageProp(ctx, img, x, y, w, h, offsetX, offsetY) {
if (arguments.length === 2) {
x = y = 0;
w = ctx.canvas.width;
h = ctx.canvas.height;
}
// default offset is center
offsetX = typeof offsetX === "number" ? offsetX : 0.5;
offsetY = typeof offsetY === "number" ? offsetY : 0.5;
// keep bounds [0.0, 1.0]
if (offsetX < 0) offsetX = 0;
if (offsetY < 0) offsetY = 0;
if (offsetX > 1) offsetX = 1;
if (offsetY > 1) offsetY = 1;
var iw = img.width,
ih = img.height,
r = Math.min(w / iw, h / ih),
nw = iw * r, // new prop. width
nh = ih * r, // new prop. height
cx, cy, cw, ch, ar = 1;
// decide which gap to fill
if (nw < w) ar = w / nw;
if (nh < h) ar = h / nh;
nw *= ar;
nh *= ar;
// calc source rectangle
cw = iw / (nw / w);
ch = ih / (nh / h);
cx = (iw - cw) * offsetX;
cy = (ih - ch) * offsetY;
// make sure source rectangle is valid
if (cx < 0) cx = 0;
if (cy < 0) cy = 0;
if (cw > iw) cw = iw;
if (ch > ih) ch = ih;
// fill image in dest. rectangle
ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h);
}
var CoverVideo = {
constructor: function(video){
this.video = video;
this.computedStyle = window.getComputedStyle(this.video, null);
if( this.video.poster ){
this.poster = new Image();
this.poster.onload = function(){
window.addEventListener('resize', this.drawPoster.bind(this));
this.drawPoster();
}.bind(this);
this.poster.src = this.video.poster;
}
this.video.addEventListener('play', this.drawVideo.bind(this), false);
},
draw: function(source){
this.replace();
drawImageProp(this.canvas.getContext('2d'), source, 0, 0, this.canvas.width, this.canvas.height);
},
isPlaying: function(){
return !this.video.paused && !this.video.ended;
},
drawPoster: function(){
if( false === this.isPlaying() ){
this.draw(this.poster);
}
},
drawVideo: function(){
if( false === this.isPlaying() ){
return false;
}
if( this.timeout ){
clearTimeout(this.timeout);
this.timeout = null;
}
this.video.width = this.video.videoWidth;
this.video.height = this.video.videoHeight;
this.draw(this.video);
this.timeout = setTimeout(this.drawVideo.bind(this), 20);
},
replace: function(){
if( this.replaced ){
return;
}
this.replaced = true;
this.video.parentNode.style.overflow = 'hidden'; // prevent scrollbar of the parent
this.video.style.position = 'absolute';
this.video.style.zIndex = -1;
this.video.style.visibility = 'hidden';
this.canvas = document.createElement('canvas');
this.canvas.style.display = this.getVideoStyle('display');
this.video.parentNode.insertBefore(this.canvas, this.video.nextSibling);
window.addEventListener('resize', this.adaptCanvas.bind(this));
this.adaptCanvas();
},
getVideoStyle: function(name){
return this.computedStyle.getPropertyValue(name);
},
adaptCanvas: function(){
this.canvas.width = Math.floor(parseInt(this.getVideoStyle('width')));
this.canvas.height = Math.floor(parseInt(this.getVideoStyle('height')));
if( this.isPlaying() ){
this.drawVideo();
}
else if( this.poster ){
this.drawPoster();
}
}
};
if( false === 'objectFit' in document.documentElement.style ){
Object.create(CoverVideo).constructor(document.querySelector('video'));
}
@dhwrwm
Copy link

dhwrwm commented Dec 28, 2020

Thanks a ton

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment