CSS 中的模糊文本动画

Blurry text animation in CSS

当我查看 three.js 的示例时,我看到了 Lusion 的页面。
标题为“实现您的创意”的动画显示文本先模糊后清晰。
我试图重现这个:

@keyframes test {
  from {
    opacity: 0;
    visibility: hidden;
    filter: blur(0.2ex);
  }
  to {
    opacity: 1;
    visibility: visible;
    filter: blur(0ex);
  }
}

body p {
  font-size: 32px;
  text-align: center;
}

body #one {
  animation: test 2.6s linear;
}

body #two {
  animation: test 2s linear;
}

body #three {
  animation: test 1.5s linear;
}

body #four {
  animation: test 2s linear;
}

body #five {
  animation: test 2.6s linear;
}
<p>
  <span id="one">T</span>
  <span id="two">i</span>
  <span id="three">t</span>
  <span id="four">l</span>
  <span id="five">e</span>
</p>

但是整个事情有点烦人,因为你必须对每个字母都这样做。
此外,我的结果看起来没有 Lusion 的好。

是否可以做得更容易/更好?也许使用 JS 库(也许 three.js)?
或者唯一的解决办法是通过CSS keyframes

使用javascript:

"use strict";
document.addEventListener("DOMContentLoaded", e => 
{
  randBlur(document.getElementsByClassName("randBlur"));
  setTimeout(()=>randBlur([document.getElementById("delayed")]), 3000);
});

function randBlur(els, randDurationMin, randDurationMax)
{
  if (!els)
    return;

  if (randDurationMin === undefined)
    randDurationMin = 0.7; //min animation duration (in sec)

  if (randDurationMax === undefined)
    randDurationMax = 1.7; //min animation duration (in sec)

  if (randBlur.list === undefined)
  {
    randBlur.style = document.createElement("style");
    document.head.appendChild(randBlur.style);
    randBlur.list = {};
    randBlur.applyStyle = () =>
    {
      randBlur.style.innerHTML = "";
      for(let i in randBlur.list)
        randBlur.style.textContent += '[randBlur="' + i + '"]{animation:randblur ' + i + 's linear}';
    }
  }

  const span = document.createElement("span"),
        text = document.createTextNode(""),
        rand = () =>
        {
          const duration = parseFloat((Math.random() * (randDurationMax - randDurationMin) + randDurationMin).toFixed(2));
          randBlur.list[duration] = "";
          return duration;
        },
        randBlurChildren = el =>
        {
          if (el.nodeType == el.TEXT_NODE)
          {
            for(let n = 0, ws, box; n < el.textContent.length; n++)
            {
              ws = el.textContent[n].match(/\s/);
              box = (ws ? text : span).cloneNode(false);
              box.textContent = el.textContent[n];
              if (!ws)
              {
                box.setAttribute("randBlur", rand());
              }
              el.parentNode.insertBefore(box, el);
            }
            el.parentNode.removeChild(el);
          }
          else
          {
            const children = Object.assign([], el.childNodes);
            for(let c = 0; c < children.length; c++)
            {
              if (!children[c].hasAttribute || !children[c].hasAttribute("randBlur"))
                randBlurChildren(children[c]);
                
            }
          }
        };

  for (let i = 0; i < els.length; i++)
    randBlurChildren(els[i]);

  randBlur.applyStyle();
}
@keyframes randblur {
  from {
    opacity: 0;
    visibility: hidden;
    filter: blur(0.2ex);
  }
  to {
    opacity: 1;
    visibility: visible;
    filter: blur(0ex);
  }
}

body {
  font-size: 1.1em;
  text-align: center;
}

h2,h3
{
  display: inline;
}

h2
{
  color: green;
}

p
{
  white-space: pre;
}
<p class="randBlur">&lt;P&gt; element with юникод text 
and whitespaces</p>
<span class="randBlur">Even <h3> elements with <h2>children</h2></h3> animated.</span>
<span id="delayed">Can be applied at any time, but only once</span>
<h1 class="randBlur">Practically any HTML tag can be used</h1>