将画布视频从灰度渐变为彩色

des*_*pet 3 javascript html5 canvas

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

有关如何实现这一目标的任何想法?或者......它甚至可能吗?

这是代码:

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);
Run Code Online (Sandbox Code Playgroud)

Bli*_*n67 5

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

黑白滤镜

做黑白滤镜很容易.

 // 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
Run Code Online (Sandbox Code Playgroud)

或者您可以将视频渲染到自身以获得其他FX,演示通过使用上面的代码和一些额外的图层显示黑白滤镜等等.

更多信息

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


演示

该演示展示了如何在我使用时进行黑白和其他一些FX.

如何使用.

查看视频标题以了解归因. 从左到右依次为

查看视频标题以了解归因.FX从左到右依次为"Lighten","Black&white","Sepia","Saturate"和"Negative".

该演示具有以下FX点亮,变暗,黑/白,负,饱和,棕褐色,B&W负片等.

Javascript

与问题相关的代码全部位于顶部并标记.其余的是UI加载等.

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

说明在演示中.

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

//==========================================================================
// 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);
Run Code Online (Sandbox Code Playgroud)
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;   
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


god*_*zsa 2

最简单的是在画布上设置灰度CSS过滤器。

var video = document.getElementById("myCanvas");
var button = document.getElementById("myButton");

function grey() {
  video.className += " greyscale";
}

function color() {
  classGreyscale = video.className.indexOf("greyscale");
  if (classGreyscale > 0)
    video.className = video.className.substring(0, video.className.length - 10)
}

button.addEventListener('click', function() {
  color();
});

grey();
Run Code Online (Sandbox Code Playgroud)
.greyscale {
  -webkit-filter: grayscale(100%);
  filter: grayscale(100%);
}

.transition {
  transition: all 1s;
  -webkit-transition: all 1s;
}
Run Code Online (Sandbox Code Playgroud)
<div>
  <img src="https://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png" id="myCanvas" class="transition" />
  <br/>
  <button id="myButton">to Colour</button>
</div>
Run Code Online (Sandbox Code Playgroud)

所以我们在这里做的是transition为我们的画布添加一个类,这样它就可以动画化这些变化。然后,当我们grayscale向其中添加一个类时,这会更改 css 过滤器以及过渡,使其淡入。当我们想让它再次变得丰富多彩时,我们删除该类greyscale

我用图像制作了这个示例,但它适用于所有内容。

这样,您就可以在所有参数上以 1 秒的过渡淡入类(此处为灰度)。最好使用这个,因为你不必计算灰度中的每个像素,它更干净。

请注意,您可以使用 jQuery addClass removeClass 来获得更简单、更清晰的灵魂。

另请注意,您应该使用权重对 r、g、b 进行平均: https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale