diff --git a/public/Particle Flow - Demonstration.png b/public/Particle Flow - Demonstration.png deleted file mode 100644 index 0b3cf1e..0000000 Binary files a/public/Particle Flow - Demonstration.png and /dev/null differ diff --git a/public/Particle Flow - Demonstration4.png b/public/Particle Flow - Demonstration4.png new file mode 100644 index 0000000..7e7960e Binary files /dev/null and b/public/Particle Flow - Demonstration4.png differ diff --git a/public/black.png b/public/black.png new file mode 100644 index 0000000..4f3c2d5 Binary files /dev/null and b/public/black.png differ diff --git a/public/black2.png b/public/black2.png new file mode 100644 index 0000000..560b1fe Binary files /dev/null and b/public/black2.png differ diff --git a/public/code-slash-or.ico b/public/code-slash-or.ico new file mode 100644 index 0000000..625ff5c Binary files /dev/null and b/public/code-slash-or.ico differ diff --git a/public/code-slash-t.ico b/public/code-slash-t.ico new file mode 100644 index 0000000..7d4dbe0 Binary files /dev/null and b/public/code-slash-t.ico differ diff --git a/public/code-slash-teal.ico b/public/code-slash-teal.ico new file mode 100644 index 0000000..03e423d Binary files /dev/null and b/public/code-slash-teal.ico differ diff --git a/public/code-slash.ico b/public/code-slash.ico new file mode 100644 index 0000000..f0e931f Binary files /dev/null and b/public/code-slash.ico differ diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html index 3e863a1..72a9b05 100644 --- a/src/app/about/about.component.html +++ b/src/app/about/about.component.html @@ -1,6 +1,28 @@
-
-

About Me

-

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.

+
+
+

Introducing, Ian Vink

+

+ Full-Stack Developer with a strong interest in efficiency and automation +

+
+
+ + {{iconItem.text}} +
+
-
\ No newline at end of file + + +

+ + Here you'll find some of my projects, with a demo and the source code linked inside (unless there is a NDA)
+ Contact details can be found below. Cheers! +

+ + + \ No newline at end of file diff --git a/src/app/about/about.component.scss b/src/app/about/about.component.scss index e69de29..afceaba 100644 --- a/src/app/about/about.component.scss +++ b/src/app/about/about.component.scss @@ -0,0 +1,11 @@ +$distance: 15.5rem; + +h2:before { + display:none; + left: -$distance; +} + +h2:after { + display:none; + left: $distance; +} \ No newline at end of file diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts index 6f52bf4..251f6d2 100644 --- a/src/app/about/about.component.ts +++ b/src/app/about/about.component.ts @@ -1,12 +1,39 @@ +import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; +interface IconItem { + icon: string, + text: string +} + @Component({ selector: 'app-about', standalone: true, - imports: [], + imports: [CommonModule], templateUrl: './about.component.html', styleUrl: './about.component.scss' }) 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; + } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index a5c88b1..9765299 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { AfterViewInit, Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { HeaderComponent } from './header/header.component'; import { AboutComponent } from './about/about.component'; @@ -12,6 +12,42 @@ import { SkillsComponent } from './skills/skills.component'; templateUrl: './app.component.html', styleUrl: './app.component.scss' }) -export class AppComponent { +export class AppComponent implements AfterViewInit { 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); + }); + } } diff --git a/src/app/contact/contact.component.html b/src/app/contact/contact.component.html index 7461a7c..6a0e323 100644 --- a/src/app/contact/contact.component.html +++ b/src/app/contact/contact.component.html @@ -1,7 +1,22 @@ -
-
-

Contact

+
+
+

Contact

Feel free to reach out to me via email or social media.

- Send me an email + +
+ + + + + + + + + + + + + +
\ No newline at end of file diff --git a/src/app/contact/contact.component.scss b/src/app/contact/contact.component.scss index e69de29..0e3f1a9 100644 --- a/src/app/contact/contact.component.scss +++ b/src/app/contact/contact.component.scss @@ -0,0 +1,11 @@ +$distance: 13.5rem; + +h2:before { + display:none; + left: -$distance; +} + +h2:after { + display:none; + left: $distance; +} \ No newline at end of file diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index bfc2c55..613a561 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -1,18 +1,12 @@
-
Ian Vink
- -
-
\ No newline at end of file diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 09ddaec..7782275 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -20,7 +20,7 @@ export class HeaderComponent { constructor() { this.menuItems.push({name:"About", href: "#about"}); 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"}); } diff --git a/src/app/header/prev.txt b/src/app/header/prev.txt deleted file mode 100644 index 7f26ed5..0000000 --- a/src/app/header/prev.txt +++ /dev/null @@ -1,33 +0,0 @@ -
-
-
- -
Your Name
- - - - - -
- -
-
- - - -
-
diff --git a/src/app/projects/projects.component.html b/src/app/projects/projects.component.html index 7c3da8d..9b85a5d 100644 --- a/src/app/projects/projects.component.html +++ b/src/app/projects/projects.component.html @@ -1,33 +1,22 @@ - - -
-

Projects

+

Projects

+
-
- - {{project.title}} + {{project.title}}
-

{{project.title}}

-

{{project.description}}

+
+

{{project.title}}

+

{{project.year}}

+
+

{{project.description}}

{{technology}} + class="bg-gray-800 text-white text-sm px-2 py-1 rounded-md">{{technology}}
diff --git a/src/app/projects/projects.component.scss b/src/app/projects/projects.component.scss index 1b04702..f8dcf5a 100644 --- a/src/app/projects/projects.component.scss +++ b/src/app/projects/projects.component.scss @@ -1,35 +1,11 @@ +$distance: 14rem; + h2:before { - content: ""; - 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; + left: -$distance; } h2:after { - content: ""; - 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; -} \ No newline at end of file + left: $distance; +} + + diff --git a/src/app/projects/projects.component.ts b/src/app/projects/projects.component.ts index 59cb7d0..11c7276 100644 --- a/src/app/projects/projects.component.ts +++ b/src/app/projects/projects.component.ts @@ -1,28 +1,81 @@ import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { AfterViewInit, Component } from '@angular/core'; interface Project { - title: string, - year: number, - description: string, - image: string, - technologies: string[] + title: string; + year: string; + description: string; + image: string; + technologies: string[]; } @Component({ selector: 'app-projects', imports: [CommonModule], templateUrl: './projects.component.html', - styleUrl: './projects.component.scss' + styleUrl: './projects.component.scss', }) -export class ProjectsComponent { +export class ProjectsComponent{ projects: Project[] = []; 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({title:"Microcontroller Tetris", year: 2019, description: "Tetris on a 7-segment display", image:"Setup 3.png", technologies: ["C", "ATmega32", "Visual Studio"]}); - 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"]}); - + this.projects.push({ + title: 'Continuously learning Supplier-identification module', + 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'], + }); } - } diff --git a/src/app/skills/skills.component.html b/src/app/skills/skills.component.html index 00637e9..06772f9 100644 --- a/src/app/skills/skills.component.html +++ b/src/app/skills/skills.component.html @@ -1,85 +1,15 @@ - - - -
-
-

Skills

- - -
- - -
-

Frontend

-
    -
  • Angular
  • -
  • Typescript
  • -
  • Javascript
  • -
  • CSS
  • -
  • HTML
  • -
  • Tailwind
  • -
  • SASS (SCSS)
  • -
  • RxJS
  • -
  • Playwright
  • -
-
- - -
-

Backend

-
    -
  • Java
  • -
  • Spring Boot
  • -
  • JUnit
  • -
  • C#
  • -
  • ASP.NET Core
  • -
  • NestJS
  • -
  • SQL
  • -
  • PostgreSQL
  • -
  • MS SQLServer
  • -
-
- - -
-

DevOps

-
    -
  • Docker
  • -
  • CI/CD
  • -
  • Jenkins
  • -
  • Bash
  • -
  • Git
  • -
  • Linux
  • -
  • GitHub
  • -
  • GitLab
  • -
  • Nginx
  • -
-
- -
-
-
\ No newline at end of file +
+
\ No newline at end of file diff --git a/src/app/skills/skills.component.scss b/src/app/skills/skills.component.scss index 0ff4ab6..8d7059a 100644 --- a/src/app/skills/skills.component.scss +++ b/src/app/skills/skills.component.scss @@ -1,35 +1,9 @@ +$distance: 11rem; + h2:before { - content: ""; - 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; + left: -$distance; } h2:after { - content: ""; - 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; + left: $distance; } \ No newline at end of file diff --git a/src/app/skills/skills.component.ts b/src/app/skills/skills.component.ts index a960f5c..810931c 100644 --- a/src/app/skills/skills.component.ts +++ b/src/app/skills/skills.component.ts @@ -1,12 +1,67 @@ +import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; +interface Category { + title: string; + skills: string[]; +} + @Component({ selector: 'app-skills', standalone: true, - imports: [], + imports: [CommonModule], templateUrl: './skills.component.html', - styleUrl: './skills.component.scss' + styleUrl: './skills.component.scss', }) 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); + } } diff --git a/src/index.html b/src/index.html index 68bc202..524ab85 100644 --- a/src/index.html +++ b/src/index.html @@ -2,10 +2,11 @@ - Portfolio + Vink Development - + + diff --git a/src/styles.scss b/src/styles.scss index 111e651..a0ed87b 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,9 +1,59 @@ -/* You can add global styles to this file, and also import other style files */ - @use "tailwindcss/base"; @use "tailwindcss/components"; @use "tailwindcss/utilities"; html { - scroll-behavior: smooth; - } \ No newline at end of file + 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; +} + diff --git a/tailwind.config.js b/tailwind.config.js index 7586753..908ba4b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,19 @@ /** @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 = { content: [ "./src/**/*.{html,ts}", @@ -6,6 +21,6 @@ module.exports = { theme: { extend: {}, }, - plugins: [], + plugins: [backfaceVisibility], }