|
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 showPaths = true; |
|
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 = ["My favorite cities are", "New York", "and", "Paris"]; |
|
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 |
|
createTextpaths(myText); |
|
createPaths(); |
|
calculatePathEndpoints(); |
|
intializeStars(); |
|
animateStars(); |
|
}, |
|
}); |
|
} |
|
|
|
function createTextpaths(_textArray) { |
|
const container = d3.select(containerDiv).node().getBoundingClientRect(); |
|
const svgHeight = 150 + _textArray.length * 50; |
|
let sel = d3.select(containerDiv) |
|
.append("div") |
|
.attr("class", "header") |
|
.append("svg") |
|
.attr("width", container.width) |
|
.attr("height", svgHeight); |
|
|
|
createGradient(sel, "grad1"); |
|
|
|
// add textpaths for all strings |
|
for (let i = 0; i < _textArray.length; i++) { |
|
sel.append("text") |
|
.attr("class", "headline") |
|
.attr("class", () => (i < _textArray.length - 1) ? "headline no-effect h" + i : "headline effect h" + i) |
|
.attr("dy", "0.7em") |
|
.append("textPath") |
|
.attr("class", () => (i < _textArray.length - 1) ? "trans no-effect" : "trans effect") |
|
.attr("href", "#textpath" + i) |
|
.style("text-anchor","middle") |
|
.attr("startOffset","50%") |
|
.text(_textArray[i]); |
|
} |
|
} |
|
|
|
function createGradient(selection, idValue) { |
|
let lg = selection.append("defs") |
|
.append("linearGradient") |
|
.attr("id", idValue); |
|
|
|
lg.append("stop") |
|
.attr("offset", "0%") |
|
.style("stop-color", "rgb(255, 0, 171)") |
|
.style("stop-opacity", 1); |
|
|
|
lg.append("stop") |
|
.attr("offset", "25%") |
|
.style("stop-color", "rgb(0, 168, 255)") |
|
.style("stop-opacity", 1); |
|
|
|
lg.append("stop") |
|
.attr("offset", "50%") |
|
.style("stop-color", "rgb(171, 0, 255)") |
|
.style("stop-opacity", 1); |
|
|
|
lg.append("stop") |
|
.attr("offset", "75%") |
|
.style("stop-color", "rgb(255, 171, 0)") |
|
.style("stop-opacity", 1); |
|
|
|
lg.append("stop") |
|
.attr("offset", "100%") |
|
.style("stop-color", "rgb(168, 255, 0)") |
|
.style("stop-opacity", 1); |
|
} |
|
|
|
function createPaths() { |
|
const container = d3.select(containerDiv).node().getBoundingClientRect(); |
|
const selTP = d3.selectAll(containerDiv + " text.headline"); |
|
let selS = d3.selectAll(containerDiv + " svg"); |
|
|
|
selTP.each(function(d, i){ |
|
let pos = {}; |
|
let str; |
|
pos.width = container.width; |
|
pos.xStart = 0; |
|
pos.yStart = 100 + i * 50; |
|
|
|
selS.append("path") |
|
.attr("class", "headline trans" + " h" + i) |
|
.attr("id", "textpath" + i) |
|
.attr("d", (d) => { |
|
if (i < selTP.size() - 1) { |
|
str = "M" + pos.xStart + ", " + pos.yStart + " L" + (pos.width + pos.xStart) + ", " + pos.yStart; |
|
} else { |
|
const controlPoint = 50; |
|
let curveHeight = 30; |
|
let iTimes = Math.floor(pos.width / controlPoint); |
|
if (iTimes % 2 === 0) { iTimes -= 1;} |
|
const r = (pos.width - iTimes * controlPoint) / 2; |
|
|
|
str = "M" + pos.xStart + ", " + pos.yStart + " l " + r + ", 0"; |
|
for (let i = 1; i <= iTimes; i++) { |
|
curveHeight = curveHeight * -1; |
|
str += " q " + (controlPoint / 2) + " " + curveHeight + " " + controlPoint + " 0 "; |
|
} |
|
str += " l " + r + ", 0"; |
|
} |
|
return str; |
|
}); |
|
}); |
|
} |
|
|
|
function calculatePathEndpoints() { |
|
d3.selectAll("text.headline") |
|
.each(function(d,i) { |
|
let selPath = d3.select("path.headline.h" + i).node(); |
|
let pathLength = selPath.getTotalLength(); |
|
let textLength = d3.select(this).node().getComputedTextLength(); |
|
let start = pathLength * .5 - textLength / 2; |
|
let length = textLength; |
|
pathEndpoints.push({start, length}); |
|
let duration = textLength * transitionSpeed; |
|
pathDurations.push(duration); |
|
if (showPaths) { // show points for illustration |
|
showEndpoints(selPath, pathLength, textLength); |
|
} |
|
}); |
|
} |
|
|
|
function showEndpoints(_selection, pathLength, textLength) { |
|
d3.selectAll("path") |
|
.style("stroke", "yellow") |
|
.style("stroke-width", "2px") |
|
.style("stroke-dasharray", 5); |
|
let startPoint = _selection.getPointAtLength(pathLength * .5 - textLength / 2); |
|
let endPoint = _selection.getPointAtLength(pathLength * .5 + textLength / 2); |
|
d3.select("svg").append("circle").attr("cx", startPoint.x).attr("cy", startPoint.y).attr("r", 10).attr("fill", "lightblue").attr("opacity", .6); |
|
d3.select("svg").append("circle").attr("cx", endPoint.x).attr("cy", endPoint.y).attr("r", 10).attr("fill", "lightblue").attr("opacity", .6); |
|
} |
|
|
|
function intializeStars() { |
|
let bBox = d3.select("div.header").node().getBoundingClientRect(); |
|
let divh = d3.select(containerDiv) |
|
.insert("div", "div.header") |
|
.attr("class", "stars"); |
|
|
|
divh.append("canvas") |
|
.attr("id", "view") |
|
.attr("width", bBox.width) |
|
.attr("height", bBox.height); |
|
|
|
starOptions.view = document.querySelector("#view"); |
|
|
|
let background = new Image(); |
|
background.src = backgroundImage; |
|
starOptions.backgroundImage = background; |
|
|
|
app = new App(starOptions); |
|
window.addEventListener("load", app.start()); |
|
window.focus(); |
|
} |
|
|
|
function starsAlongPath(path, index) { |
|
return function(d, i, a) { |
|
return function(t) { |
|
let p = path.getPointAtLength(pathEndpoints[index].start + t * pathEndpoints[index].length); |
|
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 text |
|
_selection.filter((d,i) => i === 0) |
|
.transition() |
|
.duration(d => d) |
|
.delay(1000) |
|
.ease(d3.easeLinear) |
|
.style("opacity", 1); |
|
// the path |
|
let sel = _selection.filter((d,i) => i === 1); |
|
sel.transition() |
|
.duration(d => d) |
|
.delay(1000) |
|
.ease(d3.easeLinear) |
|
.attrTween("transform", starsAlongPath(sel.node(), _index)) |
|
.on ("end", function() { |
|
_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 text |
|
_selection.filter((d,i) => i === 0) |
|
.transition() |
|
.duration(0) |
|
.style("opacity", 1); |
|
|
|
// the path |
|
let path = _selection.filter((d,i) => i === 1).node(); |
|
let start = pathEndpoints[pathEndpoints.length-1].start; |
|
let length = pathEndpoints[pathEndpoints.length-1].length; |
|
|
|
for (let t = 0; t < 1; t = t + explosionStrength) { |
|
let p = path.getPointAtLength(start + t * length); |
|
app2.spawn(p.x , p.y); |
|
} |
|
|
|
setTimeout( function() { |
|
app2.setActivate(false); // disable requestAnimationFrame calls |
|
}, 3000); |
|
} |
|
} |
|
} |