粒子组成文字动画效果
代码语言:html
所属分类:粒子
代码描述:粒子组成文字动画效果
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style> *, *::before, *::after{ box-sizing: border-box; margin: 0; padding: 0; } body, html{ width: 100%; height: 100%; position: relative; } #text-container{ font-family: 'Rock Salt'; position: relative; display: block; width: 100%; height: 100%; } </style> </head> <body translate="no"> <div id="text-container"></div> <script> function _defineProperty(obj, key, value) {if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}window.onload = function () { const PI2 = Math.PI * 2; const SIN = Math.sin; const COS = Math.cos; const SMALL = 650; /* /$$$$$$ /$$$$$$$ /$$$$$$$$ /$$ /$$ /$$__ $$ | $$__ $$ | $$_____/| $$ | $$ | $$ \__/ /$$ /$$ /$$$$$$ /$$$$$$ | $$ \ $$ /$$ /$$ /$$$$$$ /$$$$$$ | $$ | $$ /$$ /$$| $$ | $$$$$$ | $$ | $$ /$$__ $$ |____ $$ | $$ | $$| $$ | $$ /$$__ $$ |____ $$ | $$$$$ | $$| $$ | $$| $$ \____ $$| $$ | $$| $$ \ $$ /$$$$$$$ | $$ | $$| $$ | $$| $$ \ $$ /$$$$$$$ | $$__/ | $$| $$ | $$|__/ /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$ | $$ | $$| $$ | $$| $$ | $$ /$$__ $$ | $$ | $$| $$ | $$ | $$$$$$/| $$$$$$/| $$$$$$$/| $$$$$$$ | $$$$$$$/| $$$$$$/| $$$$$$$/| $$$$$$$ | $$ | $$| $$$$$$$ /$$ \______/ \______/ | $$____/ \_______/ |_______/ \______/ | $$____/ \_______/ |__/ |__/ \____ $$|__/ | $$ | $$ /$$ | $$ | $$ | $$ | $$$$$$/ |__/ |__/ \______/ */ /////////////////////// // UTILITY FUNCTIONS // /////////////////////// //some utility functions function randomInRange(min, max) {return Math.random() * (max - min) + min;} //I'll leave this here just in case anyone wants to play with the particles rgb channels //(P-P-PROTIP: setting the blue and red channels with this function makes for a cool palette) function randomIntInRange(min, max) {return ~~(Math.random() * (max - min) + min);} function mergeObjects(target, object, deep = false) { for (let prop in object) { if (target.hasOwnProperty(prop)) { if (typeof object[prop] === 'object' && deep) { target[prop] = mergeObjects(target[prop], object[prop], true); } else { target[prop] = object[prop]; } } else { target[prop] = object[prop]; } } return target; } function chooseRandomFrom(array) {return array[~~(Math.random() * array.length)];} function createSupaDupaGradient(context) { const canvas = context.canvas,gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, canvas.width / 5, canvas.width / 2, canvas.height / 2, canvas.width * 0.7); gradient.addColorStop(0, '#550e8f'); gradient.addColorStop(1, '#101010'); return gradient; } //create a canvas with the same size of a given one and return its context function createCanvasFrom(canvas) { const referenceCanvas = document.createElement('canvas'); const $referenceCanvas = referenceCanvas.getContext('2d'); $referenceCanvas.canvas.width = referenceCanvas.width = canvas.width; $referenceCanvas.canvas.height = referenceCanvas.height = canvas.height; return $referenceCanvas; } //create a new canvas fitting it inside a parent container specified by the user function createFitCanvas(element) { const canvas = document.createElement('canvas'); const $canvas = canvas.getContext('2d'); $canvas.canvas.width = canvas.width = element.offsetWidth; $canvas.canvas.height = canvas.height = element.offsetHeight; canvas.style.position = 'absolute'; //so it fits snuggly in its container element.append(canvas); return canvas; } //get active pixels from the canvas we are rendering the text to function getAlphaPixelsFromImage(imageData, step = 2) { const alphaPixels = []; for (let i = 0, counter = 0; i < imageData.data.length; i++) { if (imageData.data[i]) { counter++; if ((i - 3) % 4 === 0 && counter % step === 0 && Math.random() > 0.5) { alphaPixels.push({ r: imageData.data[i - 3], g: imageData.data[i - 2], b: imageData.data[i - 1], alpha: imageData.data[i], pixelIndex: (i - 3) / 4 }); } } } return alphaPixels; } //get an array of Floats containing the particle data we are etracting from our render text function getParticlesFromImage(imageData, { particleProps, particleMaxRadius, pointSpacing, palette }) { const alphaPixels = getAlphaPixelsFromImage(imageData, pointSpacing); const particlesArray = new Float32Array(alphaPixels.length * particleProps); for (let i = 0, particlePointer = 0, x = undefined, y = undefined, color = undefined; i < alphaPixels.length; i++) { if (palette) {color = chooseRandomFrom(palette);} x = alphaPixels[i].pixelIndex % imageData.width; y = alphaPixels[i].pixelIndex / imageData.width; particlePointer = i * particleProps; particlesArray[particlePointer++] = x; //position.x ========= +0 (aka particlePointer's current value, aka a particle's address in memory) particlesArray[particlePointer++] = y; //position.y ========= +1 particlesArray[particlePointer++] = x; //targetPosition.x === +2 particlesArray[particlePointer++] = y; //targetPosition.y === +3 particlesArray[particlePointer++] = randomInRange(0, 1000); //particle.rotationProgress.x ====== +4 particlesArray[particlePointer++] = randomInRange(0, 1000); //particle.rotationProgress.y ====== +5 particlesArray[particlePointer++] = randomInRange(-1, 1); //particle.direction.x ============= +6 particlesArray[particlePointer++] = randomInRange(-1, 1); //particle.direction.y ============= +7 particlesArray[particlePointer++] = randomInRange(0.5, particleMaxRadius); //particle.radius === +8 //colorRGBA values for each particle particlesArray[particlePointer++] = palette ? color["r"] : alphaPixels[i].r; //particles's color's "red" channel ===== +9 particlesArray[particlePointer++] = palette ? color["g"] : alphaPixels[i].g; //particles's color's "green" channel === +10 particlesArray[particlePointer++] = palette ? color["b"] : alphaPixels[i].b; //particles's color's "blue" channel ==== +11 particlesArray[particlePointer] = alphaPixels[i].alpha; //particles's color's "alpha" channel ====================== +12 } return particlesArray; } ///////////// // CLASSES // ///////////// class ParticleText { constructor(string, element, options = {}) {_defineProperty(this, "animate", () => { this.renderParticles(this.settings.particles); this.frameID = window.requestAnimationFrame(this.animate); });const _particleProps = 13; //<----- you shouldn't touch this guy right here, but I ain't your dad, so if you really want to, go ahead and change it //setting up the canvases needed for ParticleText const canvas = createFitCanvas(element);const $referenceCanvas = createCanvasFrom(canvas);const _defaults = { particles: { //canvas background color background: undefined, //particle shape pointSpacing: 5, particleMaxRadius: 2, palette: [], //particle motion revolutionRadius: 2, revolutionSlowness: 5, //only update half of the particles each cycle, repaint every other cycle enhancedRendering: true }, //text default settings text: { fontSize: 150, fontFamily: 'serif', padding: 50, lineHeight: undefined } }; //deep merge user supplied arguments and the defaults this.settings = mergeObjects(_defaults, options, true); //I am adding this property later simply because I don't want the user to change it easily //(If you are a professional reading this, how would you go about doing this in a cleaner way?? Comment below to let me know, it would be greatly appreciated!) this.settings.particles.particleProps = _particleProps; //reference to the parent element of the rendering canvas this.parent = element; //the a.........完整代码请登录后点击上方下载按钮下载查看
网友评论0