var FXMix = 1;
var addOverlay = function(type, repeat = 1){
if(FXMix > 0){
ctx.globalCompositeOperation = type;
ctx.globalAlpha = FXMix;
while (repeat-- > 0) {
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
}
}
var addMix = function(type,video, repeat = 1){
if(FXMix > 0){
ctx.globalCompositeOperation = type;
ctx.globalAlpha = FXMix;
while (repeat-- > 0) {
ctx.drawImage(video,0, 0, canvas.width, canvas.height);
}
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
}
}
var fill = function(style){
ctx.globalAlpha = FXMix;
ctx.fillStyle = style;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1;
}
var FX = {
}
var FXList = [];
var currentFX = "";
var addFX = function(name,func){
FXList.push(name);
FX[name] = func;
currentFX = name;
}
addFX("Ligher",(vid)=>{ addMix("lighter",vid);} );
addFX("BlackWhite",(vid)=>{ ctx.fillStyle = "#888"; addOverlay("color");} );
addFX("Negative",(vid)=>{ ctx.fillStyle = "#FFF"; addOverlay("difference");} );
addFX("Sepia",(vid)=>{ fill("#F94"); addMix("luminosity",vid); ;} );
addFX("B&W Negative",(vid)=>{ ctx.fillStyle = "#FFF"; addOverlay("difference");ctx.fillStyle = "#888"; addOverlay("color");} );
addFX("Ligher+",(vid)=>{ addMix("lighter",vid);addMix("lighter",vid);addMix("lighter",vid);} );
addFX("B&W Lighten",(vid)=>{ addMix("lighter",vid);ctx.fillStyle = "#888"; addOverlay("color");} );
addFX("Darken+",(vid)=>{ addMix("multiply",vid);addMix("multiply",vid);addMix("multiply",vid);} );
addFX("Darken",(vid)=>{ addMix("multiply",vid);} );
addFX("Saturate",()=>{ ctx.fillStyle = "#F00";addOverlay("saturation");});
addFX("Movement",(vid) => {
const keepMix = FXMix;
FXMix = 1;
addMix("difference",can1);
addMix("lighter",ctx.canvas,2);
addMix("multiply",vid,1);
FXMix = keepMix * 0.95;
addMix("screen",can2,1);
can2.ctx.drawImage(ctx.canvas,0,0,canvas.width, canvas.height);
FXMix = 1;
addMix("lighter",ctx.canvas,1);
FXMix = keepMix;
var scale = videoContainer.scale;
var vidH = vid.videoHeight;
var vidW = vid.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
if(can1.counting === undefined) { can1.counting = 0 }
else { can1.counting ++ }
if(can1.counting % 2 === 0) {
can1.ctx.drawImage(vid, left, top, vidW * scale, vidH * scale);
}
});
addFX("None",()=>{});
var mediaSource = "http://video.webmfiles.org/big-buck-bunny_trailer.webm";
var mediaSource = "http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv";
var muted = true;
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
const can1 = document.createElement("canvas");
can1.width = canvas.width;
can1.height = canvas.height;
can1.ctx = can1.getContext("2d");
const can2 = document.createElement("canvas");
can2.width = canvas.width;
can2.height = canvas.height;
can2.ctx = can2.getContext("2d");
var videoContainer;
var video = document.createElement("video");
video.src = mediaSource;
video.autoPlay = false;
video.loop = true;
video.muted = muted;
videoContainer = {
video : video,
ready : false,
};
video.onerror = function(e){
document.body.removeChild(canvas);
document.body.innerHTML += "<h2>There is a problem loading the video</h2><br>";
document.body.innerHTML += "Users of IE9+ , the browser does not support WebM videos used by this demo";
document.body.innerHTML += "<br><a href='https://tools.google.com/dlpage/webmmf/'> Download IE9+ WebM support</a> from tools.google.com<br> this includes Edge and Windows 10";
}
video.oncanplay = readyToPlayVideo;
function readyToPlayVideo(event){
videoContainer.scale = Math.min(
canvas.width / this.videoWidth,
canvas.height / this.videoHeight);
videoContainer.ready = true;
requestAnimationFrame(updateCanvas);
document.getElementById("playPause").textContent = "Click video to play/pause.";
document.querySelector(".mute").textContent = "Mute";
}
var playClick = false;
function updateCanvas(){
ctx.clearRect(0,0,canvas.width,canvas.height);
if(videoContainer !== undefined && videoContainer.ready){
video.muted = muted;
var scale = videoContainer.scale;
var vidH = videoContainer.video.videoHeight;
var vidW = videoContainer.video.videoWidth;
var top = canvas.height / 2 - (vidH /2 ) * scale;
var left = canvas.width / 2 - (vidW /2 ) * scale;
ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
FX[currentFX](videoContainer.video);
if(videoContainer.video.paused){
drawPayIcon();
}
overUI = false;
cursor = "default";
drawSlider();
drawList();
if(mouse.over){
if(!overUI){
if((mouse.button&1)===1){
playClick = true;
}
if((mouse.button&1)===0 && playClick){
playClick = false;
playPauseClick();
}
cursor = "pointer";
}
}
if(showFXName > 0){
showFXName = Math.max(0,showFXName - 0.05);
ctx.globalAlpha = Math.min(1,showFXName);
ctx.font = "32px Arial";
ctx.textAlign = "center";
ctx.textbaseLine = "middle";
ctx.fillStyle = "white";
ctx.strokeStyle = "black";
ctx.lineJoin = "round"
ctx.strokeText(currentFX,canvas.width/2,canvas.height/2);
ctx.fillText(currentFX,canvas.width/2,canvas.height/2);
ctx.globalAlpha = 1;
}
canvas.style.cursor = cursor;
}
requestAnimationFrame(updateCanvas);
}
var showFXName = 0;
var cursor = "default";
var overUI = false;
var sliderAlpha = 1;
var listAlpha = 1;
var dragging = false;
var listWidth = null;
function getMaxListWidth(){
ctx.font = "12px arial";
FXList.forEach(text => {listWidth = Math.max(listWidth,ctx.measureText(text).width)})
}
function drawList(){
if(listWidth === null){
getMaxListWidth();
listWidth += 10;
}
if(!overUI && mouse.over && mouse.x > canvas.width - listWidth){
listAlpha = 1;
overUI = true;
}else{
listAlpha = Math.max(0,listAlpha - 0.05);
}
if(listAlpha > 0){
ctx.font = "12px arial";
var textH = 14;
var border = 10;
ctx.textAlign = "right";
ctx.textBaseline = "middle";
ctx.globalAlpha = listAlpha;
ctx.fillStyle = "black";
ctx.strokeStyle = "white";
var len = FXList.length;
var h = len * textH;
var y = canvas.height / 2 - h/2;
var x = canvas.width - border * 2;
ctx.fillRect(x - listWidth,y - border, listWidth+border,h + border );
ctx.strokeRect(x - listWidth,y - border, listWidth + border,h + border );
ctx.fillStyle = "white"
for(var i = 0; i < len; i ++){
var yy = y + i * textH;
if(FXList[i] === currentFX){
ctx.fillStyle = "#0FF";
ctx.fillText(FXList[i],x,yy);
ctx.fillStyle = "white"
}else
if(mouse.x > canvas.width - listWidth && mouse.y > yy - textH/2 && mouse.y < yy + textH /2){
ctx.fillStyle = "#0F0";
ctx.fillText(FXList[i],x,yy);
ctx.fillStyle = "white"
cursor = "pointer";
if((mouse.button & 1) === 1){
currentFX =FXList[i];
showFXName = 4;
}
}else{
ctx.fillText(FXList[i],x,yy);
}
}
ctx.globalAlpha = 1;
}
}
function drawSlider(){
if(currentFX === "None"){
sliderAlpha = 0;
return;
}
var cw = canvas.width;
var ch = canvas.height;
var handle = 5;
var inset = 10
var x = inset;
var w = cw - inset*2;
var h = 20;
var y = ch - inset - h;
var pos = FXMix * w + x;;
if(mouse.y > y - h* 2){
cursor = "e-resize";
overUI = true;
if((mouse.button&1) && !dragging){
dragging = true;
}
}else{
cursor = "pointer";
}
if(dragging){
overUI = true;
cursor = "e-resize";
sliderAlpha = 1;
pos = mouse.x - x;
FXMix = Math.min(1,Math.max(0,pos / w));
if( (mouse.button&1) === 0 ){
dragging = false;
}
}else{
}
if(!dragging && mouse.y > y-h*2 && mouse.over){
sliderAlpha = 1;
}else{
if(sliderAlpha > 0){
sliderAlpha = Math.max(0,sliderAlpha- 0.05);
}
}
if(sliderAlpha === 0){
return;
}
ctx.globalAlpha = sliderAlpha;
ctx.font = "18px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var amount = FXMix;
ctx.fillStyle = "black";
ctx.strokeStyle = "white";
ctx.fillRect(x,y,w,h);
ctx.strokeRect(x,y,w,h);
ctx.fillStyle = "white";
ctx.fillText(currentFX + " "+ (FXMix * 100).toFixed(0)+"%",w/2,y + h / 2);
pos = amount * w + x;
ctx.fillStyle = "white";
ctx.strokeStyle = "black";
ctx.fillRect(pos-handle*2,y-handle,handle* 4,h + handle * 2);
ctx.strokeRect(pos-handle*2,y-handle,handle* 4,h + handle * 2);
ctx.strokeRect(pos-1,y-handle * 0.5,2,h + handle);
ctx.globalAlpha = 1;
}
function drawPayIcon(){
ctx.fillStyle = "#DDD";
ctx.globalAlpha = 0.75;
ctx.beginPath();
var size = (canvas.height / 2) * 0.5;
ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);
ctx.closePath();
ctx.fill();
ctx.globalAlpha = 1;
}
mouse = (function(){
var mouse = {
x : 0, y : 0, w : 0,
button : 0,
over : false,
bm : [1, 2, 4, 6, 5, 3],
active : false,
bounds : null,
border : {top : 10, left : 10},
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,contextmenu".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.clientX - m.bounds.left - m.border.left;
m.y = e.clientY - m.bounds.top - m.border.top;
if (t === "mousedown") {
m.button |= m.bm[e.which-1];
} else if (t === "mouseup") {
m.button &= m.bm[e.which + 2];
}else if (t === "mouseout") {
m.button = 0;
m.over = false;
}else if (t === "mouseover") {
m.over = true;
}
e.preventDefault();
}
m.start = function (element) {
m.element = element;
m.mouseEvents.forEach( n => { m.element.addEventListener(n, mouseMove); } );
m.active = true;
}
m.remove = function () {
if (m.element !== undefined) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); } );
m.active = false;
m.element = undefined;
}
}
return mouse;
})();
function playPauseClick(){
if(videoContainer !== undefined && videoContainer.ready){
if(videoContainer.video.paused){
videoContainer.video.play();
}else{
videoContainer.video.pause();
}
}
}
function videoMute(){
muted = !muted;
if(muted){
document.querySelector(".mute").textContent = "Mute";
}else{
document.querySelector(".mute").textContent= "Sound on";
}
}
document.querySelector(".mute").addEventListener("click",videoMute)
setTimeout(()=>{mouse.start(canvas)},100);
body {
font :14px arial;
text-align : center;
background : #36A;
}
h2 {
color : white;
}
canvas {
border : 10px white solid;
cursor : pointer;
}
a {
color : #F93;
}
.mute {
cursor : pointer;
display: initial;
}
<h2>Simple video FX via canvas "globalCompositeOperation"</h2>
<p>This example show how to use the 2d context "globalCompositeOperation" property to create a variety of FX. Video may take a few moment to load.
</p>
<p>Play pause video with click. Move to bottom of video to see FX mix slider (Not available if filter None). Move to right to get filter selection and select the filter example. Happy filtering</p>
<canvas id="myCanvas" width = "532" height ="300" ></canvas><br>
<h3><div id = "playPause">Loading content.</div></h3>
<div class="mute"></div><br>