block by EE2dev d0dfd677cc5f3f7d05838b7bcb2c085e

Animated Intro - 3

Full Screen

Animating text based on this amazing codepen from Blake Bowen. (This bl.ock is just tested with Chrome)

Using <h1> elements for the text and creates the animation by rotating letters and bursting stars.

See also:

index.html

<!DOCTYPE html>
<html>
    <meta charset="utf-8">
    <head>    
        <title>stars</title>
        <link href="https://fonts.googleapis.com/css?family=Indie+Flower" rel="stylesheet">   
        <link href="style.css" rel="stylesheet">
        <script src="https://d3js.org/d3.v5.min.js"></script>
        <script src="https://ee2dev.github.io/startext/lib/backgroundImage.js"></script> 
        <script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>
    </head>

    <body>
        <div class="chart"></div>
        <aside style="display:none">
            <img id="star-texture" src="https://ee2dev.github.io/startext/lib/stars-02.png">
            <img id="star-texture-white" src="https://ee2dev.github.io/startext/lib/stars-02w.png">
        </aside> 

        <script src="stars.js"></script>
        <script src="animateStars.js"></script>
    </body>
</html>

animateStars.js

let pathDurations = [];
let pathEndpoints = [];
let app; // the main class in stars.js to create the particles

const containerDiv = "div.chart";
const explosionStrength = 0.002;
const transitionSpeed = 7;
const starOptions = {
	mouseListener: false,
	texture: document.querySelector("#star-texture-white"),
	frames: createFrames(5, 80, 80),
	maxParticles: 2000,
	backgroundColor: "#111111",
	blendMode: "lighter",
	filterBlur: 50,
	filterContrast: 300,
	useBlurFilter: true,
	useContrastFilter: true
};

const myText = ["This animation combines", "rotation", "and", "bursting stars"];
animate(myText);

function animate(myText){
	WebFont.load({
		google: { families: ["Indie Flower"]},
		fontactive: function(familyName, fvd){ //This is called once font has been rendered in browser
			displayText(myText);
			createPaths();
			intializeStars();
			animateStars();
		},
	});
}

function displayText(_textArray) {
	let sel = d3.select(containerDiv);
	
	// add headers for all strings by wrapping each letter around a span
	// which can be styled individually
	for (let ta = 0; ta < _textArray.length; ta++) {
		let sel2 = sel.append("div")
			  .attr("class", "header h" + ta)
			.append("h1")
			  .attr("class", "trans");
	
		let myString = _textArray[ta];
		for (let i = 0; i < myString.length; i++) {
			sel2.append("span")
			  .attr("class", (ta < _textArray.length -1) ? "rotate" : "burst " + "color-" + (i % 5))
			  .text(myString[i]); 
		}
	}
}

function createPaths() {
	let bBox = d3.select(containerDiv).node().getBoundingClientRect();
	let sel = d3.select(containerDiv)
	  .append("div")
		.attr("class", "paths")
	  .append("svg")
		.attr("width", bBox.width)
		.attr("height", bBox.height);
  
	const container = d3.select(containerDiv).node().getBoundingClientRect();
  
	d3.selectAll(containerDiv + " h1")
	  .each(function(d,i) {
			let bBox = d3.select(this).node().getBoundingClientRect();
			let xTranslate = d3.select("div.chart").node().getBoundingClientRect().x;
			let pos = {};
			pos.width = bBox.width;
			pos.xStart = (container.width - bBox.width) / 2;
			pos.yStart = bBox.y + (bBox.height / 2) - container.y;

			let p = sel.append("path")
				.attr("class", "header hidden trans " + "h" + i)
				.attr("d", (d) => {
					let str =  "M" + pos.xStart + ", " + pos.yStart
				+ " L" + (pos.width + pos.xStart) + ", " + pos.yStart;
					return str;
				});
		
			let duration = p.node().getTotalLength() * transitionSpeed;
			pathDurations.push(duration);
	  });
}

function intializeStars() {
	let bBox = d3.select(containerDiv).node().getBoundingClientRect();
	d3.select(containerDiv).insert("div", ".h0")
		.attr("class", "stars")
	  .append("canvas")
		.attr("id", "view")
		.attr("width", bBox.width)
		.attr("height", bBox.height);

	starOptions.view = document.querySelector("#view");
	app = new App(starOptions);
	window.addEventListener("load", app.start());
	window.focus();
}

function starsAlongPath(path) {
	let l = path.getTotalLength();
	return function(d, i, a) {
		return function(t) {
			let p = path.getPointAtLength(t * l);
			app.spawn(p.x , p.y);
			return "translate(0,0)";
		};
	};
}

function animateStars() {
	let durations = pathDurations.concat(pathDurations);
	let chainedSel = d3.selectAll(".trans").data(durations);
	chainedTransition(chainedSel);

	function chainedTransition(_chainedSel, _index = 0) {
		const num_headers = _chainedSel.size() / 2;
		let nextSel = _chainedSel.filter((d,i) => (i % num_headers) === _index);
		transitionNext(nextSel);
    
		function transitionNext(_selection){
			console.log(_index);
			if (_index === num_headers - 1) {
				setTimeout( function() { 
					app.setActivate(false); // disable requestAnimationFrame calls
					transitionLast(_selection);
				}, 1000);  
			}
			else {
				// the header
				let letters = _selection.filter((d,i) => i === 0).selectAll("span");
				letters
				  .transition()
				  .duration(2000)
				  .delay((d,i) => i * 200)
				  .style("opacity", 1)
				  .style("-webkit-transform", "rotate(-720deg) scale(1)")
				  .on ("end", function(d,i) {
						if (i === letters.size()-1) {
							_index = _index + 1;
							if (num_headers > _index) { 
								nextSel = _chainedSel.filter((d,i) => (i % num_headers) === _index);
								transitionNext(nextSel);
							} 
						}
				  });
			}
		}

		function transitionLast(_selection){
			starOptions.texture = document.querySelector("#star-texture");
			let app2 = new App(starOptions);
			window.addEventListener("load", app2.start());
			window.focus();
			// the header
			let letters = _selection.filter((d,i) => i === 0).selectAll("span");
			letters.transition()
			  .duration(0)
			  .style("opacity", 1);

			// the path
			let path = _selection.filter((d,i) => i === 1).node();
			let l = path.getTotalLength();
			
			for (let t = 0; t < 1; t = t + explosionStrength) { 
				let p = path.getPointAtLength(t * l);
				app2.spawn(p.x , p.y);        
			}

			setTimeout( function() {
				app2.setActivate(false); // disable requestAnimationFrame calls
			}, 3000);
		}
	}
}

stars.js

// from https://codepen.io/osublake/pen/RLOzxo by Blake Bowen
// adjusted by some lines 
// - to optionally switch off requestAnimationFrame calls after animation is finished
// - to remove options panel
// - to remove mouse listener
// - to add background image

console.clear();
var log = console.log.bind(console);
var TAU = Math.PI * 2;
//
// PARTICLE
// ===========================================================================
var Particle = /** @class */ (function () {
    function Particle(texture, frame) {
        this.texture = texture;
        this.frame = frame;
        this.alive = false;
        this.width = frame.width;
        this.height = frame.height;
        this.originX = frame.width / 2;
        this.originY = frame.height / 2;
    }
    Particle.prototype.init = function (x, y) {
        if (x === void 0) { x = 0; }
        if (y === void 0) { y = 0; }
        var angle = random(TAU);
        var force = random(2, 6);
        this.x = x;
        this.y = y;
        this.alpha = 1;
        this.alive = true;
        this.theta = angle;
        this.vx = Math.sin(angle) * force;
        this.vy = Math.cos(angle) * force;
        this.rotation = Math.atan2(this.vy, this.vx);
        this.drag = random(0.82, 0.97);
        this.scale = random(0.1, 1);
        this.wander = random(0.5, 1.0);
        this.matrix = { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 };
        return this;
    };
    Particle.prototype.update = function () {
        var matrix = this.matrix;
        this.x += this.vx;
        this.y += this.vy;
        this.vx *= this.drag;
        this.vy *= this.drag;
        this.theta += random(-0.5, 0.5) * this.wander;
        this.vx += Math.sin(this.theta) * 0.1;
        this.vy += Math.cos(this.theta) * 0.1;
        this.rotation = Math.atan2(this.vy, this.vx);
        this.alpha *= 0.98;
        this.scale *= 0.985;
        this.alive = this.scale > 0.06 && this.alpha > 0.06;
        var cos = Math.cos(this.rotation) * this.scale;
        var sin = Math.sin(this.rotation) * this.scale;
        matrix.a = cos;
        matrix.b = sin;
        matrix.c = -sin;
        matrix.d = cos;
        matrix.tx = this.x - ((this.originX * matrix.a) + (this.originY * matrix.c));
        matrix.ty = this.y - ((this.originX * matrix.b) + (this.originY * matrix.d));
        return this;
    };
    Particle.prototype.draw = function (context) {
        var m = this.matrix;
        var f = this.frame;
        context.globalAlpha = this.alpha;
        context.setTransform(m.a, m.b, m.c, m.d, m.tx, m.ty);
        context.drawImage(this.texture, f.x, f.y, f.width, f.height, 0, 0, this.width, this.height);
        return this;
    };
    return Particle;
}());
//
// APP
// ===========================================================================
var App = /** @class */ (function () {
    function App(options) {
        var _this = this;
        this.pool = [];
        this.particles = [];
        this.pointer = {
            x: -9999,
            y: -9999
        };
        this.buffer = document.createElement("canvas");
        this.bufferContext = this.buffer.getContext("2d");
        this.supportsFilters = (typeof this.bufferContext.filter !== "undefined");
        _this.activate = true;
        this.setActivate = function(b) {_this.activate = b;} // setter for activate
        // this.setTexture = function(t) {_this.texture = t;}
        this.AnimationId;
        this.pointerMove = function (event) {
            event.preventDefault();
            var pointer = event.targetTouches ? event.targetTouches[0] : event;
            _this.pointer.x = pointer.clientX;
            _this.pointer.y = pointer.clientY;
            
            for (var i = 0; i < random(2, 7); i++) {
                _this.spawn(_this.pointer.x, _this.pointer.y);
            }
            
        };
        this.resize = function (event) {
            _this.width = _this.buffer.width = _this.view.width; // = window.innerWidth;
            _this.height = _this.buffer.height = _this.view.height; // = window.innerHeight;
        };
        this.render = function (time) {
            var context = _this.context;
            var particles = _this.particles;
            var bufferContext = _this.bufferContext;
            context.fillStyle = _this.backgroundColor;
            context.fillRect(0, 0, _this.width, _this.height);
            bufferContext.globalAlpha = 1;
            bufferContext.setTransform(1, 0, 0, 1, 0, 0);
            bufferContext.clearRect(0, 0, _this.width, _this.height);
            bufferContext.globalCompositeOperation = _this.blendMode;
            for (var i = 0; i < particles.length; i++) {
                var particle = particles[i];
                if (particle.alive) {
                    particle.update();
                }
                else {
                    _this.pool.push(particle);
                    removeItems(particles, i, 1);
                }
            }
            for (var _i = 0, particles_1 = particles; _i < particles_1.length; _i++) {
                var particle = particles_1[_i];
                particle.draw(bufferContext);
            }
            if (_this.supportsFilters) {
                if (_this.useBlurFilter) {
                    context.filter = "blur(" + _this.filterBlur + "px)";
                }
                context.drawImage(_this.buffer, 0, 0);
                if (_this.useContrastFilter) {
                    context.filter = "drop-shadow(4px 4px 4px rgba(0,0,0,1)) contrast(" + _this.filterContrast + "%)";
                }
            }
            context.drawImage(_this.buffer, 0, 0);
            context.filter = "none";
            
            if (_this.activate) { //  call requestAnimateFrame just if flag is true
                this.AnimationId = requestAnimationFrame(_this.render); 
            } 
            else { 
                cancelAnimationFrame(this.AnimationId);
            }
        };
        Object.assign(this, options);
        this.context = this.view.getContext("2d", { alpha: false });
    }
    App.prototype.spawn = function (x, y) {
        var particle;
        if (this.particles.length > this.maxParticles) {
            particle = this.particles.shift();
        }
        else if (this.pool.length) {
            particle = this.pool.pop();
        }
        else {
            particle = new Particle(this.texture, sample(this.frames));
        }
        particle.init(x, y);
        this.particles.push(particle);
        return this;
    };
    App.prototype.start = function () {
        this.resize();
        this.render();
        this.view.style.visibility = "visible";
        if (this.mouseListener) {
            if (window.PointerEvent) {
                window.addEventListener("pointermove", this.pointerMove);
            }
            else {
                window.addEventListener("mousemove", this.pointerMove);
                window.addEventListener("touchmove", this.pointerMove);
            }
        }
        window.addEventListener("resize", this.resize);
        requestAnimationFrame(this.render);
        return this;
    };
    return App;
}());
//
// CREATE FRAMES
// ===========================================================================
function createFrames(numFrames, width, height) {
    var frames = [];
    for (var i = 0; i < numFrames; i++) {
        frames.push({
            x: width * i,
            y: 0,
            width: width,
            height: height
        });
    }
    return frames;
}
//
// REMOVE ITEMS
// ===========================================================================
function removeItems(array, startIndex, removeCount) {
    var length = array.length;
    if (startIndex >= length || removeCount === 0) {
        return;
    }
    removeCount = (startIndex + removeCount > length ? length - startIndex : removeCount);
    var len = length - removeCount;
    for (var i = startIndex; i < len; ++i) {
        array[i] = array[i + removeCount];
    }
    array.length = len;
}
//
// RANDOM
// ===========================================================================
function random(min, max) {
    if (max == null) {
        max = min;
        min = 0;
    }
    if (min > max) {
        var tmp = min;
        min = max;
        max = tmp;
    }
    return min + (max - min) * Math.random();
}
function sample(array) {
    return array[(Math.random() * array.length) | 0];
}

style.css

body {
  margin: 0;
}

div.chart {
  position: relative;
  padding: 30px;
}

div.header {
  margin: 0 auto;
  text-align: center;
  width: max-content;
}

div.paths, div.stars {
  position: absolute;
  top: 0px;
  left: 0px;
  z-index: -1;  
}

h1 {
  color: lightGrey;
  font-family: 'Indie Flower', cursive;
}

h1 span {
  opacity: 0;
  white-space:pre;
}

h1 span.rotate {
  display: inline-block; 
  -webkit-transform: rotate(-0deg) scale(0.001);
}

.color-0 { color: rgb(255, 0, 171); }
.color-1 { color: rgb(0, 168, 255); }
.color-2 { color: rgb(171, 0, 255); }
.color-3 { color: rgb(255, 171, 0); }
.color-4 { color: rgb(168, 255, 0); }