Estoy tratando de hacer un cursor que deje un rastro de polvo mágico como en esa introducción a cualquier película de Disney: ejemplo . Así que la forma en que lo veo es que está dividido en dos partes. 1. el rastro y 2. rastro similar que cae y se desvanece. Hasta ahora he hecho que el rastro básico funcione bastante bien, el siguiente código es para el rastro descendente y esencialmente una copia del mismo con una animación css.
El problema que tengo es que tiembla mucho. Supongo que la animación css no es excelente para el rendimiento y está causando que esto realmente se mueva en mi página. Acabo de leer sobre requestAnimationFrame pero soy nuevo en esto, así que no estoy seguro de cómo implementarlo. ¿Cómo podría usar requestAnimationFrame en lugar de css aquí?
También creo que crear la animación personalizada en js permitiría que también haya un desplazamiento aleatorio en la animación de las partículas que caen... mucho más como en el video.
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>Principalmente estoy publicando esto para que otros lo usen, donde refactoricé un poco el código. Nada ha cambiado, pero es más fácil de seguir. Seguiré con una respuesta real.
let trailArr = [1, .9, .8, .5, .25, .6, .4, .3, .2]; function trailAnimation(e, i, callbackFn) { var elem = document.createElement('div'); elem = styleSparkle(elem, e, i); if (typeof callbackFn == 'function') { elem = callbackFn(elem); } elem.classList.add("sparkle"); document.body.appendChild(elem); window.setTimeout(function () { document.body.removeChild(elem); }, Math.round(Math.random() * i * 1000)); } 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; } window.addEventListener('mousemove', function (e) { trailArr.forEach((i) => {trailAnimation(e, i)}); trailArr.forEach((i) => {trailAnimation(e, i, (elem) => { elem.style.animation = "fallingsparkles 1s"; return elem; })}); }, false); body { width:100%; height:100%; background-color: #000; } .sparkle { position: fixed; z-index: 6; opacity: 0.5; pointer-events: none; } @keyframes fallingsparkles { from { transform: translateY(0); } to { transform: translateY(50px); } } <body></body>Qué pequeña animación genial. Creo que será molesto a la larga, pero aún así: genial.
Lo que no me gusta de esta solución es que tuve que hacer un bucle recursivo continuo, que continúa incluso si no hay elementos para animar. Probablemente haya formas de evitar eso, pero sería demasiado complejo mostrar cómo implementar requestAnimationFrame . Como puede ver, no es tan difícil de usar requestAnimationFrame. Simplemente llame al mismo método en el que hace los cálculos.
Además de agregar cada elemento al cuerpo, también agregué el elemento en sparkleArr con algunas propiedades más a través addAnimationProperties . Finalmente agregué un método completamente nuevo, moveSparkles , para calcular el movimiento y eliminar los elementos. calculateInterpolation calcula el porcentaje de cuánto tiempo debe moverse el elemento, en función de cuándo se creó el elemento, cuándo desaparecerá y la hora actual.
Creo que el código se explica por sí mismo. Tuve que hacer un comentario en un lugar, donde no creo que sea posible explicar el código a través de variables o nombres de métodos.
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>Hice otra versión que es bastante divertida.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> body { width: 100%; height: 100%; background-color: #000; } .star-five { background: transparent; margin: 50px 0; position: relative; display: block; color: #ffffff; width: 0px; height: 0px; border-right: 100px solid transparent; border-bottom: 70px solid #ffffff; border-left: 100px solid transparent; /* transform: rotate(35deg) scale(0.1) translate(-1450px, -250px); */ } .star-five:before { border-bottom: 80px solid #ffffff; border-left: 30px solid transparent; border-right: 30px solid transparent; background-color: transparent; position: absolute; height: 0; width: 0; top: -45px; left: -65px; display: block; content: ''; transform: rotate(-35deg); } .star-five:after { background-color: transparent; position: absolute; display: block; color: #ffffff; top: 3px; left: -105px; width: 0px; height: 0px; border-right: 100px solid transparent; border-bottom: 70px solid #ffffff; border-left: 100px solid transparent; transform: rotate(-70deg); content: ''; } </style> </head> <body> <script> window.addEventListener('mousemove', function (e) { //trail [.7, .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'; //ramdom number between 0 and 1 var precision = 50; // 2 decimals var randomnum = Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision); var rOpacity = randomnum/10; var rSize = randomnum/120; elem.style.position = 'fixed'; elem.classList.add('star-five') elem.style.zIndex = 6; elem.style.transform = `rotate(35deg) scale(${rSize})` //elem.style.transform = `rotate(35deg) scale(${rSize}) translate(-1450px, -250px)` elem.style.top = e.pageY - window.scrollY + Math.round(Math.random() * j - j / 2) - 100 + 'px'; elem.style.left = e.pageX + Math.round(Math.random() * j - j / 2) - 100 + 'px'; //elem.style.width = size; //console.log(rSize); elem.style.opacity = rOpacity; //elem.style.height = size; // elem.style.background = 'hsla(' + // Math.round(Math.random() * 160) + ', ' + // '60%, ' + // '100%, ' + // 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); </script> </body> </html>