首页 > 解决方案 > 创建迪士尼尘埃风格的光标轨迹

问题描述

我正在尝试制作一个光标,它会像任何迪士尼电影的介绍一样留下魔法尘埃的痕迹:example。所以我看到它的方式是它分为两部分。1. 小道和 2. 类似的落下和淡出的小道。到目前为止,我已经使基本轨迹工作得很好,下面的代码是用于下降轨迹的,本质上是它的副本和一个 css 动画..

我遇到的问题是抖动很多。我猜 css 动画对性能来说不是很好,并且导致它在我的页面上真正抖动。我刚刚阅读了 requestAnimationFrame 但对此很陌生,所以不知道如何实现它。我怎么能在这里使用 requestAnimationFrame 而不是 css?

我还认为在 js 中创建动画自定义将允许在下落粒子的动画中也有一个随机偏移......更像是在视频中。

window.addEventListener('mousemove', function (e) {
        //trail
         [1, .9, .8, .5, .25, .6, .4, .3, .2].forEach(function (i) {
      var j = (1 - i) * 50;
      var elem = document.createElement('div');
      var size = Math.ceil(Math.random() * 10 * i) + 'px';
      elem.style.position = 'fixed';
      elem.style.zIndex = 6;
      elem.style.top = e.pageY - window.scrollY + Math.round(Math.random() * j - j / 2) + 'px';
      elem.style.left = e.pageX + Math.round(Math.random() * j - j / 2) + 'px';
      elem.style.width = size;
      elem.style.opacity = "0.5";
      elem.style.height = size;
      elem.style.background = 'hsla(' +
        Math.round(Math.random() * 160) + ', ' +
        '60%, ' +
        '90%, ' +
        i + ')';
      elem.style.borderRadius = size;
      elem.style.pointerEvents = 'none';
      document.body.appendChild(elem);
      
      window.setTimeout(function () {
        document.body.removeChild(elem);
      }, Math.round(Math.random() * i * 1000));

    });

        // falling trail
        [1, .9, .8, .5, .25, .6,  .3, .2].forEach(function (i) {
          var j = (1 - i) * 50;
          var elem = document.createElement('div');
          var size = Math.ceil(Math.random() * 10 * i) + 'px';
          elem.style.position = 'fixed';
          elem.style.zIndex = 6;
          elem.style.top = e.pageY - window.scrollY + Math.round(Math.random() * j - j / 2) + 'px';
          elem.style.left = e.pageX + Math.round(Math.random() * j - j / 2) + 'px';
          elem.style.width = size;
          elem.style.opacity = "0.5";
          elem.style.height = size;
          elem.style.animation = "fallingsparkles 1s";
          elem.style.background = 'hsla(' +
            Math.round(Math.random() * 160) + ', ' +
            '60%, ' +
            '90%, ' +
            i + ')';
          elem.style.borderRadius = size;
          elem.style.pointerEvents = 'none';
          document.body.appendChild(elem);
          
          window.setTimeout(function () {
            document.body.removeChild(elem);
          }, Math.round(Math.random() * i * 1000));

        });
      }, false);
body {
  width:100%;
  height:100%;
  background-color: #000;
}

@keyframes fallingsparkles {
  from {
    transform: translateY(0);
  }

  to {
    transform: translateY(50px);
  }
}
<body></body>

标签: javascriptcss

解决方案


多么酷的小动画。我认为从长远来看这会很烦人,但仍然:很酷。

我不喜欢这个解决方案的一点是,我必须执行一个连续的递归循环,即使没有要设置动画的元素也会继续。可能有一些方法可以避免这种情况,但仅仅展示如何实现requestAnimationFrame. 如您所见,使用 requestAnimationFrame 并不难。您只需调用与您进行计算相同的方法。

除了将每个元素添加到正文之外,我还sparkleArr通过addAnimationProperties. 我终于添加了一种全新的方法——moveSparkles用于计算运动和移除元素。calculateInterpolation根据元素的创建时间、消失时间和当前时间计算元素应移动多长时间的百分比。

我认为代码是不言自明的。我不得不在一个地方做评论,我认为不可能通过变量或方法名称来解释代码。

let trailArr = [1, .9, .8, .5, .25, .6, .4, .3, .2];
var sparklesArr = [];

function trailAnimation(e, i, maxYTranslation) {
  let elem = document.createElement('div');

  elem = styleSparkle(elem, e, i);
  
  elem.classList.add("sparkle");

  document.body.appendChild(elem);

  elem = addAnimationProperties(elem, i, maxYTranslation);
  
  sparklesArr.push(elem);
}

function styleSparkle(elem, e, i) {
  let j = (1 - i) * 50;
  let size = Math.ceil(Math.random() * 10 * i) + 'px';
  
  elem.style.top = e.pageY - window.scrollY + Math.round(Math.random() * j - j / 2) + 'px';
  elem.style.left = e.pageX + Math.round(Math.random() * j - j / 2) + 'px';
  
  elem.style.width = size;
  elem.style.height = size;
  elem.style.borderRadius = size;
  
  elem.style.background = 'hsla(' +
    Math.round(Math.random() * 160) + ', ' +
    '60%, ' +
    '90%, ' +
    i + ')';
  
  return elem;
}

function addAnimationProperties(elem, i, maxYTranslation) {
  const ANIMATION_SPEED = 1100;
  let lifeExpectancy = Math.round(Math.random() * i * ANIMATION_SPEED);

  elem.maxYTranslation = maxYTranslation;
  elem.animationSpeed = ANIMATION_SPEED;
  elem.created = Date.now();
  elem.diesAt = elem.created + lifeExpectancy;

  return elem;
}

function moveSparkles() {
  let remove = false;
  let moveIndex = 0;
  let sparkle;

  for (let i = 0; i < sparklesArr.length; i++) {
    sparkle = sparklesArr[i];
    remove = sparkle.diesAt <= Date.now();
    
    if (remove) {
      document.body.removeChild(sparkle);
    } else {
      if (sparkle.maxYTranslation) {
        let interpolation = calculateInterpolation(sparkle);
        sparkle.style.transform = `translateY(${interpolation}px)`; 
      }
      
      sparklesArr[moveIndex++] = sparkle;  // faster than array.splice()    
    }
  }
  
  sparklesArr.length = moveIndex;
  requestAnimationFrame(moveSparkles);
}

function calculateInterpolation(sparkle) {
  let currentMillis = Date.now();
  let lifeProgress = (currentMillis - sparkle.created) / sparkle.animationSpeed;
  let interpolation = sparkle.maxYTranslation * lifeProgress;
  
  return interpolation;
}

window.addEventListener('mousemove', function (e) {
  trailArr.forEach((i) => {trailAnimation(e, i)});

  let maxYTranslation = '80';
  trailArr.forEach((i) => {trailAnimation(e, i, maxYTranslation)});
}, false);

moveSparkles(); // starts the recursive loop
body {
  width:100%;
  height:100%;
  background-color: #000;
}

.sparkle {
  position: fixed;
  z-index: 6;
  opacity: 0.5;
  pointer-events: none;
}
<body></body>


推荐阅读