-1

Is it possible to animate scanner effect with html5 canvas, to move glowing line up and down?

Effect example.

I want to use this effect on my html5 video window. If it is possible could you share code example or place where I have to start?

Streem
  • 628
  • 2
  • 12
  • 26

2 Answers2

2

Yes, it's possible. here's how you could achieve that ...

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 4,
    y = 4,
    speed = 1,
    isBottom = false;

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = '#07C';
    ctx.lineCap = 'round';
    ctx.shadowBlur = 18;
    ctx.shadowColor = "#07C";
    ctx.fillRect(x, y, 210, 10);
 
    if (!isBottom && y < canvas.height - 14) y += speed;
    else if (y === canvas.height - 14) isBottom = true;
 
    if (isBottom && y > 4) y -= speed;
    else if (y === 4) isBottom = false;
 
    requestAnimationFrame(draw);
}

draw();
body{margin:0;overflow:hidden}canvas{border:1px solid #d3d3d3}
<canvas id="canvas" width="218" height="218"></canvas>
ɢʀᴜɴᴛ
  • 32,025
  • 15
  • 116
  • 110
  • Thanks a lot for a clear and simple code with fantastic results. One more question when I use this effect with a web camera stream with every frame the speed is increasing to up unlimited speed. Is there a method to limit the speed? – Streem Apr 29 '17 at 10:48
  • @Streem You mean, the bar moving too fast? If so then, you can decrease the speed by using a floating point number, like `0.5` or so.. – ɢʀᴜɴᴛ Apr 29 '17 at 11:06
  • thank you for your help I appreciate it. I do not know why but when I use this nice effect on the video, with every frame bar move speed is increasing, I mean it starts slowly and after 10 secs it is moving so fast like crazy:)) I will try to upload my code to jsfiddle this evening to see what I mean:) – Streem Apr 29 '17 at 13:30
  • Yes, that would be better. *upload it to jsfiddle* – ɢʀᴜɴᴛ Apr 29 '17 at 13:37
  • @ɢʀᴜɴᴛ how can we control the height and depth up to which this bar should move. like i don't want this bar to go till top and neither come to bottom instead just move in between for some length. please can you help. – Kunal Pal May 30 '19 at 19:12
  • how do you add an image below the bar? – Anass Kartit Sep 29 '22 at 15:40
1

Videos and the Canvas.

To achieve the following animated glowing cross fade to background FX using HTML5 canvas and the 2D API

Image showing glowing bar width video on left and black on right

Image attribution can be found in the opening and closing title/credits of the video in the demo at the bottom of this answer

Crop using a mask.

The FX can be achieved using a mask to mask out part of the video you don't want seen.

Setting ctx.globalCompositeoperation to "destination-out" will remove any existing pixels that you render over until you turn off the composite operation with ctx.globalCompositeoperation = "source-over"; (the default setting) You can get the reverse FX by using "destination-in"

As you want a background to show at the split you can use comp mode "destination-atop" to draw the background over the masked video. The background can be anything from just blank black (in code below) or another video source to create a sort of cross fade FX.

Note If you leave the background transparent then the glowing bar will not correctly do the lighter FX on the canvas background (DOM background)

Note both code snippets assume the video has already been rendered onto the canvas.

// direction is the direction of the sweeping FX
// FXMix is the position of the mask normalised for canvas width or height
//       FXMix has a range of 0-1 inclusive with 0 being top or left and
//       1 being bottom or right.
// negate if true reverses the FX mask.
// Assuming that canvas and ctx (2D context) are in scope of this function
function mask(direction, FXMix, negate){
    ctx.globalCompositeOperation = negate ? "destination-out" : "destination-in"; 
    ctx.fillStyle = "black";
    ctx.globalAlpha = 1;
    if(direction === "Vertical"){
        ctx.fillRect(0, 0, canvas.width, canvas.height * FXMix);
    }else if(direction === "Horizontal"){
        ctx.fillRect(0, 0, canvas.width  * FXMix, canvas.height);
    }
    ctx.globalCompositeOperation = "destination-atop"; 
    // assuming background is black. But you could use anything including a second video
    // using "destination-atop" only draws on transparent pixels.
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.globalCompositeOperation = "source-over"; 
}

Simple glowing bar.

The scanning light FX can be then drawn over the top using a gradient and fillRect. To give a bit more body to the glow I draw it twice, once without any compositing FX and the second time a little larger using ctx.globalCompositeOperation = "lighter". Rather than have to move the gradient I use setTransform to position the gradient. If you did not want to change the glow bar colour you could then just create the gradient once saving a little CPU time.

// RGB is an array of channel values for R,G,B in the range 0-255
//     NOTE RGB must be integer values. Please floor them befor passing
// direction is the direction of the sweeping FX
// FXMix is the position of the mask normalised for canvas width or height
//       FXMix has a range of 0-1 inclusive with 0 being top or left and
//       1 being bottom or right.
// negate if true reverses the FX mask.
// Assuming that canvas and ctx (2D context) are in scope of this function
// size is size of bar as a fraction pf canvas width or height. 
//     A good value is 0.1
function glowBar(RGB, direction, FXMix, size){ 
    var grad;
    var col = "rgba("+RGB[0]+","+RGB[1]+","+RGB[2]+",";
    if(direction === "Vertical"){
        grad = ctx.createLinearGradient(0,0,0,canvas.height * size);
    }else if(direction === "Horizontal"){
        grad = ctx.createLinearGradient(0,0,canvas.width * size,0);
    }
    grad.addColorStop(0.0, col + "0.0)");
    grad.addColorStop(0.3, col + "0.8)");
    grad.addColorStop(0.4, col + "1.0)");
    grad.addColorStop(0.6, col + "1.0)");
    grad.addColorStop(0.7, col + "0.8)");
    grad.addColorStop(1.0, col + "0.0)");
    ctx.fillStyle = grad;
    if(direction === "Vertical"){
        // draw first time with standard comp 
        ctx.setTransform(1,0,0,1,0,canvas.height * FXMix - canvas.height * size * 0.5);
        ctx.fillRect(0, 0, canvas.width, canvas.height * size);
        // draw second time slightly bigger and with lighten to get a glow FX

        ctx.setTransform(1,0,0,1.1,0,canvas.height * FXMix -canvas.height * size * 0.5 * 1.1 );
        ctx.globalCompositeOperation = "lighter"; 
        ctx.fillRect(0, 0, canvas.width, canvas.height *size);
    }else if(direction === "Horizontal"){
        ctx.setTransform(1,0,0,1,canvas.width * FXMix -canvas.width * size * 0.5, 0);
        ctx.fillRect(0, 0, canvas.width * size, canvas.height);
        ctx.setTransform(1.1,0,0,1,canvas.width * FXMix -canvas.width * size * 0.5 * 1.1, 0);
        ctx.globalCompositeOperation = "lighter"; 
        ctx.fillRect(0, 0, canvas.width * size, canvas.height);
    }
    ctx.globalCompositeOperation = "source-over"; 
    ctx.setTransform(1,0,0,1,0,0);
}

Demo.

The following demo is an update on an existing demo I used in the answer to Fade from black & white to color I have added 2 FX to the FX list you can find if you hover the mouse over the right of the video (once loaded) V scanner and H scanner will animate but can be manually controlled by the mix slider you can find if you hover over the bottom of the video.

The FX are just slight modification of the above two snippets functions mask and glowBar.

Video attribution can be found in the opening and closing title/credits of the video ( as specified by author/s)

    //==========================================================================
    // start canvas animation after all the code below has been parsed and executed
    setTimeout(()=>requestAnimationFrame(updateCanvas),0);
    //==========================================================================
    // UI variables
    var showFXName = 0;
    var cursor = "default";
    var overUI = false;
    var sliderAlpha = 1;
    var listAlpha = 1;
    var dragging = false;
    var listWidth = null;
    var playClick = false;


    //==========================================================================
    // Media setup
    
    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
    //==========================================================================
    var FXMix = 1;
    var FX = {}
    var FXList = [];
    var currentFX = "";
    var animateFXMix = false; // if true thr FXMix is animated over time. This flag is cleared to false once a frame

    //==========================================================================
    // Mix function are in this section
    function addOverlay(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"; 
        }
    }
    function addMix(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"; 
        }
    }
    function fill(style){  // draws a rectangle over the canvas using style as the colour
        ctx.globalAlpha = FXMix;
        ctx.fillStyle = style;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.globalAlpha = 1;
    }
    function mask(direction){
        ctx.globalCompositeOperation = "destination-in"; 
        ctx.fillStyle = "black";
        ctx.globalAlpha = 1;
        if(direction === "Vertical"){
            ctx.fillRect(0, 0, canvas.width, canvas.height * FXMix);
        }else if(direction === "Horizontal"){
            ctx.fillRect(0, 0, canvas.width  * FXMix, canvas.height);
        }
        ctx.globalCompositeOperation = "destination-atop"; 
        // assuming bacground is black. But you could use anything including a second video
        // using "destination-atop" only draws on transparent pixels.
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.globalCompositeOperation = "source-over"; 
    }
    function glowBar(RGB, direction){ // RGB is array of channel values 0-255
        var grad;
        var col = "rgba("+RGB[0]+","+RGB[1]+","+RGB[2]+",";
        if(direction === "Vertical"){
            grad = ctx.createLinearGradient(0,0,0,canvas.height * 0.1);
        }else if(direction === "Horizontal"){
            grad = ctx.createLinearGradient(0,0,canvas.width * 0.1,0);
        }
        grad.addColorStop(0.0, col + "0.0)");
        grad.addColorStop(0.3, col + "0.8)");
        grad.addColorStop(0.4, col + "1.0)");
        grad.addColorStop(0.6, col + "1.0)");
        grad.addColorStop(0.7, col + "0.8)");
        grad.addColorStop(1.0, col + "0.0)");
        ctx.fillStyle = grad;
        if(direction === "Vertical"){
            ctx.setTransform(1,0,0,1,0,canvas.height * FXMix - canvas.height * 0.05);
            ctx.fillRect(0, 0, canvas.width, canvas.height * 0.1);
            ctx.setTransform(1,0,0,1.1,0,canvas.height * FXMix -canvas.height * 0.05 * 1.1 );
            ctx.globalCompositeOperation = "lighter"; 
            ctx.fillRect(0, 0, canvas.width, canvas.height * 0.1);
        }else if(direction === "Horizontal"){
            ctx.setTransform(1,0,0,1,canvas.width * FXMix -canvas.width * 0.05, 0);
            ctx.fillRect(0, 0, canvas.width * 0.1, canvas.height);
            ctx.setTransform(1.1,0,0,1,canvas.width * FXMix -canvas.width * 0.05 * 1.1, 0);
            ctx.globalCompositeOperation = "lighter"; 
            ctx.fillRect(0, 0, canvas.width * 0.1, canvas.height);
        }
        ctx.globalCompositeOperation = "source-over"; 
        ctx.setTransform(1,0,0,1,0,0);
    }
    function addFX(name, func) {
        FXList.push(name);
        FX[name] = func;
        currentFX = name;
    }

    //==========================================================================
    // avialable composite operations 
    // multiply,screen,overlay,color-dodge,color-burn,hard-light,soft-light,difference,
    // exclusion,hue,saturation,color,luminosity
    addFX("V scanner",   (vid)=>{ animateFXMix = true; mask("Vertical"); glowBar([100,200,255], "Vertical") })
    addFX("H scanner",   (vid)=>{ animateFXMix = true; mask("Horizontal"); glowBar([100,255,200], "Horizontal") })
    addFX("Ligher",      (vid)=>{ addMix("lighter",vid) } );
    addFX("Ligher+",     (vid)=>{ addMix("lighter", vid); addMix("lighter",vid); addMix("lighter", vid) } );
    addFX("Darken",      (vid)=>{ addMix("multiply", vid) } );
    addFX("Darken+",     (vid)=>{ addMix("multiply", vid); addMix("multiply",vid); addMix("multiply", vid) } );
    addFX("Saturate",    ()   =>{ ctx.fillStyle = "#F00"; addOverlay("saturation") });
    addFX("Negative",    (vid)=>{ ctx.fillStyle = "#FFF"; addOverlay("difference") } );
    addFX("Sepia",       (vid)=>{ fill("#F94"); addMix("luminosity", vid) } );
    addFX("BlackWhite",  (vid)=>{ ctx.fillStyle = "#888"; addOverlay("color") } );
    addFX("B&W Negative",(vid)=>{ ctx.fillStyle = "#FFF";   addOverlay("difference"); ctx.fillStyle = "#888"; addOverlay("color") } );
    addFX("B&W Lighten", (vid)=>{ addMix("lighter", vid); ctx.fillStyle = "#888"; addOverlay("color") } );
    addFX("None",        ()   =>{});

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


    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

        // add instruction
        document.getElementById("playPause").textContent = "Click video to play/pause.";
        document.querySelector(".mute").textContent = "Mute";
    }
    function updateCanvas(time){
        ctx.fillStyle = "#222";
        ctx.fillRect(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;
            if(animateFXMix){
                FXMix = Math.sin(time / 400) * 0.5 + 0.5;
                FXMix = FXMix < 0 ? 0 : FXMix > 1 ? 1 : FXMix; // Clamp to 0-1 or may get flicker on some FX
                
                animateFXMix = false;
            }
        }else{
            drawLoadingAnim(time);
        }
        // all done for display 
        // request the next frame in 1/60th of a second
        requestAnimationFrame(updateCanvas);
    }
    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;
        }
        if(dragging){
            animateFXMix = false;  // dont animate if slider being dragged
        }
        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;
            }
        }
        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"; // 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
    }
    function drawLoadingAnim(time){
         ctx.strokeStyle = "#8DF"; // colour of play icon
         ctx.lineCap = "round";
         ctx.globalAlpha = 0.75; // partly transparent
         ctx.lineWidth = 6;
         ctx.beginPath(); // create the path for the icon
         var size = (canvas.height / 2) * 0.5;  // the size of the icon
         ctx.arc(canvas.width / 2 , canvas.height / 2, size, time / 100, time / 100 + 2);
         ctx.stroke();
         ctx.lineWidth = 1;
         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. V and H scanner FX are animated. You can use the slider to move the glowbar position manualy. 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>
Graham
  • 7,431
  • 18
  • 59
  • 84
Blindman67
  • 51,134
  • 11
  • 73
  • 136