Add animated background
This commit is contained in:
parent
2d7ecbc553
commit
7c01cae9d1
2 changed files with 171 additions and 9 deletions
151
assets/js/background-canvas.js
Normal file
151
assets/js/background-canvas.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
/* eslint-disable no-extra-parens */
|
||||
const PROBABILITY = 1 / 10000;
|
||||
const MIN_RADIUS = 1;
|
||||
const MAX_RADIUS = 4;
|
||||
const FADE_DURATION = 5000;
|
||||
const FADE_PROBABILITY = 1 / 100;
|
||||
const THREESIXTY_DEGREES = Math.PI / 180 * 360;
|
||||
|
||||
const COLORS = [
|
||||
"0,0,0",
|
||||
"0,255,100",
|
||||
"0,100,255"
|
||||
];
|
||||
|
||||
function getStyleString (color, alpha) {
|
||||
return `rgba(${color},${alpha})`;
|
||||
}
|
||||
|
||||
function randomBetween (min, max) {
|
||||
return (Math.random() * (max - min)) + min;
|
||||
}
|
||||
|
||||
export class BackgroundCanvas {
|
||||
constructor (element) {
|
||||
this._element = element;
|
||||
this._ctx = element.getContext("2d");
|
||||
this._destroyed = false;
|
||||
this._animationFrameRequestID = null;
|
||||
this._points = [];
|
||||
this._transformation = [0, 0];
|
||||
this.width = null;
|
||||
this.height = null;
|
||||
this._isTouch = false;
|
||||
|
||||
this._windowListeners = {
|
||||
touchstart: () => {
|
||||
this._isTouch = true;
|
||||
},
|
||||
resize: () => {
|
||||
this._points = [];
|
||||
this._init();
|
||||
},
|
||||
mousemove: event => {
|
||||
if (this._isTouch) return;
|
||||
|
||||
const { clientX: x, clientY: y } = event;
|
||||
this._transformation = [(this.width / 2) - x, (this.height / 2) - y];
|
||||
}
|
||||
};
|
||||
|
||||
Object.entries(this._windowListeners)
|
||||
.forEach(([event, listener]) => window.addEventListener(event, listener, { passive: true }));
|
||||
|
||||
this._init();
|
||||
this._loop();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this._destroyed) {
|
||||
throw new Error("Instance is already destroyed.");
|
||||
}
|
||||
|
||||
this._destroyed = true;
|
||||
this._element = null;
|
||||
this._ctx = null;
|
||||
|
||||
Object.entries(this._windowListeners)
|
||||
.forEach(([event, listener]) => window.removeEventListener(event, listener));
|
||||
|
||||
if (this._animationFrameRequestID !== null) {
|
||||
cancelAnimationFrame(this._animationFrameRequestID);
|
||||
}
|
||||
}
|
||||
|
||||
get destroyed() {
|
||||
return this._destroyed;
|
||||
}
|
||||
|
||||
_init() {
|
||||
this._element.width = window.innerWidth;
|
||||
this._element.height = window.innerHeight;
|
||||
|
||||
this.width = this._element.clientWidth;
|
||||
this.height = this._element.clientHeight;
|
||||
|
||||
for (let x = 0; x < this.width; x += 1) {
|
||||
for (let y = 0; y < this.height; y += 1) {
|
||||
if (Math.random() < PROBABILITY) {
|
||||
const radius = randomBetween(MIN_RADIUS, MAX_RADIUS);
|
||||
const z = randomBetween(-0.2, 0.2);
|
||||
const color = Math.round(Math.random() * (COLORS.length - 1));
|
||||
|
||||
this._points.push([x, y, z, radius, color]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Count of points:", this._points.length);
|
||||
}
|
||||
|
||||
_loop() {
|
||||
this._animationFrameRequestID = null;
|
||||
|
||||
this._draw();
|
||||
|
||||
if (!this._destroyed) {
|
||||
requestAnimationFrame(() => this._loop());
|
||||
}
|
||||
}
|
||||
|
||||
_draw() {
|
||||
// eslint-disable-next-line unicorn/prevent-abbreviations
|
||||
const ctx = this._ctx;
|
||||
const [transformX, transformY] = this._transformation;
|
||||
|
||||
ctx.clearRect(0, 0, this.width, this.height);
|
||||
|
||||
let first = true;
|
||||
for (const point of this._points) {
|
||||
const [x, y, z, radius, color] = point;
|
||||
let [,,,,, fadeStartTime] = point;
|
||||
|
||||
let fadeProgress = 0;
|
||||
|
||||
if (fadeStartTime === undefined) {
|
||||
if (Math.random() < FADE_PROBABILITY) {
|
||||
fadeStartTime = Date.now();
|
||||
point[5] = fadeStartTime;
|
||||
}
|
||||
} else {
|
||||
fadeProgress = (Date.now() - fadeStartTime) / FADE_DURATION;
|
||||
}
|
||||
|
||||
ctx.fillStyle = getStyleString(COLORS[color], fadeProgress);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(x + (transformX * z), y + (transformY * z), radius, 0, THREESIXTY_DEGREES);
|
||||
ctx.fill();
|
||||
|
||||
point[0] = x + 0.5;
|
||||
|
||||
if (point[0] > this.width) {
|
||||
point[0] = 0;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<div class="index-page">
|
||||
<KNavigationBar/>
|
||||
<KNavigationBar background-after-scroll/>
|
||||
<AnimatedLogo/>
|
||||
<canvas class="index-page__background" ref="canvas"/>
|
||||
<main class="index-page__content">
|
||||
<div class="index-page__socials">
|
||||
<a class="index-page__social-link" href="https://github.com/moritzruth" title="GitHub">
|
||||
|
@ -34,14 +35,16 @@
|
|||
height: calc(100vh - var(--x-navbar-height));
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
.index-page__background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.index-page__socials {
|
||||
|
@ -100,10 +103,18 @@
|
|||
import TwitterIcon from "@/assets/icons/twitter.svg";
|
||||
import InstagramIcon from "@/assets/icons/instagram.svg";
|
||||
import EmailIcon from "@/assets/icons/email.svg";
|
||||
import { BackgroundCanvas } from "@/assets/js/background-canvas";
|
||||
|
||||
export default {
|
||||
name: "IndexPage",
|
||||
layout: "none",
|
||||
components: { AnimatedLogo, GitHubIcon, TwitterIcon, InstagramIcon, EmailIcon, KNavigationBar, KFooter }
|
||||
components: { AnimatedLogo, GitHubIcon, TwitterIcon, InstagramIcon, EmailIcon, KNavigationBar, KFooter },
|
||||
mounted () {
|
||||
const backgroundCanvas = new BackgroundCanvas(this.$refs.canvas);
|
||||
|
||||
this.$once("hook:beforeDestroy", () => {
|
||||
backgroundCanvas.destroy();
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue