javascript - How to blur a specific region of a HTML5 video tag? - Stack Overflow

For a VueJS Project, I have a video player with HTML5 <video> tag. In this video, I want to displ

For a VueJS Project, I have a video player with HTML5 <video> tag. In this video, I want to display some blurring spots on the bottom left corner.

I am using a canvas, pure CSS method, and none of that worked.

In CSS: I used a filter: blur(20px) on a div in front of the video and it doesn't work, the blur affects the border of the div and not the center.

Image with blur test in css

With canvas, we try the same and I never get any single blur effect on it

I just need blur effect on the red part of the images :

The red part need to be blurred

<template>
<div>  
  <div class="contenant">
    <input  type='range' v-model="width" min="0" max="1281" value="0" >
    <img id='image' class="img" src="image1.jpg" alt="test">
    <div id='filtre' v-bind:style="{ width: width + 'px'}"></div>
  </div>
</div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    width: 0,
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this ponent only -->
<style scoped>
.contenant {
  width: fit-content;
}

#filtre {
  height: 96%;
  background-color: red;
  position: absolute;
  bottom: 0;
  opacity: 0.33;
  top: 8px;
  z-index: 2;
  filter: blur(50px);
}

.img{
  position:relative;
}

input[type=range] {
  width: 81%;
  -webkit-appearance: none;
  margin: 10px 0;
  position: absolute;
  z-index: 3;
  height: -webkit-fill-available;
  background-color: transparent;
}

input[type=range]::-webkit-slider-thumb {
  height: 26px;
  width: 26px;
  border-radius: 17px;
  background: #619BFF;
  cursor: pointer;
  -webkit-appearance: none;
}

For a VueJS Project, I have a video player with HTML5 <video> tag. In this video, I want to display some blurring spots on the bottom left corner.

I am using a canvas, pure CSS method, and none of that worked.

In CSS: I used a filter: blur(20px) on a div in front of the video and it doesn't work, the blur affects the border of the div and not the center.

Image with blur test in css

With canvas, we try the same and I never get any single blur effect on it

I just need blur effect on the red part of the images :

The red part need to be blurred

<template>
<div>  
  <div class="contenant">
    <input  type='range' v-model="width" min="0" max="1281" value="0" >
    <img id='image' class="img" src="image1.jpg" alt="test">
    <div id='filtre' v-bind:style="{ width: width + 'px'}"></div>
  </div>
</div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    width: 0,
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this ponent only -->
<style scoped>
.contenant {
  width: fit-content;
}

#filtre {
  height: 96%;
  background-color: red;
  position: absolute;
  bottom: 0;
  opacity: 0.33;
  top: 8px;
  z-index: 2;
  filter: blur(50px);
}

.img{
  position:relative;
}

input[type=range] {
  width: 81%;
  -webkit-appearance: none;
  margin: 10px 0;
  position: absolute;
  z-index: 3;
  height: -webkit-fill-available;
  background-color: transparent;
}

input[type=range]::-webkit-slider-thumb {
  height: 26px;
  width: 26px;
  border-radius: 17px;
  background: #619BFF;
  cursor: pointer;
  -webkit-appearance: none;
}
Share Improve this question edited Sep 8, 2020 at 0:24 Rafik Farhad 1,2022 gold badges14 silver badges21 bronze badges asked Oct 3, 2019 at 13:50 Yann ColinYann Colin 951 gold badge2 silver badges8 bronze badges 1
  • Wele to Stack Overflow! Questions seeking code help must include the shortest code necessary to reproduce it in the question itself preferably in a Stack Snippet. See How to create a Minimal, Reproducible Example – Paulie_D Commented Oct 3, 2019 at 13:50
Add a ment  | 

1 Answer 1

Reset to default 5

In supporting browsers it's as easy as using an overlapping element with a backdrop-filter: blur() CSS property.

// just make the div follow the mouse
const mouse = {
  x: 0,
  y: 0,
  dirty: false
};
const blurme = document.getElementById('soblurme');
document.querySelector('.container')
  .addEventListener('mousemove', (evt) => {
  mouse.x = evt.offsetX;
  mouse.y = evt.offsetY;
  // recently all UI events are already debounced by UAs,
  // but all vendors didn't catch up yet
  if( !mouse.dirty ) {
    requestAnimationFrame( move );
  }
  mouse.dirty = true;
});

function move() {
  blurme.style.left = (mouse.x - 25) + 'px';
  blurme.style.top = (mouse.y - 25) + 'px';
  mouse.dirty = false;
}
.container { position: relative; }
#soblurme {
  position: absolute;
  border: 1px solid white;
  pointer-events: none;
  width: 50px;
  height: 50px;  
  left: 70px;
  top: 20px;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
video {
  width: 100%;
  cursor: none;
}
<div class="container">
  <video autoplay muted controls>
    <source src="https://upload.wikimedia/wikipedia/mons/transcoded/2/22/Volcano_Lava_Sample.webm/Volcano_Lava_Sample.webm.360p.webm">
    <source src="https://dl.dropboxusercontent./s/bch2j17v6ny4ako/movie720p.mp4">
  </video>
  <div id="soblurme"></div>
</div>

For others you'll need to draw that part of the video again on a canvas:

const vid = document.querySelector('video');
const canvas = document.getElementById('soblurme');
const ctx = canvas.getContext('2d');

if( ctx.filter !== "none" ) {
  // in case 2DContext.filter is not supported (Safari), some libraries can do the blur for us
  // I'll let the readers choose the one they prefer and implement it
  console.warn( "we should use a falbback like StackBlur.js" );
}

const spread = 10;
ctx.filter = 'blur(' + spread + 'px)';

const border_width = 1; // because we add a css border around the canvas element

let playing = false;

vid.onplaying = startDrawing;
vid.onpause = stopDrawing;

function startDrawing() {
  playing = true;
  loop();
}
function stopDrawing() {
  playing = false;
}

function loop() {
  if( mouse.dirty ) {
    canvas.style.left = mouse.x + 'px';
    canvas.style.top = mouse.y + 'px';
    mouse.dirty = false;
  }
  draw();
  if( playing ) {
    requestAnimationFrame(loop);
  }
}
function draw() {
  const vid_rect = vid.getBoundingClientRect();
  const can_rect = canvas.getBoundingClientRect();
  const s_x = (can_rect.left - vid_rect.left) + border_width;
  const s_y = (can_rect.top - vid_rect.top) + border_width;
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // if we are lazy, we can draw the whole image
  // but the blur effect is quite heavy to calculate
//  ctx.drawImage(vid, -s_x, -s_y, vid_rect.width, vid_rect.height);

  // so for better performances we may prefer to calculate the smallest area to draw
  // because blur spreads we need to draw outside a little bit anyway
  const offset = spread * 2;
  const output_w = canvas.width + (offset * 2);
  const output_h = canvas.height + (offset * 2);
  const ratio_x = vid_rect.width / vid.videoWidth;
  const ratio_y = vid_rect.height / vid.videoHeight;
  
  ctx.drawImage(
    vid,
    (s_x - offset) / ratio_x, (s_y - offset) / ratio_y, output_w  / ratio_x, output_h / ratio_y,
    -offset, -offset, output_w, output_h 
  );
}

// move with mouse
const mouse = {
  x: 0,
  y: 0,
  dirty: false
};
document.querySelector('.container')
  .addEventListener( 'mousemove', ( evt ) => {
  mouse.x = evt.offsetX - canvas.width / 2;
  mouse.y = evt.offsetY - canvas.height / 2;
  if( !mouse.dirty && !playing ) {
    requestAnimationFrame( loop ); 
  }
  mouse.dirty = true;
});
.container { position: relative; }
#soblurme {
  position: absolute;
  border: 1px solid white;
  pointer-events: none;
  left: 70px;
  top: 20px;
}
video {
  width: 100%;
}
<div class="container">
  <video autoplay muted controls>
    <source src="https://upload.wikimedia/wikipedia/mons/transcoded/2/22/Volcano_Lava_Sample.webm/Volcano_Lava_Sample.webm.360p.webm">
    <source src="https://dl.dropboxusercontent./s/bch2j17v6ny4ako/movie720p.mp4">
  </video>
  <canvas id="soblurme" width="50" height="50"></canvas>
</div>

And to make it conditionally, we can feature detect it:

const supportsBackdropFilter = (function() {
  const style = document.createElement('_').style;
  style.cssText = 'backdrop-filter: blur(2px);webkit-backdrop-filter: blur(2px)';
  return style.length !== 0 &&
    (document.documentMode === undefined || document.documentMode > 9);
})();

So all together:

const supports_backdrop_filter = (function() {
  const style = document.createElement('_').style;
  style.cssText = 'backdrop-filter: blur(2px);-webkit-backdrop-filter: blur(2px);';
  return style.length !== 0 &&
    (document.documentMode === undefined || document.documentMode > 9);
})();

const mouse = {
  x: 0,
  y: 0,
  dirty: false
};
const vid = document.querySelector('video');
const canvas = document.getElementById('soblurme');
let playing = false;
const ctx = canvas.getContext('2d');
const spread = 10;
const border_width = 1; // because we add a css border around the canvas element
  
document.querySelector('.container')
  .addEventListener('mousemove', (evt) => {
  mouse.x = evt.offsetX;
  mouse.y = evt.offsetY;
  if( !mouse.dirty ) {
    if( supports_backdrop_filter ) {
      requestAnimationFrame( move );
    }
    else if( !playing ) {
      requestAnimationFrame( loop );
    }
  }
  mouse.dirty = true;
});

function move() {
  canvas.style.left = (mouse.x - 25) + 'px';
  canvas.style.top = (mouse.y - 25) + 'px';
  mouse.dirty = false;
}

// unsupporting browsers 
if( !supports_backdrop_filter ) {
  ctx.filter = 'blur(' + spread + 'px)';

  vid.onplaying = startDrawing;
  vid.onpause = stopDrawing;
}

function startDrawing() {
  playing = true;
  loop();
}
function stopDrawing() {
  playing = false;
}

function loop() {
  if( mouse.dirty ) {
    move();
  }
  draw();
  if( playing ) {
    requestAnimationFrame(loop);
  }
}
function draw() {
  const vid_rect = vid.getBoundingClientRect();
  const can_rect = canvas.getBoundingClientRect();
  const s_x = (can_rect.left - vid_rect.left) + border_width;
  const s_y = (can_rect.top - vid_rect.top) + border_width;
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  const offset = spread * 2;
  const output_w = canvas.width + (offset * 2);
  const output_h = canvas.height + (offset * 2);
  const ratio_x = vid_rect.width / vid.videoWidth;
  const ratio_y = vid_rect.height / vid.videoHeight;

  ctx.drawImage(
    vid,
    (s_x - offset) / ratio_x, (s_y - offset) / ratio_y, output_w  / ratio_x, output_h / ratio_y,
    -offset, -offset, output_w, output_h 
  );
}
.container { position: relative; }
#soblurme {
  position: absolute;
  border: 1px solid white;
  pointer-events: none;
  left: 70px;
  top: 20px;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
video {
  width: 100%;
  cursor: none;
}
<div class="container">
  <video autoplay muted controls>
    <source src="https://upload.wikimedia/wikipedia/mons/transcoded/2/22/Volcano_Lava_Sample.webm/Volcano_Lava_Sample.webm.360p.webm">
    <source src="https://dl.dropboxusercontent./s/bch2j17v6ny4ako/movie720p.mp4">
  </video>
  <canvas id="soblurme" width="50" height="50"></canvas>
</div>



OP asked in a ment so that the blur actually spreads instead of having a clear-cut like in the previous examples.

To do so in CSS, it should have been just a matter of adding an inner element on which we would set the backdrop-filter with half of the blurring along with some margin, and then add a blur on the element with the other half of the blur as a simple filter rule.
However, it seems there is a bug in current Blink where backdrop-filter will simply be discarded if one of the ancestors already had a blur() applied to it...

So this will currently only work in Safari:

// just make the div follow the mouse
const mouse = {
  x: 0,
  y: 0,
  dirty: false
};
const blurme = document.getElementById('soblurme');
document.querySelector('.container')
  .addEventListener('mousemove', (evt) => {
  mouse.x = evt.offsetX;
  mouse.y = evt.offsetY;
  // recently all UI events are already debounced by UAs,
  // but all vendors didn't catch up yet
  if( !mouse.dirty ) {
    requestAnimationFrame( move );
  }
  mouse.dirty = true;
});

function move() {
  blurme.style.left = (mouse.x - 25) + 'px';
  blurme.style.top = (mouse.y - 25) + 'px';
  mouse.dirty = false;
}
.container { position: relative; }
#soblurme {
  position: absolute;
  pointer-events: none;
  width: 50px;
  height: 50px;  
  left: 70px;
  top: 20px;
  --spread: 10px;
  filter: blur(calc(var(--spread) ));
}
#soblurme > div {
  width: calc(100% - var(--spread));
  height: calc(100% - var(--spread));
  backdrop-filter: blur(calc(var(--spread) / 2));
  -webkit-backdrop-filter: blur(calc(var(--spread) / 2));
  padding: 10px;
}
video {
  width: 100%;
  cursor: none;
}
<div class="container">
  <video autoplay muted controls>
    <source src="https://upload.wikimedia/wikipedia/mons/transcoded/2/22/Volcano_Lava_Sample.webm/Volcano_Lava_Sample.webm.360p.webm">
    <source src="https://dl.dropboxusercontent./s/bch2j17v6ny4ako/movie720p.mp4">
  </video>
 <div id="soblurme"> <!-- we apply a simple blur() here -->
  <div></div> <!-- this one will get the backdrop-filter -->
 </div>
</div>

However the good news is that the canvas version bees way simpler.
What we'll do is to simply draw the video on the canvas, and then apply a CSS filter:blur() on the canvas element directly from CSS.
Since we don't need to account for the spread, the calculations for drawImage are easier, but since we don't apply any filter, we can even use the lazy version that was in the ments of the second snippet:

const vid = document.querySelector('video');
const canvas = document.getElementById('soblurme');
const ctx = canvas.getContext('2d');

let playing = false;

vid.onplaying = startDrawing;
vid.onpause = stopDrawing;

function startDrawing() {
  playing = true;
  loop();
}
function stopDrawing() {
  playing = false;
}

function loop() {
  if( mouse.dirty ) {
    canvas.style.left = mouse.x + 'px';
    canvas.style.top = mouse.y + 'px';
    mouse.dirty = false;
  }
  draw();
  if( playing ) {
    requestAnimationFrame(loop);
  }
}
function draw() {
  const vid_rect = vid.getBoundingClientRect();
  const can_rect = canvas.getBoundingClientRect();
  const s_x = (can_rect.left - vid_rect.left);
  const s_y = (can_rect.top - vid_rect.top);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(vid, -s_x, -s_y, vid_rect.width, vid_rect.height);
}

// move with mouse
const mouse = {
  x: 0,
  y: 0,
  dirty: false
};
document.querySelector('.container')
  .addEventListener( 'mousemove', ( evt ) => {
  mouse.x = evt.offsetX - canvas.width / 2;
  mouse.y = evt.offsetY - canvas.height / 2;
  if( !mouse.dirty && !playing ) {
    requestAnimationFrame( loop ); 
  }
  mouse.dirty = true;
});
.container { position: relative; }
#soblurme {
  position: absolute;
  pointer-events: none;
  left: 70px;
  top: 20px;
  filter: blur(10px);
}
video {
  width: 100%;
}
<div class="container">
  <video autoplay muted controls>
    <source src="https://upload.wikimedia/wikipedia/mons/transcoded/2/22/Volcano_Lava_Sample.webm/Volcano_Lava_Sample.webm.360p.webm">
    <source src="https://dl.dropboxusercontent./s/bch2j17v6ny4ako/movie720p.mp4">
  </video>
  <canvas id="soblurme" width="50" height="50"></canvas>
</div>

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745650882a4638256.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信