[Added] Animations, Intro, Projects, Spinning links
|
Before Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
@@ -1,6 +1,28 @@
|
|||||||
<section id="about" class="py-20 bg-white text-center">
|
<section id="about" class="py-20 bg-white text-center">
|
||||||
<div class="container mx-auto px-4">
|
<div class="container mx-auto px-4">
|
||||||
<h2 class="text-3xl font-semibold text-gray-800 mb-6">About Me</h2>
|
<div class="mb-6">
|
||||||
<p class="text-xl text-gray-600">I am a passionate software developer with experience in building web applications using modern technologies like Angular, TypeScript, and Tailwind CSS. I love creating clean, maintainable, and efficient code.</p>
|
<h2 class="text-3xl font-semibold italic text-gray-800 relative mb-1">Introducing, Ian Vink</h2>
|
||||||
|
<p class="text-xl font-medium text-gray-500 text-center">
|
||||||
|
Full-Stack Developer with a strong interest in efficiency and automation
|
||||||
|
</p>
|
||||||
|
<div class="flex justify-center md:items-center space-x-6 mt-6">
|
||||||
|
<div *ngFor="let iconItem of iconItems" class="flex flex-col md:flex-row items-center text-gray-600">
|
||||||
|
<i class="bi {{iconItem.icon}} text-2xl md:mr-2"></i>
|
||||||
|
<span class="text-lg">{{iconItem.text}}</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <br> -->
|
||||||
|
<!-- <br> -->
|
||||||
|
<p class="text-xl text-gray-600 text-center">
|
||||||
|
<!-- Full-Stack Developer with a strong interest in efficiency and reproduceability<br> -->
|
||||||
|
Here you'll find some of my projects, with a demo and the source code linked inside (unless there is a NDA)<br>
|
||||||
|
Contact details can be found below. Cheers!
|
||||||
|
</p>
|
||||||
|
<!-- <pre class="text-xl text-gray-600 text-center text-wrap">
|
||||||
|
Full-Stack Developer with a strong interest in efficiency and reproduceability
|
||||||
|
Below you'll find some noteworthy projects, with a demo and the source code linked inside (unless there is a NDA)
|
||||||
|
Contact details can be found below. Cheers!
|
||||||
|
</pre> -->
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
$distance: 15.5rem;
|
||||||
|
|
||||||
|
h2:before {
|
||||||
|
display:none;
|
||||||
|
left: -$distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2:after {
|
||||||
|
display:none;
|
||||||
|
left: $distance;
|
||||||
|
}
|
||||||
@@ -1,12 +1,39 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
interface IconItem {
|
||||||
|
icon: string,
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-about',
|
selector: 'app-about',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [],
|
imports: [CommonModule],
|
||||||
templateUrl: './about.component.html',
|
templateUrl: './about.component.html',
|
||||||
styleUrl: './about.component.scss'
|
styleUrl: './about.component.scss'
|
||||||
})
|
})
|
||||||
export class AboutComponent {
|
export class AboutComponent {
|
||||||
|
iconItems: IconItem[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.iconItems.push({icon: "bi-globe", text: "Dutch, English"});
|
||||||
|
this.iconItems.push({icon: "bi-geo-alt-fill", text: "Limburg, The Netherlands"});
|
||||||
|
this.iconItems.push({icon: "bi-cake2-fill", text: "28 years old"});
|
||||||
|
// this.iconItems.push({icon: "bi-file-person-fill", text: "View my CV"});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAge(): number {
|
||||||
|
const birthdate: Date = new Date(1995, 11, 15);
|
||||||
|
const today = new Date();
|
||||||
|
let age = today.getFullYear() - birthdate.getFullYear();
|
||||||
|
const monthDifference = today.getMonth() - birthdate.getMonth();
|
||||||
|
|
||||||
|
if (monthDifference < 0 || (monthDifference === 0 && today.getDate() < birthdate.getDate())) {
|
||||||
|
age--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import { AfterViewInit, Component } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { HeaderComponent } from './header/header.component';
|
import { HeaderComponent } from './header/header.component';
|
||||||
import { AboutComponent } from './about/about.component';
|
import { AboutComponent } from './about/about.component';
|
||||||
@@ -12,6 +12,42 @@ import { SkillsComponent } from './skills/skills.component';
|
|||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss'
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements AfterViewInit {
|
||||||
title = 'portfolio';
|
title = 'portfolio';
|
||||||
|
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.observeProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Watches for elements tagged with a fade-in animation when the user scrolls by them */
|
||||||
|
observeProjects() {
|
||||||
|
let projectItems: any[] = [];
|
||||||
|
const ups = document.querySelectorAll('.fade-in-up');
|
||||||
|
const lefts = document.querySelectorAll('.fade-in-left');
|
||||||
|
projectItems = projectItems.concat(ups);
|
||||||
|
projectItems = [...ups, ...lefts];
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
if (entry.target.classList.contains("fade-in-up")){
|
||||||
|
entry.target.classList.add('fade-in-up-ani');
|
||||||
|
entry.target.classList.remove('fade-in-up'); // Tp get rid of the opacity 0, conflicts with the scale animation
|
||||||
|
}
|
||||||
|
if (entry.target.classList.contains("fade-in-left")){
|
||||||
|
entry.target.classList.add('fade-in-left-ani');
|
||||||
|
entry.target.classList.remove('fade-in-left'); // Tp get rid of the opacity 0, conflicts with the scale animation
|
||||||
|
}
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
threshold: 0.2,
|
||||||
|
});
|
||||||
|
|
||||||
|
projectItems.forEach(item => {
|
||||||
|
observer.observe(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,22 @@
|
|||||||
<section id="contact" class="py-20 bg-gray-100">
|
<section id="contact" class="py-16 md:py-20 bg-gray-100">
|
||||||
<div class="container mx-auto text-center">
|
<div class="container mx-auto text-center px-6">
|
||||||
<h2 class="text-3xl font-semibold text-gray-800 mb-6">Contact</h2>
|
<h2 class="text-3xl font-semibold text-gray-800 mb-6 relative">Contact</h2>
|
||||||
<p class="text-xl text-gray-600 mb-6">Feel free to reach out to me via email or social media.</p>
|
<p class="text-xl text-gray-600 mb-6">Feel free to reach out to me via email or social media.</p>
|
||||||
<a href="mailto:your-email@example.com" class="text-blue-500">Send me an email</a>
|
<!-- <a href="mailto:vink.ian@protonmail.ch" class="text-blue-500">Send me an email</a> -->
|
||||||
|
<div class="mt-6">
|
||||||
|
|
||||||
|
<a href="mailto:vink.ian@protonmail.ch" target="_blank" class="text-gray-600 inline-block mx-3 p-4 rounded-full hover:text-orange-500 transition-all transform hover:rotate-[360deg] duration-700 ease-in-out">
|
||||||
|
<i class="bi bi-envelope-fill text-4xl"></i> <!-- Larger icon with Tailwind -->
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="https://github.com/ninjaws" target="_blank" class="text-gray-600 inline-block mx-3 p-4 rounded-full hover:text-black transition-all transform hover:rotate-[360deg] duration-700 ease-in-out">
|
||||||
|
<i class="bi bi-github text-4xl"></i> <!-- Larger icon with Tailwind -->
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- LinkedIn link with icon -->
|
||||||
|
<a href="https://www.linkedin.com/in/vink-development" target="_blank" class="text-gray-600 inline-block mx-3 p-4 rounded-full hover:text-blue-700 transition-all transform hover:rotate-[360deg] duration-700 ease-in-out">
|
||||||
|
<i class="bi bi-linkedin text-4xl"></i> <!-- Larger icon with Tailwind -->
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
$distance: 13.5rem;
|
||||||
|
|
||||||
|
h2:before {
|
||||||
|
display:none;
|
||||||
|
left: -$distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2:after {
|
||||||
|
display:none;
|
||||||
|
left: $distance;
|
||||||
|
}
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
<header class="bg-gray-800 text-white py-6 relative">
|
<header class="bg-gray-800 text-white py-6 relative">
|
||||||
<div class="container mx-auto px-4">
|
<div class="container mx-auto px-4">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<!-- Logo -->
|
|
||||||
<div class="text-2xl font-bold">Ian Vink</div>
|
<div class="text-2xl font-bold">Ian Vink</div>
|
||||||
|
|
||||||
<!-- Navbar for larger screens -->
|
|
||||||
<nav class="hidden md:flex space-x-6">
|
<nav class="hidden md:flex space-x-6">
|
||||||
<a href="#about" class="hover:text-gray-400">About</a>
|
<a *ngFor="let menuItem of menuItems" href="{{menuItem.href}}" class="hover:text-gray-400">{{menuItem.name}}</a>
|
||||||
<a href="#skills" class="hover:text-gray-400">Skills</a>
|
|
||||||
<a href="#projects" class="hover:text-gray-400">Projects</a>
|
|
||||||
<a href="#contact" class="hover:text-gray-400">Contact</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Mobile Menu Button -->
|
|
||||||
<div class="md:hidden">
|
<div class="md:hidden">
|
||||||
<button (click)="toggleMenu()" class="text-white focus:outline-none">
|
<button (click)="toggleMenu()" class="text-white focus:outline-none">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
@@ -22,12 +16,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile Menu -->
|
|
||||||
<div *ngIf="isMenuOpen" class="absolute top-full left-0 w-full bg-gray-800 text-white space-y-4 p-6 z-50">
|
<div *ngIf="isMenuOpen" class="absolute top-full left-0 w-full bg-gray-800 text-white space-y-4 p-6 z-50">
|
||||||
<a *ngFor="let menuItem of menuItems" href="{{menuItem.href}}" class="block hover:text-gray-400" (click)="closeMenu()">{{menuItem.name}}</a>
|
<a *ngFor="let menuItem of menuItems" href="{{menuItem.href}}" class="block hover:text-gray-400" (click)="closeMenu()">{{menuItem.name}}</a>
|
||||||
<!-- <a href="#skills" class="block hover:text-gray-400">Skills</a> -->
|
|
||||||
<!-- <a href="#projects" class="block hover:text-gray-400">Projects</a> -->
|
|
||||||
<!-- <a href="#contact" class="block hover:text-gray-400">Contact</a> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -20,7 +20,7 @@ export class HeaderComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.menuItems.push({name:"About", href: "#about"});
|
this.menuItems.push({name:"About", href: "#about"});
|
||||||
this.menuItems.push({name:"Skills", href: "#skills"});
|
this.menuItems.push({name:"Skills", href: "#skills"});
|
||||||
this.menuItems.push({name:"Projects", href: "#projets"});
|
this.menuItems.push({name:"Projects", href: "#projects"});
|
||||||
this.menuItems.push({name:"Contact", href: "#contact"});
|
this.menuItems.push({name:"Contact", href: "#contact"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
<header class="bg-gray-800 text-white py-6">
|
|
||||||
<div class="container mx-auto px-4">
|
|
||||||
<div class="flex justify-between items-center">
|
|
||||||
<!-- Logo -->
|
|
||||||
<div class="text-2xl font-bold">Your Name</div>
|
|
||||||
|
|
||||||
<!-- Navbar for larger screens -->
|
|
||||||
<nav class="hidden md:flex space-x-6">
|
|
||||||
<a href="#about" class="hover:text-gray-400">About</a>
|
|
||||||
<a href="#skills" class="hover:text-gray-400">Skills</a>
|
|
||||||
<a href="#projects" class="hover:text-gray-400">Projects</a>
|
|
||||||
<a href="#contact" class="hover:text-gray-400">Contact</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Mobile Menu Button -->
|
|
||||||
<div class="md:hidden">
|
|
||||||
<button (click)="toggleMenu()" class="text-white focus:outline-none">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Mobile Menu -->
|
|
||||||
<div *ngIf="isMenuOpen" class="md:hidden mt-4 space-y-4 text-center">
|
|
||||||
<a href="#about" class="block hover:text-gray-400">About</a>
|
|
||||||
<a href="#skills" class="block hover:text-gray-400">Skills</a>
|
|
||||||
<a href="#projects" class="block hover:text-gray-400">Projects</a>
|
|
||||||
<a href="#contact" class="block hover:text-gray-400">Contact</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
@@ -1,33 +1,22 @@
|
|||||||
<!-- <section id="projects" class="py-20 bg-white">
|
|
||||||
<div class="container mx-auto text-center">
|
|
||||||
<h2 class="text-3xl font-semibold text-gray-800 mb-6">Projects</h2>
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
||||||
<div class="bg-gray-100 p-6 rounded-lg shadow-md">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-700">Project 1</h3>
|
|
||||||
<p class="text-gray-600 mt-2">A short description of the project.</p>
|
|
||||||
<a href="#" class="text-blue-500 mt-4 inline-block">View Project</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section> -->
|
|
||||||
|
|
||||||
<!-- bg-gray-900 -->
|
|
||||||
<section id="projects" class="py-16 bg-gray-800 text-white">
|
<section id="projects" class="py-16 bg-gray-800 text-white">
|
||||||
<div class="container mx-auto px-6">
|
<div class="container mx-auto px-6">
|
||||||
<h2 class="text-4xl font-semibold text-center mb-12 relative">Projects</h2>
|
<h2 class="text-4xl font-semibold text-center mb-12 relative fade-in-left">Projects</h2>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
|
<div *ngFor="let project of projects"
|
||||||
|
class="bg-white/10 backface-hidden rounded-lg shadow-lg hover:scale-105 transition-all
|
||||||
|
duration-300 group relative fade-in-up">
|
||||||
|
|
||||||
<div *ngFor="let project of projects" class="bg-white/10 backdrop-blur-lg rounded-lg shadow-lg overflow-visible group relative">
|
<img src="{{project.image}}" alt="{{project.title}}" class="w-full h-56 rounded-sm object-cover">
|
||||||
<!-- class="bg-gray-400 rounded-lg shadow-lg overflow-visible group"> -->
|
|
||||||
<img src="{{project.image}}" alt="{{project.title}}"
|
|
||||||
class="w-full h-56 rounded-sm object-cover hover:scale-105 transition-all duration-300">
|
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
|
<div class="flex justify-between">
|
||||||
<h3 class="text-2xl font-semibold text-teal-400 mb-4">{{project.title}}</h3>
|
<h3 class="text-2xl font-semibold text-teal-400 mb-4">{{project.title}}</h3>
|
||||||
<p class="text-gray-300 mb-4">{{project.description}}</p>
|
<p class="pl-1">{{project.year}}</p>
|
||||||
|
</div>
|
||||||
|
<p class="text-gray-300 mb-4 whitespace-pre-line">{{project.description}}</p>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<span *ngFor="let technology of project.technologies"
|
<span *ngFor="let technology of project.technologies"
|
||||||
class="bg-teal-500 text-white text-xs px-2 py-1 rounded-md">{{technology}}</span>
|
class="bg-gray-800 text-white text-sm px-2 py-1 rounded-md">{{technology}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,35 +1,11 @@
|
|||||||
|
$distance: 14rem;
|
||||||
|
|
||||||
h2:before {
|
h2:before {
|
||||||
content: "";
|
left: -$distance;
|
||||||
width: 4rem;
|
|
||||||
height: 2px;
|
|
||||||
background: #ea5f00;
|
|
||||||
/* Orange color */
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
/* Center it horizontally */
|
|
||||||
transform: translateY(-50%);
|
|
||||||
/* Adjust vertically to ensure it's centered */
|
|
||||||
left: -14rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2:after {
|
h2:after {
|
||||||
content: "";
|
left: $distance;
|
||||||
width: 4rem;
|
|
||||||
height: 2px;
|
|
||||||
background: #ea5f00;
|
|
||||||
/* Orange color */
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
/* Center it horizontally */
|
|
||||||
transform: translateY(-50%);
|
|
||||||
/* Adjust vertically to ensure it's centered */
|
|
||||||
left: 14rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,81 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { AfterViewInit, Component } from '@angular/core';
|
||||||
|
|
||||||
interface Project {
|
interface Project {
|
||||||
title: string,
|
title: string;
|
||||||
year: number,
|
year: string;
|
||||||
description: string,
|
description: string;
|
||||||
image: string,
|
image: string;
|
||||||
technologies: string[]
|
technologies: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-projects',
|
selector: 'app-projects',
|
||||||
imports: [CommonModule],
|
imports: [CommonModule],
|
||||||
templateUrl: './projects.component.html',
|
templateUrl: './projects.component.html',
|
||||||
styleUrl: './projects.component.scss'
|
styleUrl: './projects.component.scss',
|
||||||
})
|
})
|
||||||
export class ProjectsComponent {
|
export class ProjectsComponent{
|
||||||
projects: Project[] = [];
|
projects: Project[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.projects.push({title:"Jelly Rescue", year: 2015, description: "Rescue Jellies", image:"Jelly Game - demo.png", technologies: ["C++", "SFML2.0", "Visual Studio"]});
|
this.projects.push({
|
||||||
this.projects.push({title:"Microcontroller Tetris", year: 2019, description: "Tetris on a 7-segment display", image:"Setup 3.png", technologies: ["C", "ATmega32", "Visual Studio"]});
|
title: 'Continuously learning Supplier-identification module',
|
||||||
this.projects.push({title:"Goal-based vector field pathfinding algorithm", year: 2018, description: "Particle simulator that can be controlled using the vector-field, and can deal with obstacles", image:"Particle Flow - Demonstration.png", technologies: ["Java", "Swing", "IntelliJ"]});
|
year: '2021',
|
||||||
|
description:
|
||||||
|
'An application that combines key-value detection and multiclass-classification, to identify the correct supplier for an invoice. It uses online machine learning to keep improving itself as the user provides feedback.',
|
||||||
|
image: 'Supplier Recognition - Demo.png',
|
||||||
|
technologies: [
|
||||||
|
'Angular (Typescript, HTML, CSS)',
|
||||||
|
'ASP.NET Core (C#)',
|
||||||
|
'MS SQL Server',
|
||||||
|
'Python',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
this.projects.push({
|
||||||
|
title: 'Seabed modification tool',
|
||||||
|
year: '2019 - 2020',
|
||||||
|
description:
|
||||||
|
'A tool to edit the seabed mesh of a maritime simulator. The collisionmap is also updated, to allow ships to interact with this update terrain. \nThis tool allows instructors to rapidly create new terrain, for training shipcrews',
|
||||||
|
image: 'Seabed Modification.png',
|
||||||
|
technologies: ['Unigine (C#)', 'AGC Dynamics (C++)', 'Qt Creator'],
|
||||||
|
});
|
||||||
|
this.projects.push({
|
||||||
|
title: 'Goal-based vector field pathfinding algorithm',
|
||||||
|
year: '2018',
|
||||||
|
description:
|
||||||
|
'The application uses a vector field to direct particles towards a target. The vectors are calculated using a heatmap, which centers on the position of the target. \nThe particles collide with the environment, which can be edited and saved/loaded using the GUI.',
|
||||||
|
image: 'black.png',//'Particle Flow - Demonstration4.png', //"black.png",
|
||||||
|
technologies: ['Java', 'Swing', 'IntelliJ'],
|
||||||
|
});
|
||||||
|
this.projects.push({
|
||||||
|
title: 'Jelly Rescue',
|
||||||
|
year: '2015',
|
||||||
|
description:
|
||||||
|
'A platformer written in C++, with saving/loading, tile- and collisionmaps, enemy AI, collectibles and state management to switch between multiple screens (game, menu, victory, etc.)',
|
||||||
|
image: 'Jelly Game - demo.png',
|
||||||
|
technologies: ['C++', 'SFML2.0', 'Platformer', 'Visual Studio'],
|
||||||
|
});
|
||||||
|
this.projects.push({
|
||||||
|
title: 'Microcontroller Tetris (group project)',
|
||||||
|
year: '2019',
|
||||||
|
description:
|
||||||
|
'Tetris written in C, visualized on dot-matrix displays, with the score visible on a 7-segment display and a menu system visible on the ATMega128.',
|
||||||
|
image: 'Setup 3.png',
|
||||||
|
technologies: [
|
||||||
|
'C',
|
||||||
|
'ATmega128',
|
||||||
|
'7-segment display',
|
||||||
|
'Dot-matrix displays',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
this.projects.push({
|
||||||
|
title: 'Augmented Reality game (group project)',
|
||||||
|
year: '2019',
|
||||||
|
description:
|
||||||
|
'The game uses computer vision to track a red ball, which serves as the controller. I created a system for the rigging and animations for the player character and the opponents. The math for OpenGL has been handwritten.',
|
||||||
|
image: 'AR Game.png',
|
||||||
|
technologies: ['C++', 'OpenCV', 'OpenGL', 'IrrKlang'],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +1,15 @@
|
|||||||
<!-- <section id="skills" class="py-20 bg-gray-100">
|
<section id="skills" class="py-16 bg-gray-900 text-white">
|
||||||
<div class="container mx-auto text-center">
|
|
||||||
<h2 class="text-3xl font-semibold text-gray-800 mb-6">Skills</h2>
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8">
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-700">Angular</h3>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-700">JavaScript</h3>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-700">Tailwind CSS</h3>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-700">Node.js</h3>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-700">Git & GitHub</h3>
|
|
||||||
</div>
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-md">
|
|
||||||
<h3 class="text-xl font-semibold text-gray-700">TypeScript</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section> -->
|
|
||||||
|
|
||||||
|
|
||||||
<section id="skills" class="py-16 bg-gray-900 text-white">
|
|
||||||
<div class="container mx-auto px-6">
|
<div class="container mx-auto px-6">
|
||||||
<h2 class="text-4xl font-semibold text-center mb-12 relative">Skills</h2>
|
<h2 class="text-4xl font-semibold text-center mb-12 relative fade-in-left">Skills</h2>
|
||||||
|
|
||||||
<!-- Grid container for Frontend, Backend, DevOps -->
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
|
||||||
<!-- Frontend -->
|
<div *ngFor="let category of categories" class="flex flex-col items-center p-8 bg-gray-800 rounded-lg shadow-lg fade-in-up">
|
||||||
<div class="flex flex-col items-center p-8 bg-gray-800 rounded-lg shadow-lg">
|
<h3 class="text-3xl font-semibold mb-4 text-center text-teal-400">{{category.title}}</h3>
|
||||||
<h3 class="text-3xl font-semibold mb-4 text-center text-teal-400">Frontend</h3>
|
|
||||||
<ul class="list-none space-y-2 text-center">
|
<ul class="list-none space-y-2 text-center">
|
||||||
<li>Angular</li>
|
<li *ngFor="let skill of category.skills">{{skill}}</li>
|
||||||
<li>Typescript</li>
|
|
||||||
<li>Javascript</li>
|
|
||||||
<li>CSS</li>
|
|
||||||
<li>HTML</li>
|
|
||||||
<li>Tailwind</li>
|
|
||||||
<li>SASS (SCSS)</li>
|
|
||||||
<li>RxJS</li>
|
|
||||||
<li>Playwright</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Backend -->
|
|
||||||
<div class="flex flex-col items-center p-8 bg-gray-800 rounded-lg shadow-lg">
|
|
||||||
<h3 class="text-3xl font-semibold mb-4 text-center text-teal-400">Backend</h3>
|
|
||||||
<ul class="list-none space-y-2 text-center">
|
|
||||||
<li>Java</li>
|
|
||||||
<li>Spring Boot</li>
|
|
||||||
<li>JUnit</li>
|
|
||||||
<li>C#</li>
|
|
||||||
<li>ASP.NET Core</li>
|
|
||||||
<li>NestJS</li>
|
|
||||||
<li>SQL</li>
|
|
||||||
<li>PostgreSQL</li>
|
|
||||||
<li>MS SQLServer</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- DevOps -->
|
|
||||||
<div class="flex flex-col items-center p-8 bg-gray-800 rounded-lg shadow-lg">
|
|
||||||
<h3 class="text-3xl font-semibold mb-4 text-center text-teal-400">DevOps</h3>
|
|
||||||
<ul class="list-none space-y-2 text-center">
|
|
||||||
<li>Docker</li>
|
|
||||||
<li>CI/CD</li>
|
|
||||||
<li>Jenkins</li>
|
|
||||||
<li>Bash</li>
|
|
||||||
<li>Git</li>
|
|
||||||
<li>Linux</li>
|
|
||||||
<li>GitHub</li>
|
|
||||||
<li>GitLab</li>
|
|
||||||
<li>Nginx</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -1,35 +1,9 @@
|
|||||||
|
$distance: 11rem;
|
||||||
|
|
||||||
h2:before {
|
h2:before {
|
||||||
content: "";
|
left: -$distance;
|
||||||
width: 4rem;
|
|
||||||
height: 2px;
|
|
||||||
background: #ea5f00;
|
|
||||||
/* Orange color */
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
/* Center it horizontally */
|
|
||||||
transform: translateY(-50%);
|
|
||||||
/* Adjust vertically to ensure it's centered */
|
|
||||||
left: -11rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2:after {
|
h2:after {
|
||||||
content: "";
|
left: $distance;
|
||||||
width: 4rem;
|
|
||||||
height: 2px;
|
|
||||||
background: #ea5f00;
|
|
||||||
/* Orange color */
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
/* Center it horizontally */
|
|
||||||
transform: translateY(-50%);
|
|
||||||
/* Adjust vertically to ensure it's centered */
|
|
||||||
left: 11rem;
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,67 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
interface Category {
|
||||||
|
title: string;
|
||||||
|
skills: string[];
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-skills',
|
selector: 'app-skills',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [],
|
imports: [CommonModule],
|
||||||
templateUrl: './skills.component.html',
|
templateUrl: './skills.component.html',
|
||||||
styleUrl: './skills.component.scss'
|
styleUrl: './skills.component.scss',
|
||||||
})
|
})
|
||||||
export class SkillsComponent {
|
export class SkillsComponent {
|
||||||
|
frontend: Category = { title: 'Frontend', skills: [] };
|
||||||
|
backend: Category = { title: 'Backend', skills: [] };
|
||||||
|
devOps: Category = { title: 'DevOps', skills: [] };
|
||||||
|
// other: Category = {title: "Other", skills: []};
|
||||||
|
|
||||||
|
categories: Category[] = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.frontend.skills.push(
|
||||||
|
'Angular',
|
||||||
|
'Typescript',
|
||||||
|
'Javascript',
|
||||||
|
'HTML',
|
||||||
|
'CSS/Sass',
|
||||||
|
'Tailwind',
|
||||||
|
'RxJS',
|
||||||
|
'i18n',
|
||||||
|
'Playwright',
|
||||||
|
);
|
||||||
|
// 'WebGL/WebGPU',
|
||||||
|
// 'WebAssembly (Rust/C++)'
|
||||||
|
this.backend.skills.push(
|
||||||
|
'Java',
|
||||||
|
'Spring Boot',
|
||||||
|
'JUnit',
|
||||||
|
'C#',
|
||||||
|
'ASP.NET Core',
|
||||||
|
'NestJS',
|
||||||
|
'Python',
|
||||||
|
'Machine Learning',
|
||||||
|
'Flask/FastAPI',
|
||||||
|
'SQL',
|
||||||
|
'PostgreSQL',
|
||||||
|
'MS SQL Server'
|
||||||
|
);
|
||||||
|
this.devOps.skills.push(
|
||||||
|
'Docker',
|
||||||
|
'CI/CD',
|
||||||
|
'Jenkins',
|
||||||
|
'Git',
|
||||||
|
'GitHub/GitLab',
|
||||||
|
'Linux',
|
||||||
|
'Bash',
|
||||||
|
'Nginx',
|
||||||
|
'Windows',
|
||||||
|
'PowerShell'
|
||||||
|
);
|
||||||
|
// this.other.skills.push("MS Office", "Machine Learning", "Computer Vision");
|
||||||
|
this.categories.push(this.frontend, this.backend, this.devOps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Portfolio</title>
|
<title>Vink Development</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="code-slash-or.ico">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
|||||||
@@ -1,9 +1,59 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
|
|
||||||
@use "tailwindcss/base";
|
@use "tailwindcss/base";
|
||||||
@use "tailwindcss/components";
|
@use "tailwindcss/components";
|
||||||
@use "tailwindcss/utilities";
|
@use "tailwindcss/utilities";
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Orange wings shown on each h2 element */
|
||||||
|
h2:before, h2:after {
|
||||||
|
content: "";
|
||||||
|
width: 4rem;
|
||||||
|
height: 2px;
|
||||||
|
background: #ea5f00;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.fade-in-up {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.fade-in-up-ani {
|
||||||
|
animation: fadeInUp 0.95s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInLeft {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform:translateX(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform:translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.fade-in-left {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.fade-in-left-ani {
|
||||||
|
animation: fadeInLeft 0.95s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,19 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
|
|
||||||
|
const plugin = require('tailwindcss/plugin')
|
||||||
|
|
||||||
|
const backfaceVisibility = plugin(function({addUtilities}) {
|
||||||
|
addUtilities({
|
||||||
|
'.backface-visible': {
|
||||||
|
'backface-visibility': 'visible',
|
||||||
|
},
|
||||||
|
'.backface-hidden': {
|
||||||
|
'backface-visibility': 'hidden',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: [
|
content: [
|
||||||
"./src/**/*.{html,ts}",
|
"./src/**/*.{html,ts}",
|
||||||
@@ -6,6 +21,6 @@ module.exports = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [backfaceVisibility],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||