我有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);
答案 0 :(得分: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
或者您可以将视频渲染到自身以获得其他FX,该演示通过使用上面的代码和一些额外的图层显示黑白滤镜等等。
有关显示视频的详情,请参阅Display video inside canvas
该演示展示了如何在我使用时进行黑白和其他一些FX。
查看视频标题以了解归因。从左到右依次为FX&#34; Lighten&#34;,&#34; Black&amp; white&#34;,&#34; Sepia&#34;,&#34; Saturate&#34;,&#34; Negative&#34;。
该演示具有以下FX点亮,变暗,黑/白,负,饱和,棕褐色,B&amp; W负片等等。
与问题相关的代码全部位于顶部并标记。其余的是UI加载等。
每个FX都是一个函数,可以调用addMix
或addOverlay
来应用过滤器,如上面的代码段所示。 addMix
功能略有不同,因为它会在视频上绘制视频以获取FX而不是填充。
说明在演示中。
请注意并非所有浏览器都支持所有的复合模式(为什么?谁知道!! :()如果浏览器支持某种模式,也没有办法100%确定。安全的赌注是Firefox,Chrome,和Edge对于所有其他浏览器运气好..
//==========================================================================
// All the mix function are in this section
var FXMix = 1;
var addOverlay = function(type){
if(FXMix > 0){
ctx.globalCompositeOperation = type;
ctx.globalAlpha = FXMix;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
}
}
var addMix = function(type,video){
if(FXMix > 0){
ctx.globalCompositeOperation = type;
ctx.globalAlpha = FXMix;
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("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");
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);
&#13;
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;
}
&#13;
<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>
&#13;
答案 1 :(得分: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();
.greyscale {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
.transition {
transition: all 1s;
-webkit-transition: all 1s;
}
<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>
所以我们在这里做的是为我们的画布添加一个transition
类,以便它可以为更改设置动画。然后,当我们向它添加一个grayscale
类时,这将改变css过滤器以及过渡,以便淡入。当我们想要再次使它变得丰富多彩时,我们删除了greyscale
类。
我用图像制作了示例,但它可以处理所有内容。
这样你就可以在所有参数上进行1s过渡(这里是灰度)。最好使用它,因为你不必计算灰度中的每个像素,它更清晰。
请注意,您可以使用jQuery addClass removeClass来实现更简单,更清晰的灵魂。
另请注意,您应该使用权重对r,g,b求平均值: https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
答案 2 :(得分:0)
带转换和-webkit-transition的饱和度版本:-webkit-filter 10s;
这仅适用于Safari和Chrome。 代码用于悬停。
我猜与@godzsa非常相似
我能想到的另一种方法是在视频上创建一个具有更高索引的div,并播放白色饱和度。
对于Youtube视频工作, https://jsfiddle.net/yd215t9p/
对于图像,
div {
-webkit-filter:saturate(0.0);
-webkit-transition: -webkit-filter 10s; /* Safari */
transition: -webkit-filter 10s;
}
div:hover {
-webkit-filter:saturate(1.0);
}
<div>
<img src="https://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png" id="myCanvas" class="transition" />
</div>