在鼠标悬停时创建圆形火焰效果

Posted

技术标签:

【中文标题】在鼠标悬停时创建圆形火焰效果【英文标题】:Create rounded fire effect on mouseover 【发布时间】:2019-01-06 16:15:24 【问题描述】:

Hobbit's网站偶然发现了一个漂亮的火焰效果。这个效果我实际上只在游戏中看到过。

有两个带有leftright边的块(evilgood

当你将鼠标悬停在其中一个方块上时,会有圆形的火圈

喜欢在底部

来自

我创建了类似的东西,但实际上我的火并没有像上面的网站那样顺利显示。我需要与网站完全相同的效果,请帮助。

.good 
  height:200px;
  width:200px;
  background-image: url(https://image.ibb.co/gapib8/good_icon.png);
  background-size:cover;
  box-sizing:border-box;
  padding:10px;

.good .ring 
  overflow:hidden;

  transition: 1s opacity;
  transition-timing-function:easeInOut;
  opacity:0;
  height:100%;
  width:100%;
  background-image: url(https://image.ibb.co/eJCQpT/fire_ring.png);
  background-repeat:no-repeat;
  background-size:cover;



.good:hover .ring 
  opacity:1;

<div class="good">
  <div class="ring">

  </div>
</div>

【问题讨论】:

@BramVanroy 原版中缺少“旋转”效果。 您需要查看剪辑路径并为其设置动画。可能是一个从 0° 到 360° 动画的三角形。 【参考方案1】:

这是使用片段着色器的重量级 WebGL 解决方案

<img src="https://image.ibb.co/gapib8/good_icon.png">
<div class="zone"></div>
<canvas   id="canvas"/>
<script type="glsl">
precision highp float;
uniform float time;     
uniform vec2 resolution;
uniform float fill;      // fill of fire ring (0-1)

vec3 firePalette(float i) 
    float T = 1400. + 1300.*i; 
    vec3 L = vec3(7.4, 5.6, 4.4); 
    L = pow(L,vec3(5.0)) * (exp(1.43876719683e5/(T*L))-1.0);
    return 1.0-exp(-5e8/L); 
    

vec3 hash33(vec3 p)  
    float n = sin(dot(p, vec3(7, 157, 113)));    
    return fract(vec3(2097152, 262144, 32768)*n); 


float voronoi(vec3 p) 
// https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm
	vec3 b, r, g = floor(p);
	p = fract(p); 
	float d = 1.; 
	for(int j = -1; j <= 1; j++) 
	    for(int i = -1; i <= 1; i++) 
		    b = vec3(i, j, -1);
		    r = b - p + hash33(g+b);
		    d = min(d, dot(r,r));
		    b.z = 0.0;
		    r = b - p + hash33(g+b);
		    d = min(d, dot(r,r));
		    b.z = 1.;
		    r = b - p + hash33(g+b);
		    d = min(d, dot(r,r));	
	    
	
	return d;


// https://www.iquilezles.org/www/articles/warp/warp.htm
float noiseLayers(in vec3 p) 
    vec3 t = vec3(0., 0., p.z+time*1.5); 
    const int iter = 5; 
    float tot = 0., sum = 0., amp = 1.; 
    for (int i = 0; i < iter; i++) 
        tot += voronoi(p + t) * amp; 
        p *= 2.0; 
        t *= 1.5; 
        sum += amp; 
        amp *= 0.5; 
    
    return tot/sum; 

    
float sdPie( in vec2 p, in vec2 c, in float r ) 
    p.x = abs(p.x);
    float l = length(p) - r;
	float m = length(p - c*clamp(dot(p,c),0.0,r) );
    return max(l,m*sign(c.y*p.x-c.x*p.y));

    
void main(void) 
    float f = (1.-fill)*3.1415;
    vec2 uv = (gl_FragCoord.xy - resolution.xy*0.5)/ resolution.y;
    float cs = cos(f-time/4.), si = sin(f-time/4.);
    uv.xy *= mat2(cs, -si, si, cs); 
    vec3 rd = normalize(vec3(uv.x, uv.y, 3.1415/8.));
    float c = noiseLayers(rd*2.); 
    float d = dot(uv, uv);
    float ts = 0.105 + sin(time)*0.001;
    float ring = 1. - min(smoothstep(d, 0.11, ts), smoothstep(d, ts, 0.11));
    vec3 col = sqrt(clamp((firePalette(c*ring)-0.03)*1.5, 0., 1.));    
    if (fill<1.)
      col *= sdPie(uv,vec2(sin(f+0.001),cos(f+0.001)), 0.5)*3.1;
    gl_FragColor = vec4(col, ring);  

</script>

<script>
let gl = canvas.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,3,-1,-1,3,-1]), gl.STATIC_DRAW);

let pid = gl.createProgram();
shader(`attribute vec2 v;void main(void)gl_Position=vec4(v,0.,1.);`,gl.VERTEX_SHADER);
shader(document.querySelector(`script[type="glsl"]`).textContent,gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);

let v = gl.getAttribLocation(pid, "v");
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);

let resolution = gl.getUniformLocation(pid, 'resolution');
let time = gl.getUniformLocation(pid, 'time');
let fill = gl.getUniformLocation(pid, 'fill');
requestAnimationFrame(draw);

function draw(t) 
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(time, t/1000);
  gl.uniform1f(fill, getComputedStyle(canvas).opacity);
  gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  requestAnimationFrame(draw);


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((s, i) => (""+(1+i)).padStart(4, 0)+": "+s).join("\n"));
    throw message;
  

</script>

<style>
canvas 
  position: absolute;
  top: -29px;
  pointer-events: none;
  left: -26px;
  opacity: 0;
  transition: 1s;


.zone 
  position: absolute;
  top: 16px;
  left: 19px;
  border-radius: 50%;
  width: 245px;
  height: 245px;


div.zone:hover + canvas
  opacity: 1;

</style>

【讨论】:

【参考方案2】:

这是我的解决方案。它应该是复制网站上的动画。

编辑:更新了剪辑以提供圆形显示效果,这也适用于 Firefox :) 它没有使用与原始网站相同的资产,因此效果当然没有那么好。通过替换资产,您应该能够获得相同的效果,包括显示效果结束时的闪光。

.good 
  height: 150px;
  width: 150px;
  background-image: url(https://image.ibb.co/gapib8/good_icon.png);
  background-size: cover;
  position: 


.good .ring 
  position: relative;
  overflow: hidden;
  height: 100%;
  width: 100%;


.good .ring img 
  position: absolute;
  top: -1px;
  opacity: 0;
  height: 100%;
  width: 100%;
  transition: opacity 0.2s;


.good .ring .fire-reveal 
    position: absolute;
    top: 4.2%;
    left: 3.6%;
    width: 90%;
    height: 90%;
    background-image: url(https://preview.ibb.co/cNZZG8/fire_ring_complete.png);
    background-size: 588px auto;
    background-repeat: no-repeat;
    background-position: -150px -300px;
    opacity: 0;
    transition: 0.2s opacity;


.good:hover .ring .fire-reveal 
    opacity: 1;
    transition: opacity 0.1s;
    transform: rotate(5deg);
    animation: reveal 0.4s cubic-bezier(0,0,1,1);


@keyframes reveal 
  0% 
clip-path: polygon(100% 100%, 51% 63%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 100% 57%, 51% 63%);
  
  12.5% 
clip-path: polygon(100% 100%, 51% 63%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 100% 100%, 51% 63%);
  
  25% 
clip-path: polygon(49% 37%, 49% 30%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 51% 100%, 51% 50%);
  
  37.5% 
clip-path: polygon(50% 63%, 51% 63%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 0 100%, 51% 63%);
  
  50% 
clip-path: polygon(0 50%, 50% 54%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 0 100%, 0 50%);
  
  62.5% 
clip-path: polygon(0 0, 52% 53%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 0 100%, 0 42%);
  
  75% 
  clip-path: polygon(48% 0, 49% 50%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 0 100%, 0 0);
  
  87.5% 
  clip-path: polygon(100% 0, 46% 30%, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 0 100%, 0 0);
  
  100% 
  clip-path: polygon(100% 0, 100% 0, 100% 30%, 100% 45%, 100% 57%, 100% 100%, 0 100%, 0 0);
  


.good:hover .ring img 
  opacity:1;
  transition: opacity 0.3s;
  transition-delay: 0.2s;
  animation: rotation 20s linear both infinite;


@keyframes rotation 
  from 
    transform: rotate(0deg) scale(0.9);
  
  to 
    transform: rotate(360deg) scale(0.9);
  
<div class="good">
  <div class="ring">
    <div class="fire-reveal"></div>
    <img src="https://image.ibb.co/eJCQpT/fire_ring.png">
  </div>
</div>

【讨论】:

你是 100% 正确的。我最初在 Firefox 中查看该站点,它的行为与在 Chrome 中不同。会再去一次 更新了圆形显示效果。【参考方案3】:

这是我的解决方案 它不像网站。

您可以随意修改动画的计时值

我在火中添加了旋转动画

.good 
  height: 200px;
  width: 200px;
  background-image: url(https://image.ibb.co/gapib8/good_icon.png);
  background-size: cover;
  box-sizing: border-box;
  padding: 10px;


.good .ring 
  overflow: hidden;
  transition: 1s opacity;
  transition-timing-function: easeInOut;
  opacity: 0;
  height: 100%;
  width: 100%;
  background-image: url(https://image.ibb.co/eJCQpT/fire_ring.png);
  background-repeat: no-repeat;
  background-size: cover;


.good:hover .ring 
opacity:1;
  animation: spin 2800ms linear 0ms infinite;


@keyframes spin 
  0% 
    transform: rotate(0deg);
   /* opacity: 0; */
  
  50% 
    transform: rotate(360deg);
    /* opacity: 0; */
  
  100% 
    transform: rotate(720deg);
   /* opacity: 0; */
  
<div class="good">
  <div class="ring">

  </div>
</div>

【讨论】:

从 OP 的描述和示例中,需要无限动画循环。 @Script47 没错

以上是关于在鼠标悬停时创建圆形火焰效果的主要内容,如果未能解决你的问题,请参考以下文章

鼠标悬停时仅显示当前卡片悬停效果

悬停在图片上时如何创建圆形悬停显示

当我在 SVG 中将鼠标悬停时,如何显示和隐藏同级元素?

悬停时的 SVG 圆形动画不起作用

HTML图片鼠标悬停效果设置!

放大鼠标悬停效果