mirror of
https://github.com/adam-benyekkou/my_portfolio.git
synced 2026-01-15 20:20:09 +00:00
Added animation to tree node
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<app-section-title title="NEURAL PROFILE" />
|
<app-section-title title="NEURAL PROFILE" />
|
||||||
|
|
||||||
<div class="flex-1 flex items-start justify-between gap-6 mt-6">
|
<div class="flex-1 flex items-start justify-between gap-6 mt-6">
|
||||||
<!-- Left side - Neural Profile Tree (always shown) -->
|
<!-- Left side - Neural Profile Tree with NieR animations -->
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<app-neural-profile-tree />
|
<app-neural-profile-tree />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/* NieR-style hover effects for tree nodes */
|
||||||
|
.tree-node-item {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Underline effect */
|
||||||
|
.tree-node-item::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--color-nier-text-dark);
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text color change on hover */
|
||||||
|
.tree-node-item:hover {
|
||||||
|
color: var(--color-nier-text-dark);
|
||||||
|
border-bottom-color: var(--color-nier-text-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Underline animation on hover */
|
||||||
|
.tree-node-item:hover::before {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Glitch effect on hover - target the text span directly */
|
||||||
|
.tree-node-item:hover span.text-nier-dark {
|
||||||
|
animation: nier-text-glitch 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alternative - target any span with text */
|
||||||
|
.tree-node-item:hover span:last-of-type {
|
||||||
|
animation: nier-text-glitch 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scan line for tree nodes */
|
||||||
|
.tree-node-item .scan-line {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--color-nier-text-dark);
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 3;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-item:hover .scan-line {
|
||||||
|
animation: nier-button-scan 0.3s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text glitch animation for tree nodes */
|
||||||
|
@keyframes nier-text-glitch {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
clip-path: inset(0 0 0 0);
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
transform: translateX(-2px);
|
||||||
|
clip-path: inset(0 0 40% 0);
|
||||||
|
}
|
||||||
|
15% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
transform: translateX(1px);
|
||||||
|
clip-path: inset(40% 0 0 0);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translateX(-1px);
|
||||||
|
clip-path: inset(20% 0 20% 0);
|
||||||
|
}
|
||||||
|
35% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The scan line animation */
|
||||||
|
@keyframes nier-button-scan {
|
||||||
|
0% {
|
||||||
|
opacity: 0.5;
|
||||||
|
left: -100%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Active/click state for tree nodes */
|
||||||
|
.tree-node-item:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Touch device optimization */
|
||||||
|
@media (hover: none) {
|
||||||
|
.tree-node-item:active::before {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-item:active .scan-line {
|
||||||
|
animation: nier-button-scan 0.3s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-item:active {
|
||||||
|
color: var(--color-nier-text-dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<div class="bg-nier-bg border-2 border-nier-border text-nier-dark w-96 font-terminal text-sm leading-relaxed p-4">
|
<div class="bg-nier-bg border-2 border-nier-border text-nier-dark w-96 md:w-[28rem] lg:w-[32rem] font-terminal text-sm md:text-base lg:text-lg leading-relaxed p-4 md:p-6">
|
||||||
@for (node of treeData(); track node.id) {
|
@for (node of treeData(); track node.id) {
|
||||||
<div
|
<div
|
||||||
class="border-l-2 border-transparent"
|
class="border-l-2 border-transparent"
|
||||||
@@ -6,8 +6,10 @@
|
|||||||
[class.hidden]="!node.visible"
|
[class.hidden]="!node.visible"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex items-center py-1 px-2 cursor-pointer transition-all duration-150 ease-in-out hover:bg-nier-mid/20 hover:translate-x-0.5 relative"
|
class="tree-node-item flex items-center py-1 md:py-2 px-2 md:px-3 cursor-pointer transition-all duration-150 ease-in-out hover:bg-nier-mid/20 hover:translate-x-0.5 relative overflow-hidden"
|
||||||
|
[class.font-bold]="node.isSelected"
|
||||||
[style.padding-left]="getIndentPadding(node.level)"
|
[style.padding-left]="getIndentPadding(node.level)"
|
||||||
|
[attr.data-label]="node.title"
|
||||||
(click)="selectNode(node)"
|
(click)="selectNode(node)"
|
||||||
>
|
>
|
||||||
<!-- Selection indicator -->
|
<!-- Selection indicator -->
|
||||||
@@ -17,7 +19,7 @@
|
|||||||
|
|
||||||
<!-- Expand/Collapse Icon -->
|
<!-- Expand/Collapse Icon -->
|
||||||
<span
|
<span
|
||||||
class="w-4 h-4 flex items-center justify-center text-nier-accent mr-2 cursor-pointer select-none"
|
class="w-4 h-4 md:w-5 md:h-5 lg:w-6 lg:h-6 flex items-center justify-center text-nier-accent mr-2 md:mr-3 cursor-pointer select-none text-sm md:text-base lg:text-lg"
|
||||||
[class.text-nier-mid]="!node.hasChildren"
|
[class.text-nier-mid]="!node.hasChildren"
|
||||||
(click)="$event.stopPropagation(); toggleExpand(node)"
|
(click)="$event.stopPropagation(); toggleExpand(node)"
|
||||||
>
|
>
|
||||||
@@ -34,13 +36,16 @@
|
|||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Node Title -->
|
<!-- Node Title with NieR effects -->
|
||||||
<span
|
<span
|
||||||
class="flex-1 text-nier-dark select-none"
|
class="flex-1 text-nier-dark select-none relative z-10"
|
||||||
[class.font-bold]="node.isSelected"
|
[class.font-bold]="node.isSelected"
|
||||||
>
|
>
|
||||||
{{ node.title }}
|
{{ node.title }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<!-- Scan line effect -->
|
||||||
|
<span class="scan-line"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface NeuralProfileNode {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule],
|
imports: [CommonModule],
|
||||||
templateUrl: './neural-profile-tree.component.html',
|
templateUrl: './neural-profile-tree.component.html',
|
||||||
styles: [],
|
styleUrl: './neural-profile-tree.component.css',
|
||||||
})
|
})
|
||||||
export class NeuralProfileTreeComponent {
|
export class NeuralProfileTreeComponent {
|
||||||
// Input for external data (optional)
|
// Input for external data (optional)
|
||||||
@@ -32,8 +32,8 @@ export class NeuralProfileTreeComponent {
|
|||||||
private getInitialData(): NeuralProfileNode[] {
|
private getInitialData(): NeuralProfileNode[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: 'neuralProfile',
|
id: 'workstation',
|
||||||
title: ':TECH_CORE ',
|
title: 'Workstation/',
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
level: 0,
|
level: 0,
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { HeaderContactLinksComponent } from './header-contact-links.component';
|
|
||||||
|
|
||||||
describe('HeaderContactLinksComponent', () => {
|
|
||||||
let component: HeaderContactLinksComponent;
|
|
||||||
let fixture: ComponentFixture<HeaderContactLinksComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [HeaderContactLinksComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(HeaderContactLinksComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user