From 310543963ddf2977e1681d3239ae9b7453d1fe67 Mon Sep 17 00:00:00 2001 From: auchers Date: Sun, 17 May 2020 19:06:15 -0400 Subject: [PATCH] initial animation and title --- dist/index.html | 13 ++++++++- src/Animation.js | 44 ++++++++++++++++++++++++++++ src/Title.js | 22 ++++++++++---- src/Triangle.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 26 ++++++++++++++--- src/style.scss | 21 +++++++++++++- utils/constants.js | 14 +++++++++ 7 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 src/Animation.js create mode 100644 src/Triangle.js create mode 100644 utils/constants.js diff --git a/dist/index.html b/dist/index.html index 02f3f74..a2ac09a 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1 +1,12 @@ -Output Management \ No newline at end of file + + + + + Output Management + + + + + + + diff --git a/src/Animation.js b/src/Animation.js new file mode 100644 index 0000000..8a1a13b --- /dev/null +++ b/src/Animation.js @@ -0,0 +1,44 @@ +import { select } from "d3"; +import { Triangle } from "./Triangle"; +import { animationConfig } from "../utils/constants"; + +const { width, height, count, delay, drawRate } = animationConfig; +const [x, y] = [width / 2, height / 2]; + +export class Animation { + intervalId; + intervalCount = 0; + + constructor() { + this.triangles = []; + + this.canvas = select("body") + .append("canvas") + .attr("id", "animation") + .attr("width", width) + .attr("height", height); + + this.ctx = this.canvas.node().getContext("2d"); + + [...Array(count)].forEach((d, i) => + setTimeout(() => this.triangles.push(new Triangle(this.ctx)), i * delay) + ); + } + + draw() { + this.intervalCount++; + this.ctx.fillStyle = "rgba(210, 210, 210, 0.05)"; + this.ctx.fillRect(0, 0, width, height); // clear canvas + this.triangles.map((t) => t.update([x, y])); + } + + startAnimation() { + // note: doing `()=> this.function` maintains the proper scoping for the method + // so that it has access to the object as `this` + this.intervalId = setInterval(() => this.draw(), drawRate); + } + + stopAnimation() { + clearInterval(this.intervalId); + } +} diff --git a/src/Title.js b/src/Title.js index df41c71..f4afb06 100644 --- a/src/Title.js +++ b/src/Title.js @@ -1,7 +1,19 @@ import { select } from "d3"; -const Title = () => { - console.log("testing"); - select("#container").append("h1").text("Interactive Data Visualization"); -}; -export default Title; +export class Title { + constructor() { + this.el = select("body") + .append("div") + .attr("id", "page-title") + .text("Interactive Data Visualization") + .classed("visible", false); + } + + makeVisible() { + this.el.classed("visible", true); + } + + makeHidden() { + this.el.classed("visible", false); + } +} diff --git a/src/Triangle.js b/src/Triangle.js new file mode 100644 index 0000000..13c71d4 --- /dev/null +++ b/src/Triangle.js @@ -0,0 +1,72 @@ +import { getRandom, animationConfig } from "../utils/constants"; + +const { width, height, count, delay, drawRate } = animationConfig; + +export class Triangle { + constructor(ctx) { + this.ctx = ctx; + this.resetPoints(); + this.opacity = 1; + this.draw(); + this.count = 0; + } + + draw() { + const [cx, cy] = this.calcCentroid(); + this.ctx.save(); + this.ctx.translate(width / 2 - cx, height / 2 - cy); + this.ctx.beginPath(); + this.ctx.strokeStyle = "steelblue"; + this.ctx.globalAlpha = this.opacity; + this.ctx.moveTo(this.p1.x, this.p1.y); + this.ctx.lineTo(this.p2.x, this.p2.y); + this.ctx.lineTo(this.p3.x, this.p3.y); + this.ctx.closePath(); + this.ctx.stroke(); + this.ctx.restore(); + } + + update() { + this.opacity = Math.max(this.opacity - 0.003, 0); + this.p1 = this.updatePoint(this.p1); + this.p2 = this.updatePoint(this.p2); + this.p3 = this.updatePoint(this.p3); + this.count += 1; + this.checkBounds(); + this.draw(); + } + + checkBounds() { + const inBounds = [this.p1, this.p2, this.p3].reduce( + (inBounds, { x, y }) => inBounds && x < width && y < height, + true + ); + if (!inBounds && this.count > (delay / drawRate) * count) + this.resetPoints(); + } + + resetPoints() { + this.opacity = 1; + this.p1 = { x: 0, y: 0, ...this.getRandomUnitVector() }; + this.p2 = { x: 0, y: 0, ...this.getRandomUnitVector() }; + this.p3 = { x: 0, y: 0, ...this.getRandomUnitVector() }; + this.count = 0; + } + + getRandomUnitVector() { + const [mx, my] = [getRandom(), getRandom()]; //[deltaX, deltaY] + const mag = Math.sqrt(mx ** 2 + my ** 2); + return { mx: mx / mag, my: my / mag }; + } + + calcCentroid() { + const { p1, p2, p3 } = this; + return [(p1.x + p2.x + p3.x) / 3, (p1.y + p2.y + p3.y) / 3]; + } + + updatePoint({ x, y, mx, my }) { + // they appear to get slower as they get larger, so gradually increase + // the mx/my so that they accelerate out + return { x: x + mx, y: y + my, mx: mx * 1.005, my: my * 1.005 }; + } +} diff --git a/src/index.js b/src/index.js index b8d5502..2bdc3ee 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,24 @@ -import { select } from "d3"; -import Title from "./Title"; +import { Animation } from "./Animation"; +import { Title } from "./Title"; + import "./style.scss"; -select("#container").text("Interactive Data Visualization"); -Title(); +import { appConfig } from "../utils/constants"; +const { titleDuration } = appConfig; + +class Controller { + state = { + selectedStudent: null, + isAnimating: true, + }; + + constructor() { + this.animation = new Animation(); + this.title = new Title(); + this.animation.startAnimation(); + + setTimeout(() => this.title.makeVisible(), titleDuration); + } +} + +new Controller(); diff --git a/src/style.scss b/src/style.scss index 1f92011..c2f42b9 100644 --- a/src/style.scss +++ b/src/style.scss @@ -1,3 +1,22 @@ body { - background: palegoldenrod; + font-family: "Open Sans"; +} + +canvas#animation { + position: absolute; + top: 0; + left: 0; +} + +#page-title { + position: relative; + font-weight: 700; + font-size: 4em; + visibility: hidden; + + &.visible { + visibility: visible; + color: transparent; + -webkit-text-stroke: black 1px; + } } diff --git a/utils/constants.js b/utils/constants.js new file mode 100644 index 0000000..9db4ac3 --- /dev/null +++ b/utils/constants.js @@ -0,0 +1,14 @@ +export const getRandom = (min = -1, max = 1) => + Math.random() * (max - min) + min; + +export const appConfig = { + titleDuration: 2500, +}; + +export const animationConfig = { + width: window.innerWidth, + height: window.innerHeight, + count: 25, + delay: 1500, + drawRate: 35, +};