Как сделать Radial blur с помощью SVG?

130
02 ноября 2019, 14:10

Такой вопрос, как сделать Radial blur с помощью SVG? Порыв в гугле я понял что либо он в SVG не так называется, либо способа нет...

Answer 1

Это конечно не CSS, как и в других моих постах последнее время, будем окучивать WebGL / glsl, (мало внимания ему уделяется, это заметно по просмотрам)

Я написал маленькую функцию, чтобы вынести за скобки весь WebGL код, оставив один лишь фрагментный шейдер на входе:

document.body.append(webglFilter("https://i.imgur.com/cdqfoqZ.png",  `
    const int samples = 22;               // кол-во сэмплов
    const float power = 0.001;            // сила эффекта
    const vec2 center = vec2( 0.5, 0.5 ); // центр эффекта
    // матрица поворота
    mat2 rotate2d (float angle) {
        vec2 sc = vec2( sin(angle), cos(angle) );
        return mat2( sc.y, -sc.x, sc.xy );
    }
    vec4 frag (vec2 uv) {
        vec4 color = vec4(0.);                          // аккумулятор
        for (int i = 0; i < samples; i++) {
            float dir = sin(length(uv - center)*200.);  // направление повотора
            dir = smoothstep(-.5, .5, dir) - .5;        // делаем из синусоиды сглаженную 
                                                        // ступенчатую функцию
            uv -= center;                               // сдвиг к центру поворота
            uv *= rotate2d( dir * power * float(i) );   // поворот
            uv += center;                               // обратный сдвиг
            color += sample(uv);                        // получить цвет
        }   
        return color / float(samples);                  // взять среднее
    }
`));

UPD1: попытался лучше повторить эффект на оригинальной картинке

UPD2: добавлена реакция на мышь

UPD3: radial blur теперь применяется плавно

UPD4: на мобилке теперь видно эффекты

UPD5: добавил еще сниппет

Версия со ступенчатой функцией поворота

let filter = webglFilter("https://i.imgur.com/cdqfoqZ.png", ` 
 
  const int samples = 22; 
  uniform float power; 
  uniform vec2 mouse; 
 
  mat2 rotate2d (float angle) { 
    vec2 sc = vec2( sin(angle), cos(angle) ); 
    return mat2( sc.y, -sc.x, sc.xy ); 
  } 
 
  vec4 frag (vec2 uv) { 
 
    float rotateDir = sin(length(uv - mouse)*1./(0.005 + power*5.)); 
    rotateDir = smoothstep(-.3, .3, rotateDir)-.5; 
 
    vec2 shiftDir = (uv-mouse)*vec2(-1.0,-1.0); 
 
    vec4 color = vec4(0.); 
    for (int i = 0; i < samples; i ++) { 
      uv += float(i)/float(samples)*shiftDir*0.01; 
      uv -= mouse;       
      uv *= rotate2d( rotateDir * power * float(i));  
      uv += mouse; 
      color += sample(uv) / float(samples); 
    }  
    return color; 
  } 
 
`); 
 
  let changeCenter = function(e) { 
    e = e.touches ? e.touches[0] : e; 
    let c = filter.canvas; 
    let z = window.getComputedStyle(c).zoom; 
    let d = document.documentElement; 
    let x = (e.clientX + d.scrollLeft - c.offsetLeft*z) / c.width / z; 
    let y = (e.clientY + d.scrollTop - c.offsetTop*z) / c.height / z 
    filter.uniform('2f', 'mouse', x, y).apply(); 
  } 
 
  var applyEffect = (function() { 
   
    let power = 0; 
    let targ = 0 
    let started = 0; 
 
    return function (pow) { 
      targ = pow; 
      started = new Date().getTime(); 
      requestAnimationFrame(animate); 
    } 
 
    function animate() { 
      let dt = new Date().getTime() - started;  
      power += dt * 1e-6 * (targ === 0 ? -1 : 1); 
      power = Math[targ === 0 ? 'max' : 'min'](power, targ); 
      filter.uniform('1f', 'power', power).apply(); 
      Math.abs(power-targ) > 1e-7 && requestAnimationFrame(animate) 
    } 
 
  })(); 
 
filter.ready = function() { 
 
  let c = filter.canvas; 
  document.body.append(c); 
  let z = window.getComputedStyle(c).zoom; 
  changeCenter({ 
      clientX: c.width/2*z, 
      clientY: c.height/2*z, 
  }); 
  applyEffect(0.001); 
   
  filter.apply(); 
   
  window.addEventListener('mousemove', e => changeCenter(e)) 
  window.addEventListener('touchmove', e => changeCenter(e)) 
  window.addEventListener('mouseup', () => applyEffect(0)) 
  window.addEventListener('touchend', () => applyEffect(0)) 
  window.addEventListener('mousedown', () => applyEffect(0.001)) 
  window.addEventListener('touchstart', () => applyEffect(0.001)) 
   
 
}
canvas { 
  zoom: 33%; 
}
<script> 
function webglFilter(url, fragCode) { 
   
  let canvas = document.createElement('canvas'); 
  let pid, gl = canvas.getContext('webgl')  
        || canvas.getContext('experimental-webgl'); 
   
  let loader = new Image(); 
  loader.crossOrigin = "anonymous"; 
  loader.src = url; 
   
  loader.onload = function() {  
     
    canvas.width = loader.width; 
    canvas.height = loader.height; 
 
    pid = gl.createProgram(); 
 
    shader(` 
      attribute vec2 coords; 
        void main(void) { 
        gl_Position = vec4(coords.xy, 0.0, 1.0); 
      } 
    `, gl.VERTEX_SHADER); 
 
    shader(` 
      precision highp float; 
      uniform sampler2D texture; 
 
      vec4 sample(vec2 uv) { 
          return texture2D(texture, uv); 
      } 
 
      ${fragCode} 
 
      void main(void) { 
          gl_FragColor = frag(vec2(  
            gl_FragCoord.x / ${canvas.width}.,   
            1. - gl_FragCoord.y / ${canvas.height}.  
          )); 
      } 
    `, gl.FRAGMENT_SHADER); 
 
    gl.linkProgram(pid); 
    gl.useProgram(pid); 
 
    let array = new Float32Array([-1,  3, -1, -1, 3, -1]); 
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 
    gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); 
 
    let al = gl.getAttribLocation(pid, "coords"); 
    gl.vertexAttribPointer(al, 2, gl.FLOAT, false, 0, 0); 
    gl.enableVertexAttribArray(al); 
 
    let texture = gl.createTexture(); 
    gl.bindTexture(gl.TEXTURE_2D, texture); 
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, loader); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 
 
    var textureLocation = gl.getUniformLocation(pid, "texture"); 
    gl.uniform1i(textureLocation, 0); 
  
    filter.ready && filter.ready(); 
     
    function shader(src, type) { 
      let sid = gl.createShader(type); 
      gl.shaderSource(sid, src); 
      gl.compileShader(sid); 
      var message = gl.getShaderInfoLog(sid); 
      gl.attachShader(pid, sid); 
      if (message.length > 0) { 
        console.log(src.split('\n').map(function (str, i) { 
          return ("" + (1 + i)).padStart(4, "0") + ": " + str 
        }).join('\n')); 
        throw message; 
      } 
    } 
  } 
   
  let filter = { 
     
    canvas: canvas, 
     
    ready: null, 
     
    uniform: function(type, name, v1, v2, v3, v4) { 
      if (!pid) 
        throw new Error('image not loaded yet'); 
      var ul = gl.getUniformLocation(pid, name); 
      gl['uniform' + type](ul, v1, v2, v3, v4); 
      return filter; 
    }, 
     
    apply: function() { 
      if (!pid) 
        throw new Error('image not loaded yet'); 
      gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); 
      gl.clearColor(0, 0, 0, 0); 
      gl.drawArrays(gl.TRIANGLES, 0, 3); 
      return filter; 
    } 
  } 
   
  return filter; 
} 
</script>
Обычный radial blur:

let filter = webglFilter("https://i.imgur.com/tbmyMTo.jpg", ` 
 
  const int samples = 66; 
  uniform float power; 
  uniform vec2 mouse; 
 
  mat2 rotate2d (float angle) { 
    vec2 sc = vec2( sin(angle), cos(angle) ); 
    return mat2( sc.y, -sc.x, sc.xy ); 
  } 
 
  vec4 frag (vec2 uv) { 
 
    float rotateDir = length(uv - mouse)*1./(0.005 + power*5.); 
    rotateDir = smoothstep(-.3, .3, rotateDir)-.5; 
 
    vec2 shiftDir = (uv-mouse)*vec2(-1.0,-1.0); 
 
    vec4 color = vec4(0.); 
    for (int i = 0; i < samples; i ++) { 
      uv += float(i)/float(samples)*shiftDir*0.01; 
      uv -= mouse;       
      uv *= rotate2d( rotateDir * power * float(i));  
      uv += mouse; 
      color += sample(uv)/float(samples+i); 
    }  
    return color*1.5; 
  } 
 
`); 
 
  let changeCenter = function(e) { 
    e = e.touches ? e.touches[0] : e; 
    let c = filter.canvas; 
    let z = window.getComputedStyle(c).zoom; 
    let d = document.documentElement; 
    let x = (e.clientX + d.scrollLeft - c.offsetLeft*z) / c.width / z; 
    let y = (e.clientY + d.scrollTop - c.offsetTop*z) / c.height / z 
    filter.uniform('2f', 'mouse', x, y).apply(); 
  } 
 
  var applyEffect = (function() { 
   
    let power = 0; 
    let targ = 0 
    let started = 0; 
 
    return function (pow) { 
      targ = pow; 
      started = new Date().getTime(); 
      requestAnimationFrame(animate); 
    } 
 
    function animate() { 
      let dt = new Date().getTime() - started;  
      power += dt * 1e-6 * (targ === 0 ? -1 : 1); 
      power = Math[targ === 0 ? 'max' : 'min'](power, targ); 
      filter.uniform('1f', 'power', power).apply(); 
      Math.abs(power-targ) > 1e-7 && requestAnimationFrame(animate) 
    } 
 
  })(); 
 
filter.ready = function() { 
   
  let c = filter.canvas; 
  document.body.append(c); 
  let z = window.getComputedStyle(c).zoom; 
  changeCenter({ 
      clientX: c.width/2*z, 
      clientY: c.height/2*z, 
  }); 
  applyEffect(0.001); 
   
  filter.apply(); 
   
  window.addEventListener('mousemove', e => changeCenter(e)) 
  window.addEventListener('touchmove', e => changeCenter(e)) 
  window.addEventListener('mouseup', () => applyEffect(0)) 
  window.addEventListener('touchend', () => applyEffect(0)) 
  window.addEventListener('mousedown', () => applyEffect(0.001)) 
  window.addEventListener('touchstart', () => applyEffect(0.001)) 
   
 
}
canvas { 
zoom:33% 
}
<script> 
function webglFilter(url, fragCode) { 
   
  let canvas = document.createElement('canvas'); 
  let pid, gl = canvas.getContext('webgl')  
        || canvas.getContext('experimental-webgl'); 
   
  let loader = new Image(); 
  loader.crossOrigin = "anonymous"; 
  loader.src = url; 
   
  loader.onload = function() {  
     
    canvas.width = loader.width; 
    canvas.height = loader.height; 
 
    pid = gl.createProgram(); 
 
    shader(` 
      attribute vec2 coords; 
        void main(void) { 
        gl_Position = vec4(coords.xy, 0.0, 1.0); 
      } 
    `, gl.VERTEX_SHADER); 
 
    shader(` 
      precision highp float; 
      uniform sampler2D texture; 
 
      vec4 sample(vec2 uv) { 
          return texture2D(texture, uv); 
      } 
 
      ${fragCode} 
 
      void main(void) { 
          gl_FragColor = frag(vec2(  
            gl_FragCoord.x / ${canvas.width}.,   
            1. - gl_FragCoord.y / ${canvas.height}.  
          )); 
      } 
    `, gl.FRAGMENT_SHADER); 
 
    gl.linkProgram(pid); 
    gl.useProgram(pid); 
 
    let array = new Float32Array([-1,  3, -1, -1, 3, -1]); 
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 
    gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); 
 
    let al = gl.getAttribLocation(pid, "coords"); 
    gl.vertexAttribPointer(al, 2, gl.FLOAT, false, 0, 0); 
    gl.enableVertexAttribArray(al); 
 
    let texture = gl.createTexture(); 
    gl.bindTexture(gl.TEXTURE_2D, texture); 
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, loader); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 
 
    var textureLocation = gl.getUniformLocation(pid, "texture"); 
    gl.uniform1i(textureLocation, 0); 
  
    filter.ready && filter.ready(); 
    filter.apply(); 
     
    function shader(src, type) { 
      let sid = gl.createShader(type); 
      gl.shaderSource(sid, src); 
      gl.compileShader(sid); 
      var message = gl.getShaderInfoLog(sid); 
      gl.attachShader(pid, sid); 
      if (message.length > 0) { 
        console.log(src.split('\n').map(function (str, i) { 
          return ("" + (1 + i)).padStart(4, "0") + ": " + str 
        }).join('\n')); 
        throw message; 
      } 
    } 
  } 
   
  let filter = { 
     
    canvas: canvas, 
     
    ready: null, 
     
    uniform: function(type, name, v1, v2, v3, v4) { 
      if (!pid) 
        throw new Error('program not ready'); 
      var ul = gl.getUniformLocation(pid, name); 
      gl['uniform' + type](ul, v1, v2, v3, v4); 
      return filter; 
    }, 
     
    apply: function() { 
      if (!pid) 
        throw new Error('program not ready'); 
      gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); 
      gl.clearColor(0, 0, 0, 0); 
      gl.drawArrays(gl.TRIANGLES, 0, 3); 
      return filter; 
    } 
  } 
   
  return filter; 
} 
</script>
Самая актуальная версия тут

Answer 2

Не претендую на участие, ибо подхожу по меткам лишь частично, да и реализация - "костыль костыльный". Но, тем не менее, пока не дали ответы с SVG, нативный вариант:

var nDivs = 90; 
var oWrap = document.querySelector('div.radial_blur'); 
var oDiv = document.createElement('DIV'); 
var oTemp; 
 
/* Раскомментировать, если нужен только эффект, без анимации 
while (nDivs--) { 
  oTemp = oWrap.querySelector('.radial_blur *:empty').appendChild(oDiv.cloneNode(true)); 
  oTemp.style.transform = 'rotate(1deg)'; 
} 
*/ 
 
/* Код ниже, только для наглядной демонстрации */ 
function radialBlur() { 
  oTemp = oWrap.querySelector('.radial_blur *:empty').appendChild(oDiv.cloneNode(true)); 
  oTemp.style.transform = 'rotate(1deg)'; 
  nDivs--; 
  if (!nDivs) { clearInterval(nIntervId); } 
} 
var nIntervId = window.setInterval(radialBlur, 80);
.radial_blur { 
  margin: 20px auto; 
  height: 400px; 
  width: 435px; 
  overflow: hidden; 
  border: 3px solid #000; 
  background: url('https://i.imgur.com/CQvNGGD_d.jpg?maxwidth=520&shape=thumb&fidelity=high') center/cover; 
  /* Можно попробовать с другим изображением 
  background: url('https://i.imgur.com/cdqfoqZ.png') center/cover; */ 
} 
 
.radial_blur div { 
  height: inherit; 
  width: inherit; 
  background: inherit; 
  opacity: .95; 
}
<div class="radial_blur"> 
  <div></div> 
</div>

Answer 3

Вариант SVG

Конечный вид очень сильно зависит от выбранной картинки.
При одинаковой схеме решения: последовательное применении радиальных градиентов, набора SVG фильтров, всё равно приходится тонко регулировать атрибуты всех SVG компонентов, чтобы получить требуемые эффекты.

  • У радиальных градиентов регулируется атрибуты cx="0.5" cy="0.55" r="0.16" для получения центра фокусировки и придания тунельной переспективы.
  • У фильтров регулируется baseFrequency="0.01 0.01" Чем меньше цифры тем больше искажение. seed="12"- коэффициент начального искажения
    scale="30" - коэффициент увеличения искажения

Добавлена анимация искажения при наведении мыши

.container { 
width:100%; 
height:100%; 
}
<div class="container"> 
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"  
    xmlns:xlink="http://www.w3.org/1999/xlink" 
       width="900" height="600" viewBox="0 0 900 600" preserveAspectRatio="xMinYMin meet" style="border:1px solid gray;" >   
 
	<defs> 
    <mask id="msk1"  >  
	  <rect fill="white" width="100%" height="100%"  /> 
  
	 <circle stroke="black"  fill="white" cx="450" cy="300" r="450" stroke-width="0" /> 
	 <circle stroke="black"  fill="white" cx="450" cy="300" r="350" stroke-width="0" /> 
     
	</mask>	 
	 
	<filter id="filter1"> 
     <feTurbulence type="turbulence" baseFrequency="0.01 0.01" 
        numOctaves="1" result="turbulence" seed="12"/> 
    <feDisplacementMap in2="turbulence" in="SourceGraphic" 
        scale="5" xChannelSelector="R" yChannelSelector="B"/> 
  </filter>	    
 
  <filter id="goovey"> 
		    <feTurbulence type="fractalNoise" baseFrequency="0.015"  numOctaves="3" result="warpper"/> 
          <feColorMatrix in="warpper" type="hueRotate" values="0" > 
                <animate attributeType="XML" attributeName="values" values="0;150;0" dur="5s" repeatCount="indefinite" begin="svg1.mouseover" end="svg1.mouseout"/>  
          </feColorMatrix> 
          <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="25" in="SourceGraphic"/> 
		  <feGaussianBlur stdDeviation="1"/> 
  </filter> 
	 <radialGradient id="grad1" cx="0.5" cy="0.55" r="0.16"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="15%" stop-color="#BED0DC" stop-opacity="1" />     
		  <stop offset="55%" stop-color="white"/> 
		  <stop offset="90%" stop-color="#BED0DC" stop-opacity="1"/> 
		   
    </radialGradient>  
		 
	    <radialGradient id="grad2" cx="0.5" cy="0.6" r="0.06"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="100%" stop-color="#BED0DC" stop-opacity="0.9" />     
		</radialGradient>  
	 
  </defs>  
  
  <g>  
  <image id="img1"   filter="url(#goovey)" xlink:href="https://i.stack.imgur.com/1Ty5b.jpg"  width="100%" height="100%"  />  
   <rect width="100%" height="100%"  fill="url(#grad2)"  fill-opacity="0.6" /> 
  </g>   
 </svg>	  
</div>

Второй вариант.

Другая картинка и настройки немного другие

.container { 
width:100%; 
height:100%; 
}
<div class="container"> 
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"  
    xmlns:xlink="http://www.w3.org/1999/xlink" 
       width="900" height="600" viewBox="0 0 900 600" preserveAspectRatio="xMinYMin meet" >   
 
	<defs> 
    <mask id="msk1"  >  
	  <rect fill="white" width="100%" height="100%"  /> 
  
	 <circle stroke="black"  fill="white" cx="450" cy="300" r="450" stroke-width="0" /> 
	 <circle stroke="black"  fill="white" cx="450" cy="300" r="350" stroke-width="0" /> 
     
	</mask>	 
	 
	<filter id="filter1"> 
     <feTurbulence type="turbulence" baseFrequency="0.01 0.01" 
        numOctaves="1" result="turbulence" seed="12"/> 
    <feDisplacementMap in2="turbulence" in="SourceGraphic" 
        scale="5" xChannelSelector="R" yChannelSelector="B"/> 
  </filter>	    
 
  <filter id="goovey"> 
		    <feTurbulence type="fractalNoise" baseFrequency="0.01"  numOctaves="3" result="warpper"/> 
          <feColorMatrix in="warpper" type="hueRotate" values="0" > 
                <animate attributeType="XML" attributeName="values" values="0;150;0" dur="8s" repeatCount="indefinite" begin="svg1.mouseover" end="svg1.mouseout"/>  
          </feColorMatrix> 
          <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic"/> 
    </filter> 
	 <radialGradient id="grad1" cx="0.5" cy="0.6" r="0.19"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="52%" stop-color="#BED0DC" stop-opacity="0.5" />     
		  <stop offset="85%" stop-color="white"/> 
		  <stop offset="100%" stop-color="#BED0DC" stop-opacity="0.8"/> 
		   
		</radialGradient>  
		 
		 <radialGradient id="grad2" cx="0.5" cy="0.6" r="0.06"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="100%" stop-color="#BED0DC" stop-opacity="0.9" />     
		</radialGradient>  
	</defs>  
 <g>  
  <image id="img1"   filter="url(#goovey)" xlink:href="https://i.stack.imgur.com/l1Mpe.jpg"  width="100%" height="100%"  />  
   <rect width="100%" height="100%"  fill="url(#grad1)"  fill-opacity="0.7" /> 
 </g>   
  
</svg>	  
</div>

Третий вариант

.container { 
width:100%; 
height:100%; 
}
<div class="container"> 
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"  
    xmlns:xlink="http://www.w3.org/1999/xlink" 
       width="900" height="600" viewBox="0 0 900 600" preserveAspectRatio="xMinYMin meet">   
 
	<defs> 
    <mask id="msk1">  
	  <rect fill="white" width="100%" height="100%"  /> 
  
	 <circle stroke="black"  fill="white" cx="450" cy="300" r="450" stroke-width="0" /> 
	 <circle stroke="black"  fill="white" cx="450" cy="300" r="350" stroke-width="0" /> 
     
	</mask>	 
	 
	<filter id="filter1"> 
     <feTurbulence type="turbulence" baseFrequency="0.01 0.01" 
        numOctaves="1" result="turbulence" seed="12"/> 
    <feDisplacementMap in2="turbulence" in="SourceGraphic" 
        scale="5" xChannelSelector="R" yChannelSelector="B"/> 
  </filter>	    
 
  <filter id="goovey"> 
		    <feTurbulence type="fractalNoise" baseFrequency="0.01"  numOctaves="1" result="warpper"/> 
          <feColorMatrix in="warpper" type="hueRotate" values="0" > 
                <animate attributeType="XML" attributeName="values" values="0;150;0" dur="4s" repeatCount="indefinite" begin="svg1.mouseover" end="svg1.mouseout"/>  
          </feColorMatrix> 
          <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic"/> 
		  <feGaussianBlur stdDeviation="1"/> 
    </filter> 
	 <radialGradient id="grad1" cx="0.5" cy="0.6" r="0.25"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="52%" stop-color="#BED0DC" stop-opacity="0.8" />     
		  <stop offset="85%" stop-color="white"/> 
		  <stop offset="100%" stop-color="#BED0DC" stop-opacity="0.8"/> 
		   
		</radialGradient>  
		 
    <radialGradient id="grad2" cx="0.5" cy="0.6" r="0.06"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="100%" stop-color="#BED0DC" stop-opacity="0.9" />     
    </radialGradient>  
	 
  </defs>  
  
 <g>  
   <image id="img1"   filter="url(#goovey)" xlink:href="https://i.stack.imgur.com/ia9YO.jpg"  width="100%" height="100%"  />  
     <rect width="100%" height="100%"  fill="url(#grad1)"  fill-opacity="0.6" /> 
 </g>   
  
 
  
</svg>	  
</div>

Update 04.03.2019 г.

  • Добавлена комплексная маска, которая обрезает изображение по окружности.
  • К разным частям маски применены разные радиальные градиенты
  • К градиентам применена анимация изменения радиуса градиента, создающая туннельный эффект с фокусировкой на отдельном человеке

    Для демонстрации эффекта, откройте сниппет в полном формате и наведите курсор

.container { 
width:100%; 
height:100%; 
}
<div class="container"> 
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"  
    xmlns:xlink="http://www.w3.org/1999/xlink" 
       width="900" height="600" viewBox="0 0 900 600" preserveAspectRatio="xMinYMin meet"  >   
 
	<defs> 
    <mask id="msk1"  >  
	<!--   <rect fill="white" width="100%" height="100%"   />  --> 
 	   <circle cx="450" cy="300" r="400" stroke-width="100" stroke="url(#grad2)" fill="url(#grad1)" /> 
 	</mask>	 
	 
	<filter id="filter1"> 
     <feTurbulence type="turbulence" baseFrequency="0.01 0.01" 
        numOctaves="1" result="turbulence" seed="12"/> 
    <feDisplacementMap in2="turbulence" in="SourceGraphic" 
        scale="5" xChannelSelector="R" yChannelSelector="B"/> 
  </filter>	    
 
  <filter id="goovey"> 
		    <feTurbulence type="fractalNoise" baseFrequency="0.015"  numOctaves="3" result="warpper"/> 
          <feColorMatrix in="warpper" type="hueRotate" values="0" > 
                <animate attributeType="XML" attributeName="values" values="0;150;0" dur="5s" repeatCount="indefinite" begin="svg1.mouseover" end="svg1.mouseout"/>  
          </feColorMatrix> 
          <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="25" in="SourceGraphic"/> 
		  <feGaussianBlur stdDeviation="1"/> 
  </filter> 
	 <radialGradient id="grad1" cx="0.35" cy="0.48" r="0.14"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="15%" stop-color="#BED0DC" stop-opacity="1" />     
		  <stop offset="55%" stop-color="white"/> 
		  <stop offset="90%" stop-color="#BED0DC" stop-opacity="1"/> 
     
        <animate 
		  attributeType="XML" 
		  attributeName="r" 
		  values="0.2;0.035;0.2" 
		  dur="20s" 
		  repeatCount="indefinite" 
		  begin="svg1.mouseover" 
		  end="svg1.mouseout"/>  
		 
   </radialGradient>   
	     
	    <radialGradient id="grad2" cx="0.5" cy="0.6" r="0.06"  spreadMethod="reflect"> 
		  <stop offset="2%" stop-color="white"/> 
		  <stop offset="100%" stop-color="#BED0DC" stop-opacity="0.9" />     
		</radialGradient>  
	 
  </defs>  
   
  <image id="img1" mask="url(#msk1)"   filter="url(#goovey)" xlink:href="https://i.stack.imgur.com/kp46o.jpg"  width="100%" height="100%"  />  
     
 </svg>	  
</div>

READ ALSO
ajax ($.post) то работает, то нет (в некоторых браузерах)

ajax ($.post) то работает, то нет (в некоторых браузерах)

Есть код ajax который работает с помощью библиотеки jquery:

99
Добавление изображение по ссылке в DataGridView

Добавление изображение по ссылке в DataGridView

Есть таблица DataGridView, в ней колонка с типом данных DataGridViewImageColumnКак добавить туда ссылку на изображение, чтобы показать картинку? По типу как...

95
Как включить сенсорный интерфейс в GeckoFX (C#)?

Как включить сенсорный интерфейс в GeckoFX (C#)?

Всем добрый деньСтолкнулся с проблемой включение "сенсорного" интерфейса на движке geckofx 60

107
Получение метода из класса

Получение метода из класса

Имеется строка запроса(ну или просто строка)/<контроллер>/<метод>/ далее параметры через "/"

105