Add Prototype Version of Brand Accent Component
This commit is contained in:
1 parent
450da37d5b
commit
0405b15b01
7 files changed
+161
-1
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
<canvas class="canvas" #canvasElement></canvas>
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { afterNextRender, Component, ElementRef, NgZone, OnDestroy, ViewChild } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'brand-accent',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
templateUrl: './brand-accent.html',
|
||||||
|
styleUrl: './brand-accent.scss',
|
||||||
|
})
|
||||||
|
export class BrandAccent implements OnDestroy {
|
||||||
|
@ViewChild('canvasElement') canvasRef!: ElementRef<HTMLCanvasElement>;
|
||||||
|
private ctx!: CanvasRenderingContext2D;
|
||||||
|
private resizeObserver?: ResizeObserver;
|
||||||
|
|
||||||
|
private canvasNativeSize: [number, number] = [0, 0];
|
||||||
|
|
||||||
|
private animation: {
|
||||||
|
id?: number;
|
||||||
|
startTime: number;
|
||||||
|
progress: number;
|
||||||
|
gridDimension: [number, number];
|
||||||
|
|
||||||
|
duration: number;
|
||||||
|
gridSquareSize: number;
|
||||||
|
} = {
|
||||||
|
startTime: 0,
|
||||||
|
progress: 0,
|
||||||
|
gridDimension: [0, 0],
|
||||||
|
|
||||||
|
duration: 30,
|
||||||
|
gridSquareSize: 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(private ngZone: NgZone) {
|
||||||
|
afterNextRender(() => {
|
||||||
|
this.ctx = this.canvasRef.nativeElement.getContext('2d')!;
|
||||||
|
|
||||||
|
this.ngZone.runOutsideAngular(() => {
|
||||||
|
this.resizeObserver = new ResizeObserver(() => this.resize());
|
||||||
|
this.resizeObserver.observe(this.canvasRef.nativeElement);
|
||||||
|
|
||||||
|
this.resize(); // Safe Initialization
|
||||||
|
this.animation.startTime = performance.now();
|
||||||
|
this.draw();
|
||||||
|
|
||||||
|
this.animate(performance.now());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private resize() {
|
||||||
|
const canvas = this.canvasRef.nativeElement;
|
||||||
|
const dpr = window.devicePixelRatio || 1;
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
|
||||||
|
canvas.width = rect.width * dpr;
|
||||||
|
canvas.height = rect.height * dpr;
|
||||||
|
|
||||||
|
this.ctx.scale(dpr, dpr);
|
||||||
|
this.updateAnimationInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateAnimationInfo() {
|
||||||
|
this.canvasNativeSize = [this.canvasRef.nativeElement.clientWidth, this.canvasRef.nativeElement.clientHeight];
|
||||||
|
|
||||||
|
this.animation.gridDimension = [
|
||||||
|
Math.trunc(this.canvasNativeSize[0] / this.animation.gridSquareSize) + 3,
|
||||||
|
Math.trunc(this.canvasNativeSize[1] / this.animation.gridSquareSize) + 3,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private animate = (currentTime: number) => {
|
||||||
|
this.animation.id = requestAnimationFrame(this.animate);
|
||||||
|
|
||||||
|
this.update(currentTime);
|
||||||
|
this.draw();
|
||||||
|
};
|
||||||
|
|
||||||
|
private update(currentTime: number) {
|
||||||
|
let elapsedTime: number;
|
||||||
|
elapsedTime = (currentTime - this.animation.startTime) / 1000;
|
||||||
|
|
||||||
|
const animationTime = elapsedTime % this.animation.duration;
|
||||||
|
this.animation.progress = animationTime / this.animation.duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private draw() {
|
||||||
|
this.ctx.clearRect(0, 0, this.canvasNativeSize[0], this.canvasNativeSize[1]);
|
||||||
|
const linearGradient = this.ctx.createLinearGradient(0, 0, this.canvasNativeSize[0], this.canvasNativeSize[1]);
|
||||||
|
|
||||||
|
linearGradient.addColorStop(0, 'oklch(1 0 0 / 0.6)');
|
||||||
|
linearGradient.addColorStop(1, 'oklch(0.7938 0.1292 47.77 / 0.6)');
|
||||||
|
|
||||||
|
this.ctx.fillStyle = linearGradient;
|
||||||
|
this.ctx.fillRect(0, 0, this.canvasNativeSize[0], this.canvasNativeSize[1]);
|
||||||
|
|
||||||
|
this.ctx.globalCompositeOperation = 'multiply';
|
||||||
|
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.lineWidth = 2;
|
||||||
|
this.ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
|
||||||
|
this.ctx.lineCap = 'round';
|
||||||
|
|
||||||
|
const lineRenderMargin = this.animation.gridSquareSize * 2;
|
||||||
|
|
||||||
|
const xOffset = (this.canvasNativeSize[0] - this.animation.gridDimension[0] * this.animation.gridSquareSize) / 2;
|
||||||
|
const yOffset = (this.canvasNativeSize[1] - this.animation.gridDimension[1] * this.animation.gridSquareSize) / 2;
|
||||||
|
const movingOffset = -(this.animation.gridSquareSize * this.animation.progress);
|
||||||
|
|
||||||
|
for (let y = 0; y < this.animation.gridDimension[1]; y++) {
|
||||||
|
this.ctx.moveTo(-lineRenderMargin, y * this.animation.gridSquareSize + yOffset + movingOffset);
|
||||||
|
this.ctx.lineTo(this.canvasNativeSize[0] + lineRenderMargin, y * this.animation.gridSquareSize + yOffset + movingOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let x = 0; x < this.animation.gridDimension[0]; x++) {
|
||||||
|
this.ctx.moveTo(x * this.animation.gridSquareSize + xOffset + movingOffset, -lineRenderMargin);
|
||||||
|
this.ctx.lineTo(x * this.animation.gridSquareSize + xOffset + movingOffset, this.canvasNativeSize[1] + lineRenderMargin);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.animation.id) {
|
||||||
|
cancelAnimationFrame(this.animation.id);
|
||||||
|
}
|
||||||
|
this.resizeObserver?.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
<brand-accent></brand-accent>
|
||||||
|
|
||||||
<h1>AEF Component Test</h1>
|
<h1>AEF Component Test</h1>
|
||||||
|
|
||||||
<section id="switch-test" class="test-container">
|
<section id="switch-test" class="test-container">
|
||||||
|
|||||||
@@ -7,3 +7,15 @@ section#dropdown-test {
|
|||||||
.test-container {
|
.test-container {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
brand-accent {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
z-index: -999999;
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { Component, signal } from '@angular/core';
|
import { Component, signal } from '@angular/core';
|
||||||
import { Switch } from '../../components/switch/switch';
|
import { Switch } from '../../components/switch/switch';
|
||||||
import { Dropdown, DropdownOption } from '../../components/dropdown/dropdown';
|
import { Dropdown, DropdownOption } from '../../components/dropdown/dropdown';
|
||||||
|
import { BrandAccent } from '../../components/brand-accent/brand-accent';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-main-page',
|
selector: 'app-main-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [Switch, Dropdown],
|
imports: [Switch, Dropdown, BrandAccent],
|
||||||
templateUrl: './main.html',
|
templateUrl: './main.html',
|
||||||
styleUrl: './main.scss',
|
styleUrl: './main.scss',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ section {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
border: 2px solid oklch(0 0 0 / 20%); // rgba(0, 0, 0, 0.2)
|
border: 2px solid oklch(0 0 0 / 20%); // rgba(0, 0, 0, 0.2)
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
|
background-color: oklch(1 0 0 / 60%);
|
||||||
|
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
|||||||
Reference in new issue
Block a user