将画布视频从灰度淡化到彩色 [英] Fade canvas video from greyscale to color

查看:18
本文介绍了将画布视频从灰度淡化到彩色的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 2 个元素 - 视频和画布.在视频播放事件中,函数在画布上仅灰度绘制相同的视频.然后我有一个按钮,它应该将画布视频从灰度淡化回彩色.到目前为止,我已经设法在按钮点击时恢复颜色,但我需要它褪色 - 从灰度到彩色,而不仅仅是立即显示颜色.

I have 2 elements - video and canvas. On video play event, a functions draws the same video on canvas only greyscale. Then I have a button which is supposed to fade canvas video from greyscale back to color. So far I've managed to get back colors on button click, but I need it to fade - from greyscale to color, not just instantly show color.

关于我如何做到这一点的任何想法?或者..有可能吗?

Any ideas on how could I accomplish that? Or.. is it even possible?

代码如下:

function grey() {
    if (!stop) {
    bgContext.drawImage(video, 0, 0, w, h);
    var pixelData = bgContext.getImageData(0, 0, w, h);
    for (var i = 0; i < pixelData.data.length; i += 4 ) {
        var r = pixelData.data[i];
        var g = pixelData.data[i+1];
        var b = pixelData.data[i+2];
        var averageColour = (r + g + b) / 3;
        pixelData.data[i] = averageColour;
        pixelData.data[i+1] = averageColour;
        pixelData.data[i+2] = averageColour;
    }
    context.putImageData(pixelData, 0, 0);
    }
}

function color() {
    bgContext.drawImage(video, 0, 0, w, h);
    var pixelData = bgContext.getImageData(0, 0, w, h);
    for (var i = 0; i < pixelData.data.length; i += 4 ) {
        var r = pixelData.data[i];
        var g = pixelData.data[i+1];
        var b = pixelData.data[i+2];
        pixelData.data[i] = r;
        pixelData.data[i+1] = g;
        pixelData.data[i+2] = b;
    }
    context.putImageData(pixelData, 0, 0);
}

video.addEventListener('play', function() {
    setInterval("grey()", 0);
}, false);

button.addEventListener('click', function() {
    stop = true;
    setInterval("color()", 0);
}, false);

推荐答案

用于实时视频/动画的画布过滤器.

黑色 &白色滤镜

做一个黑白滤镜很容易.

Canvas Filters for real-time Video/Animation.

Black & White filter

To do a black and white filter is easy.

 // mixAmount is a value from 0 - 1 0 = no mix 1 = full FX
 // video is the video
 ctx.drawImage(video,0,0); // draw the video
 // set up filter
 ctx.fillStyle = "#888"; // gray colour
 ctx.globalAlpha = mixAmount;   // amount of FX
 ctx.globalCompositeOperation = "color";  // The comp setting to do BLACK/WHITE
 ctx.fillRect(0,0,video.width,video.height);  // Draw gray over the video
 ctx.globalAlpha = 1;  // reset alpha
 ctx.globalCompositeOperation = "source-over";  // reset comp

或者您可以渲染视频本身以获得其他效果,该演示仅使用上述代码和一些额外图层即可显示黑白过滤器和更多其他效果.

Or you can render the video over itself to get other FX, the demo shows the Black and White filter and several more by just using the above code and a few extra layers.

有关显示视频的更多信息,请参阅在画布内显示视频

For more on displaying a video see Display video inside canvas

演示展示了我在做黑白和其他一些特效的方法.

The demo show how to do Black and white and some other FX while I am at it.

查看视频标题以了解归属.左侧 FX 从上到下变亮"、黑白"、棕褐色"、饱和"和负片".

演示有以下 FX Lighter、Darken、Black/White、Negative、Saturate、Sepia、B&Wnegative 等.

The Demo has the following FX Lighter, Darken, Black/White, Negative, Saturate, Sepia, B&W negative, and more.

与问题相关的代码都在顶部并标记.剩下的就是UI加载等.

The code relating to the question is all at the top and marked. The rest is UI loading etc..

每个 FX 都是一个函数,它将调用 addMixaddOverlay 来应用过滤器,如上面的代码片段所示.addMix 函数略有不同,因为它在视频上绘制视频以获得 FX 而不是填充.

Each FX is a function that will call either addMix or addOverlay to apply the filter as shown in the snippet above. The addMix function is slightly different as it draws the video over the video to get the FX rather than a fill.

说明在演示中.

请注意,并非所有浏览器都支持所有 comp 模式(为什么??谁知道!:()也没有办法 100% 确定浏览器是否支持某种模式.安全的赌注是 Firefox、Chrome、和所有其他浏览器的 Edge 祝你好运..

Please note not all browser support all comp modes (WHY?? who knows!! :( ) Nor is there a way to be 100% sure if a browser supports a mode or not. The safe bet is Firefox, Chrome, and Edge for all other browsers best of luck..

//==========================================================================
// All the mix function are in this section
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;
}
// multiply,screen,overlay,color-dodge,color-burn,hard-light,soft-light,difference,exclusion,hue,saturation,color,luminosity
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",()=>{});



// end of FX mixing
//==========================================================================

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"); // get the canvas from the page
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; // object to hold video and associated info
var video = document.createElement("video"); // create a video element
video.src = mediaSource;
// the video will now begin to load.
// As some additional info is needed we will place the video in a
// containing object for convenience
video.autoPlay = false; // ensure that the video does not auto play
video.loop = true; // set the video to loop.
video.muted = muted;
videoContainer = {  // we will add properties as needed
     video : video,
     ready : false,   
};
// To handle errors. This is not part of the example at the moment. Just fixing for Edge that did not like the ogv format video
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; // set the event to the play function that 
                                  // can be found below
function readyToPlayVideo(event){ // this is a referance to the video
    // the video may not match the canvas size so find a scale to fit
    videoContainer.scale = Math.min(
                         canvas.width / this.videoWidth, 
                         canvas.height / this.videoHeight); 
    videoContainer.ready = true;
    // the video can be played so hand it off to the display function
    requestAnimationFrame(updateCanvas);
    // add instruction
    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); 
    // only draw if loaded and ready
    if(videoContainer !== undefined && videoContainer.ready){ 
        // find the top left of the video on the canvas
        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;
        // now just draw the video the correct size
        ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);

        FX[currentFX](videoContainer.video);
        if(videoContainer.video.paused){ // if not playing show the paused screen 
            drawPayIcon();
        }
        overUI = false;
        cursor = "default";
        drawSlider();
        drawList();
        if(mouse.over){
            if(!overUI){
                if((mouse.button&1)===1){ // bit field
                    playClick = true;
                }
                if((mouse.button&1)===0 && playClick){ // bit field
                    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;
    }
    // all done for display 
    // request the next frame in 1/60th of a second
    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){  // bit field
            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 ){ //bit field
            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 = "black";  // darken display
    // ctx.globalAlpha = 0.5;
    // ctx.fillRect(0,0,canvas.width,canvas.height);
     ctx.fillStyle = "#DDD"; // colour of play icon
     ctx.globalAlpha = 0.75; // partly transparent
     ctx.beginPath(); // create the path for the icon
     var size = (canvas.height / 2) * 0.5;  // the size of the icon
     ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
     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; // restore alpha
}    


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.border.top = Number(element.style.borderTopWidth.replace(/[a-zA-Z]/g,""));
        //m.border.left = Number(element.style.borderLeftWidth.replace(/[a-zA-Z]/g,""));
    }
    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";
    }


}
// register the event
//canvas.addEventListener("click",playPauseClick);
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>

添加过滤器移动",突出显示每帧(移动)的变化.滑块更改突出显示更改的持久性.

Add filter "Movement" that highlight the change per frame (movement). Slider changes the persistence of the highlighted changes.

这篇关于将画布视频从灰度淡化到彩色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆