Skip to content

Instantly share code, notes, and snippets.

@EE2dev
Last active April 1, 2018 10:56
Show Gist options
  • Save EE2dev/d0dfd677cc5f3f7d05838b7bcb2c085e to your computer and use it in GitHub Desktop.
Save EE2dev/d0dfd677cc5f3f7d05838b7bcb2c085e to your computer and use it in GitHub Desktop.
Animated Intro - 3
license: mit

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:

  • Animated Intro (1) with
    • text elements: <h1>
    • animation: stars
    • color: individual letters
  • Animated Intro (2) with
    • text elements: <h1>
    • animation: stars + rotation
    • color: individual letters
  • Animated Intro (3) with
    • text elements: <h1>
    • animation: rotation + stars
    • color: individual letters
  • Animated Intro (4) with
    • text elements: <h1>
    • animation: stars
    • color: gradient
  • Animated Intro (5) with
    • text elements: SVG <textpath>
    • animation: stars
    • color: gradient
  • Animated Intro (6) with
    • text elements: SVG <textpath>
    • animation: stars
    • color: gradient
    • other: background image, show paths + textlength
  • Animated Intro (7) with
    • text elements: SVG <textpath>
    • animation: stars
    • color: gradient
    • other: background image, full size
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);
}
}
}
<!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>
// 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];
}
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); }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment