feat!: inserted new site
6
src/css/bootstrap.min.css
vendored
Normal file
150
src/css/style.css
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
.navbar{
|
||||||
|
background-color: #000b;
|
||||||
|
/* background: linear-gradient(0deg,rgba(35, 43, 62, 200) 0%, rgba(0, 0, 0, 200) 100%); */
|
||||||
|
border-bottom: 1px solid grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
height: 3rem;
|
||||||
|
background-color: rgba(0, 0, 0, .1);
|
||||||
|
border: solid rgba(0, 0, 0, .15);
|
||||||
|
border-width: 1px 0;
|
||||||
|
box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-picture{
|
||||||
|
width: 8em;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 5px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-icon {
|
||||||
|
width: 4em;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
margin: .5em .5em;
|
||||||
|
padding: 3px;
|
||||||
|
transition: all .2s;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-icon:hover {
|
||||||
|
width: 5em;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid white;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 255, 255, .8);
|
||||||
|
background-color: #FFFFFF88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 40rem;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid black;
|
||||||
|
background-color: #aaaaaadd;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
box-shadow: .5em .5em 1.5em rgba(0, 0, 0, .3);
|
||||||
|
margin: 1em 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
box-shadow: inset 0 0 3px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card iframe {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16/9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card a {
|
||||||
|
color: #0000FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #8888FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel {
|
||||||
|
max-width: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-control-next,
|
||||||
|
.carousel-control-prev {
|
||||||
|
filter: brightness(.5) drop-shadow(0px 0px 1px #FFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-caption>h5 {
|
||||||
|
color: #aaaaffdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-indicators [data-bs-target] {
|
||||||
|
background-color: #aaaaffdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attention {
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowlines-short li:not(:last-of-type)::after {
|
||||||
|
content: "";
|
||||||
|
width: 52%;
|
||||||
|
margin: 0 4rem;
|
||||||
|
border-bottom: 1px solid #66666688;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowlines li:not(:last-of-type)::after {
|
||||||
|
content: "";
|
||||||
|
width: 78%;
|
||||||
|
margin: 0 5rem;
|
||||||
|
border-bottom: 1px solid #66666688;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowlines-short li,
|
||||||
|
.rowlines li {
|
||||||
|
transition: all .2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowlines li:hover {
|
||||||
|
/* background-color: #8888; */
|
||||||
|
box-shadow: inset 0 0 3px white;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowlines-short li:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
-webkit-transform: translate3d(0, 0, 0);
|
||||||
|
-moz-transform: translate3d(0, 0, 0);
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
z-index: -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
background: #00000032;
|
||||||
|
border: 2px solid #888888;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
box-shadow: .5em .5em 1.5em rgba(0, 0, 0, .3);
|
||||||
|
padding: 1em;
|
||||||
|
margin: 1em auto;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
.note {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 32rem;
|
||||||
|
}
|
||||||
|
}
|
BIN
src/docs/cv-wardtruyen-2025-Java.pdf
Normal file
BIN
src/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/img/arch.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
src/img/docker.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
src/img/gitea.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
src/img/lua.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
src/img/me.jpg
Executable file
After Width: | Height: | Size: 45 KiB |
BIN
src/img/mountain2.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
src/img/neovim.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
src/img/nginx.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
src/img/oneko.gif
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/img/tds1.png
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
src/img/tds2.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
src/img/tds3.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
src/img/tds4.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
src/img/traefik.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
src/img/tux.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
src/img/windows.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
src/img/wterminal.png
Normal file
After Width: | Height: | Size: 19 KiB |
329
src/index.html
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Ward Truyen's CV</title>
|
||||||
|
|
||||||
|
<meta name="description" content="Op zoek naar een Java developer job.">
|
||||||
|
|
||||||
|
<!-- Facebook Meta Tags -->
|
||||||
|
<meta property="og:url" content="https://www.ward.truyen.network/">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:title" content="Ward Truyen's CV">
|
||||||
|
<meta property="og:description" content="Op zoek naar een Java developer job.">
|
||||||
|
<meta property="og:image" content="/img/me.jpg">
|
||||||
|
|
||||||
|
<!-- Twitter Meta Tags -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta property="twitter:domain" content="ward.truyen.network">
|
||||||
|
<meta property="twitter:url" content="https://www.ward.truyen.network/">
|
||||||
|
<meta name="twitter:title" content="Ward Truyen's CV">
|
||||||
|
<meta name="twitter:description" content="Op zoek naar een Java developer job.">
|
||||||
|
<meta name="twitter:image" content="/img/me.jpg">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="text-bg-dark bg-dark">
|
||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-expand-md fixed-top navbar-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">Ward Truyen</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown"
|
||||||
|
aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNavDropdown">
|
||||||
|
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#skills">Vaardigheden</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#werk">Werkervaring</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#studies">Studies</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#portfolio">Portfolio</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1 class="visually-hidden">Ward Truyen's CV</h1>
|
||||||
|
<section id="intro">
|
||||||
|
<div class="p-2 my-5">
|
||||||
|
<div class="note">
|
||||||
|
<h2 class="d-inline">Introductie</h2>
|
||||||
|
<img class="my-picture d-none d-md-block" src="/img/me.jpg" alt="[my picture]" />
|
||||||
|
<p>Ik ben een computer liefhebber met een gedreven, hulpvaardig karakter en een zeer technisch geinformeerde
|
||||||
|
achtergrond. Bijleren doe ik graag. Werk met structuur en orde. Ik ben geduldig,
|
||||||
|
zal met volle concentratie problemen aanpakken en streven naar afwerking. Je kan me vinden voor een
|
||||||
|
aangename babbel of een luisterend oor.</p>
|
||||||
|
<a href="docs/cv-wardtruyen-2025-Java.pdf" class="btn btn-primary" download>Download mijn CV</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="skills" class="divider"></div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="px-4 py-4">
|
||||||
|
<h2>Vaardigheden</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<h4>Back-end</h4>
|
||||||
|
<ul>
|
||||||
|
<li>Java EE</li>
|
||||||
|
<li>Spring & REST</li>
|
||||||
|
<li>SQL & Hibernate</li>
|
||||||
|
<li>Lombok</li>
|
||||||
|
<li>Maven & Gradle</li>
|
||||||
|
<li>JSP</li>
|
||||||
|
<li>JUnit</li>
|
||||||
|
<li>Multithreading & Concurrency</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<h4>Front-end</h4>
|
||||||
|
<ul>
|
||||||
|
<li>JavaScript</li>
|
||||||
|
<li>TypeScript</li>
|
||||||
|
<li>Angular</li>
|
||||||
|
<li>HTML & CSS</li>
|
||||||
|
<li>NPM</li>
|
||||||
|
<li>JQuery</li>
|
||||||
|
<li>Bootstrap</li>
|
||||||
|
<li>async & wait</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<h4>Tools</h4>
|
||||||
|
<ul>
|
||||||
|
<li>Git</li>
|
||||||
|
<li>Scrutm & Agile</li>
|
||||||
|
<li>VS code & IntelliJ & vim</li>
|
||||||
|
<li>Docker & Virtualisatie</li>
|
||||||
|
<li>CI/CD</li>
|
||||||
|
<li>Powershell & bash</li>
|
||||||
|
<li>Terminal & SSH</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<img class="img-icon" src="img/tux.png" alt="Linux" title="Tux, the linux mascot" />
|
||||||
|
<img class="img-icon" src="img/arch.png" alt="Arch linux" title="Arch linux" />
|
||||||
|
<img class="img-icon" src="img/windows.png" alt="Windows" title="MS Windows" />
|
||||||
|
<img class="img-icon" src="img/traefik.png" alt="Traefik" title="Traefik: a reverse proxy" />
|
||||||
|
<img class="img-icon" src="img/docker.png" alt="Docker" title="Docker" />
|
||||||
|
<img class="img-icon" src="img/lua.png" alt="Lua" title="Lua script" />
|
||||||
|
<img class="img-icon" src="img/neovim.png" alt="Neovim" title="Neovim editor" />
|
||||||
|
<img class="img-icon" src="img/nginx.png" alt="Nginx" title="Nginx a reverse proxy" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="werk" class="divider"></div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="px-4 py-4">
|
||||||
|
<h2>Werkervaring</h2>
|
||||||
|
<ul class="rowlines">
|
||||||
|
<li class="row attention">
|
||||||
|
<span class="col-5">Opleiding Java Enterprise</span>
|
||||||
|
<span class="col-4">/</span>
|
||||||
|
<span class="col-3">2025 - Nu</span>
|
||||||
|
</li>
|
||||||
|
<li class="row text-secondary">
|
||||||
|
<span class="col-5">Tijdelijke Interim jobs</span>
|
||||||
|
<span class="col-4">Verscheidene werkplaatsen</span>
|
||||||
|
<span class="col-3">2025 - Nu</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-5">Zelfstudie Angular en docker</span>
|
||||||
|
<span class="col-4">/</span>
|
||||||
|
<span class="col-3">2023 - 2025</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-5">Allround ICT-er</span>
|
||||||
|
<span class="col-4">Nelissen computers</span>
|
||||||
|
<span class="col-3">2022 - 2022</span>
|
||||||
|
</li>
|
||||||
|
<li class="row attention">
|
||||||
|
<span class="col-5">ICT Trainer Java</span>
|
||||||
|
<span class="col-4">Multimedi</span>
|
||||||
|
<span class="col-3">2018 - 2021</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-5">Zelfstudie Java en Linux</span>
|
||||||
|
<span class="col-4">/</span>
|
||||||
|
<span class="col-3">2016 - 2017</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-5">Helpdesk technische dienst</span>
|
||||||
|
<span class="col-4">KUL</span>
|
||||||
|
<span class="col-3">2015 - 2016</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-5">Vrijwillig programmeur</span>
|
||||||
|
<span class="col-4">KUL</span>
|
||||||
|
<span class="col-3">2013 - 2014</span>
|
||||||
|
</li>
|
||||||
|
<li class="row text-secondary">
|
||||||
|
<span class="col-5">Tijdelijke Interim jobs</span>
|
||||||
|
<span class="col-4">Verscheidene werkplaatsen</span>
|
||||||
|
<span class="col-3">2007 - 2013</span>
|
||||||
|
</li>
|
||||||
|
<li class="row attention">
|
||||||
|
<span class="col-5">Programmeur C++</span>
|
||||||
|
<span class="col-4">Zetes</span>
|
||||||
|
<span class="col-3">2007 - 2007</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-5">Computer dokter</span>
|
||||||
|
<span class="col-4">Compudoc</span>
|
||||||
|
<span class="col-3">2006 - 2007</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="studies" class="divider"></div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="px-4 py-4">
|
||||||
|
<h2>Studies</h2>
|
||||||
|
<ul class="rowlines-short">
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-6">Multimedi Java Enterprise ontwikkelaar</span>
|
||||||
|
<span class="col-6">2025 - nu</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-6">Multimedi Systeembeheer</span>
|
||||||
|
<span class="col-6">2017 - 2018</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-6">Cevora Java Enterprise ontwikkelaar</span>
|
||||||
|
<span class="col-6">2008 - 2009</span>
|
||||||
|
</li>
|
||||||
|
<li class="row">
|
||||||
|
<span class="col-6">A2: Elektrische installatietechnieken</span>
|
||||||
|
<span class="col-6">1998 - 2004</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="portfolio" class="divider"></div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="px-4 py-4">
|
||||||
|
<h2>Portfolio</h2>
|
||||||
|
<h4>Huidig project:</h4>
|
||||||
|
<a target="_blank" href="https://tds.truyen.network">TDS Family website</a>
|
||||||
|
<p>Full stack Java & Angular website.</p>
|
||||||
|
<div id="carouselExampleIndicators" class="carousel carousel-dark slide carousel-fade mx-auto">
|
||||||
|
<div class="carousel-indicators">
|
||||||
|
<button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="0" class="active"
|
||||||
|
aria-current="true" aria-label="Slide 1"></button>
|
||||||
|
<button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="1"
|
||||||
|
aria-label="Slide 2"></button>
|
||||||
|
<button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="2"
|
||||||
|
aria-label="Slide 3"></button>
|
||||||
|
<button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="3"
|
||||||
|
aria-label="Slide 4"></button>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-inner">
|
||||||
|
<div class="carousel-item active">
|
||||||
|
<img src="img/tds1.png" class="d-block w-100" alt="Screenshot 1">
|
||||||
|
<div class="carousel-caption d-none d-md-block">
|
||||||
|
<h5>1</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-item">
|
||||||
|
<img src="img/tds2.png" class="d-block w-100" alt="Screenshot 2">
|
||||||
|
<div class="carousel-caption d-none d-md-block">
|
||||||
|
<h5>2</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-item">
|
||||||
|
<img src="img/tds3.png" class="d-block w-100" alt="Screenshot 3">
|
||||||
|
<div class="carousel-caption d-none d-md-block">
|
||||||
|
<h5>3</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-item">
|
||||||
|
<img src="img/tds4.png" class="d-block w-100" alt="Screenshot 4">
|
||||||
|
<div class="carousel-caption d-none d-md-block">
|
||||||
|
<h5>4</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleIndicators"
|
||||||
|
data-bs-slide="prev">
|
||||||
|
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Previous</span>
|
||||||
|
</button>
|
||||||
|
<button class="carousel-control-next" type="button" data-bs-target="#carouselExampleIndicators"
|
||||||
|
data-bs-slide="next">
|
||||||
|
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Next</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h4>Websites:</h4>
|
||||||
|
<ul>
|
||||||
|
<li><a target="_blank" href="https://ward.truyen.network">Ward(this one)</a></li>
|
||||||
|
<li><a target="_blank" href="https://willem.truyen.network">Willem games</a></li>
|
||||||
|
<li><a target="_blank" href="https://git.truyen.network">Gitea</a></li>
|
||||||
|
<!-- <li><a target="_blank" href="https://wterminal.truyen.network">WTerminal</a></li> -->
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<h4>Oudere projecten:</h4>
|
||||||
|
<div class="row">
|
||||||
|
<div class="card">
|
||||||
|
<a target="_blank" href="https://wterminal.truyen.network">
|
||||||
|
<h3>WTerminal</h3>
|
||||||
|
</a>
|
||||||
|
<p>JavaScript 2023-2024<br>
|
||||||
|
Kan gebruikt worden in websites en als browser-addon</p>
|
||||||
|
<h5>Screenshot:</h5>
|
||||||
|
<img style="width: 100%" src="img/wterminal.png">
|
||||||
|
</img>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3>Ronny</h3>
|
||||||
|
<p>VB.net 2004-2006<br>Game met Direct draw rendering</p>
|
||||||
|
<h5>Youtube video:</h5>
|
||||||
|
<iframe src="https://www.youtube.com/embed/m7tgDUc2jXs">
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3>Minesweeper</h3>
|
||||||
|
<p>C/C++ 2006-2013<br>Game met OpenGL rendering</p>
|
||||||
|
<h5>Youtube video:</h5>
|
||||||
|
<iframe src="https://www.youtube.com/embed/Rejp6cVRxfc">
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<footer class="text-end">
|
||||||
|
<p class="text-secondary my-0 me-1">Life is a long lesson in humility.</p>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
<script src="js/starback.js"></script>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<script src="js/main.js"> </script>
|
||||||
|
<script src="js/oneko.js"> </script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
7
src/js/bootstrap.min.js
vendored
Normal file
49
src/js/main.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
const starback = new Starback("#canvas", {
|
||||||
|
width: document.body.clientWidth,
|
||||||
|
height: document.body.clientHeight,
|
||||||
|
type: 'dot',
|
||||||
|
quantity: 100,
|
||||||
|
direction: 225,
|
||||||
|
backgroundColor: ['#0e1118', '#232b3e'],
|
||||||
|
randomOpacity: true,
|
||||||
|
});
|
||||||
|
const mountain = new Image();
|
||||||
|
mountain.src = 'img/mountain2.png';
|
||||||
|
mountain.onload = () => {
|
||||||
|
starback.addToFront((ctx) => {
|
||||||
|
ctx.drawImage(
|
||||||
|
mountain,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
mountain.width,
|
||||||
|
mountain.height,
|
||||||
|
0,
|
||||||
|
canvas.height - mountain.height,
|
||||||
|
canvas.width,
|
||||||
|
mountain.height
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resizeCanvas() {
|
||||||
|
const canvas = document.getElementById("canvas");
|
||||||
|
canvas.setAttribute("width", window.innerWidth);
|
||||||
|
canvas.setAttribute("height", window.innerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLoadDoc() {
|
||||||
|
resizeCanvas();
|
||||||
|
const elements = document.getElementsByClassName("nav-link");
|
||||||
|
for (const el of elements) {
|
||||||
|
el.onclick = onNavLinkClick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNavLinkClick() {
|
||||||
|
const collapse = document.getElementsByClassName('navbar-collapse');
|
||||||
|
if (collapse[0] != null)
|
||||||
|
collapse[0].classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", resizeCanvas);
|
||||||
|
window.addEventListener("load", onLoadDoc);
|
310
src/js/oneko.js
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
// oneko.js: https://github.com/adryd325/oneko.js
|
||||||
|
|
||||||
|
var onekoEnabled = true;
|
||||||
|
|
||||||
|
(function oneko() {
|
||||||
|
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
||||||
|
|
||||||
|
const isReducedMotion =
|
||||||
|
window.matchMedia(`(prefers-reduced-motion: reduce)`) === true ||
|
||||||
|
window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
|
||||||
|
|
||||||
|
if (isReducedMotion || isMobile)
|
||||||
|
{
|
||||||
|
console.log("disabling cat");
|
||||||
|
onekoEnabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nekoEl = document.createElement("div");
|
||||||
|
|
||||||
|
let nekoPosX = window.innerWidth + 32;
|
||||||
|
let nekoPosY = window.innerHeight + 32;
|
||||||
|
|
||||||
|
let mousePosX = 0;
|
||||||
|
let mousePosY = 0;
|
||||||
|
|
||||||
|
let frameCount = 0;
|
||||||
|
let idleTime = 0;
|
||||||
|
let idleAnimation = null;
|
||||||
|
let idleAnimationFrame = 0;
|
||||||
|
|
||||||
|
const nekoSpeed = 10;
|
||||||
|
const spriteSets = {
|
||||||
|
idle: [[-3, -3]],
|
||||||
|
alert: [[-7, -3]],
|
||||||
|
scratchSelf: [
|
||||||
|
[-5, 0],
|
||||||
|
[-6, 0],
|
||||||
|
[-7, 0],
|
||||||
|
],
|
||||||
|
scratchWallN: [
|
||||||
|
[0, 0],
|
||||||
|
[0, -1],
|
||||||
|
],
|
||||||
|
scratchWallS: [
|
||||||
|
[-7, -1],
|
||||||
|
[-6, -2],
|
||||||
|
],
|
||||||
|
scratchWallE: [
|
||||||
|
[-2, -2],
|
||||||
|
[-2, -3],
|
||||||
|
],
|
||||||
|
scratchWallW: [
|
||||||
|
[-4, 0],
|
||||||
|
[-4, -1],
|
||||||
|
],
|
||||||
|
tired: [[-3, -2]],
|
||||||
|
sleeping: [
|
||||||
|
[-2, 0],
|
||||||
|
[-2, -1],
|
||||||
|
],
|
||||||
|
N: [
|
||||||
|
[-1, -2],
|
||||||
|
[-1, -3],
|
||||||
|
],
|
||||||
|
NE: [
|
||||||
|
[0, -2],
|
||||||
|
[0, -3],
|
||||||
|
],
|
||||||
|
E: [
|
||||||
|
[-3, 0],
|
||||||
|
[-3, -1],
|
||||||
|
],
|
||||||
|
SE: [
|
||||||
|
[-5, -1],
|
||||||
|
[-5, -2],
|
||||||
|
],
|
||||||
|
S: [
|
||||||
|
[-6, -3],
|
||||||
|
[-7, -2],
|
||||||
|
],
|
||||||
|
SW: [
|
||||||
|
[-5, -3],
|
||||||
|
[-6, -1],
|
||||||
|
],
|
||||||
|
W: [
|
||||||
|
[-4, -2],
|
||||||
|
[-4, -3],
|
||||||
|
],
|
||||||
|
NW: [
|
||||||
|
[-1, 0],
|
||||||
|
[-1, -1],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
nekoEl.id = "oneko";
|
||||||
|
nekoEl.ariaHidden = true;
|
||||||
|
nekoEl.style.width = "32px";
|
||||||
|
nekoEl.style.height = "32px";
|
||||||
|
nekoEl.style.position = "fixed";
|
||||||
|
nekoEl.style.pointerEvents = "none"; // no petting :(
|
||||||
|
nekoEl.style.imageRendering = "pixelated";
|
||||||
|
nekoEl.style.left = `${nekoPosX - 16}px`;
|
||||||
|
nekoEl.style.top = `${nekoPosY - 16}px`;
|
||||||
|
nekoEl.style.zIndex = 999999;
|
||||||
|
|
||||||
|
let nekoFile = "/img/oneko.gif"
|
||||||
|
const curScript = document.currentScript
|
||||||
|
if (curScript && curScript.dataset.cat) {
|
||||||
|
nekoFile = curScript.dataset.cat
|
||||||
|
}
|
||||||
|
nekoEl.style.backgroundImage = `url(${nekoFile})`;
|
||||||
|
|
||||||
|
document.body.appendChild(nekoEl);
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", function(event) {
|
||||||
|
mousePosX = event.clientX;
|
||||||
|
mousePosY = event.clientY;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.requestAnimationFrame(onAnimationFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastFrameTimestamp;
|
||||||
|
|
||||||
|
function onAnimationFrame(timestamp) {
|
||||||
|
// Stops execution if the neko element is removed from DOM
|
||||||
|
if (!nekoEl.isConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!lastFrameTimestamp) {
|
||||||
|
lastFrameTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
if (timestamp - lastFrameTimestamp > 100) {
|
||||||
|
lastFrameTimestamp = timestamp
|
||||||
|
frame()
|
||||||
|
}
|
||||||
|
window.requestAnimationFrame(onAnimationFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSprite(name, frame) {
|
||||||
|
const sprite = spriteSets[name][frame % spriteSets[name].length];
|
||||||
|
nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetIdleAnimation() {
|
||||||
|
idleAnimation = null;
|
||||||
|
idleAnimationFrame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function idle() {
|
||||||
|
idleTime += 1;
|
||||||
|
|
||||||
|
// every ~ 20 seconds
|
||||||
|
if (
|
||||||
|
idleTime > 10 &&
|
||||||
|
Math.floor(Math.random() * 200) == 0 &&
|
||||||
|
idleAnimation == null
|
||||||
|
) {
|
||||||
|
let avalibleIdleAnimations = ["sleeping", "scratchSelf"];
|
||||||
|
let scratchDist = 64;
|
||||||
|
if (nekoPosX < scratchDist) {
|
||||||
|
avalibleIdleAnimations.push("scratchWallW");
|
||||||
|
}
|
||||||
|
if (nekoPosY < scratchDist) {
|
||||||
|
avalibleIdleAnimations.push("scratchWallN");
|
||||||
|
}
|
||||||
|
if (nekoPosX > window.innerWidth - scratchDist) {
|
||||||
|
avalibleIdleAnimations.push("scratchWallE");
|
||||||
|
}
|
||||||
|
if (nekoPosY > window.innerHeight - scratchDist) {
|
||||||
|
avalibleIdleAnimations.push("scratchWallS");
|
||||||
|
}
|
||||||
|
|
||||||
|
idleAnimation =
|
||||||
|
avalibleIdleAnimations[
|
||||||
|
Math.floor(Math.random() * avalibleIdleAnimations.length)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (idleAnimation) {
|
||||||
|
case "sleeping":
|
||||||
|
if (idleAnimationFrame < 8) {
|
||||||
|
setSprite("tired", 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setSprite("sleeping", Math.floor(idleAnimationFrame / 4));
|
||||||
|
if (idleAnimationFrame > 192) {
|
||||||
|
resetIdleAnimation();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "scratchWallN":
|
||||||
|
case "scratchWallS":
|
||||||
|
case "scratchWallE":
|
||||||
|
case "scratchWallW":
|
||||||
|
case "scratchSelf":
|
||||||
|
setSprite(idleAnimation, idleAnimationFrame);
|
||||||
|
if (idleAnimationFrame > 9) {
|
||||||
|
resetIdleAnimation();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setSprite("idle", 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
idleAnimationFrame += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function explodeHearts() {
|
||||||
|
const parent = nekoEl.parentElement;
|
||||||
|
const rect = nekoEl.getBoundingClientRect();
|
||||||
|
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
|
||||||
|
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||||
|
const centerX = rect.left + rect.width / 2 + scrollLeft;
|
||||||
|
const centerY = rect.top + rect.height / 2 + scrollTop;
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const heart = document.createElement('div');
|
||||||
|
heart.className = 'heart';
|
||||||
|
heart.textContent = 'â¤';
|
||||||
|
const offsetX = (Math.random() - 0.5) * 50;
|
||||||
|
const offsetY = (Math.random() - 0.5) * 50;
|
||||||
|
heart.style.left = `${centerX + offsetX - 16}px`;
|
||||||
|
heart.style.top = `${centerY + offsetY - 16}px`;
|
||||||
|
heart.style.transform = `translate(-50%, -50%) rotate(${Math.random() * 360}deg)`;
|
||||||
|
parent.appendChild(heart);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
parent.removeChild(heart);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.innerHTML = `
|
||||||
|
@keyframes heartBurst {
|
||||||
|
0% { transform: scale(0); opacity: 1; }
|
||||||
|
100% { transform: scale(1); opacity: 0; }
|
||||||
|
}
|
||||||
|
.heart {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 2em;
|
||||||
|
animation: heartBurst 1s ease-out;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
color: #ab9df2;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.head.appendChild(style);
|
||||||
|
nekoEl.addEventListener('click', explodeHearts);
|
||||||
|
|
||||||
|
let previousOnekoEnabled = onekoEnabled;
|
||||||
|
function frame() {
|
||||||
|
if(previousOnekoEnabled != onekoEnabled)
|
||||||
|
{
|
||||||
|
nekoEl.style.display = onekoEnabled ? "block" : "none";
|
||||||
|
|
||||||
|
if(onekoEnabled)
|
||||||
|
document.getElementById("toggle-cat").classList.remove("sleepy");
|
||||||
|
else
|
||||||
|
document.getElementById("toggle-cat").classList.add("sleepy");
|
||||||
|
|
||||||
|
previousOnekoEnabled = onekoEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!onekoEnabled || mousePosX == 0 || mousePosY == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
frameCount += 1;
|
||||||
|
const diffX = nekoPosX - mousePosX;
|
||||||
|
const diffY = nekoPosY - mousePosY;
|
||||||
|
const distance = Math.sqrt(diffX ** 2 + diffY ** 2);
|
||||||
|
|
||||||
|
if (distance < nekoSpeed || distance < 48) {
|
||||||
|
idle();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
idleAnimation = null;
|
||||||
|
idleAnimationFrame = 0;
|
||||||
|
|
||||||
|
if (idleTime > 1) {
|
||||||
|
setSprite("alert", 0);
|
||||||
|
// count down after being alerted before moving
|
||||||
|
idleTime = Math.min(idleTime, 7);
|
||||||
|
idleTime -= 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let direction;
|
||||||
|
direction = diffY / distance > 0.5 ? "N" : "";
|
||||||
|
direction += diffY / distance < -0.5 ? "S" : "";
|
||||||
|
direction += diffX / distance > 0.5 ? "W" : "";
|
||||||
|
direction += diffX / distance < -0.5 ? "E" : "";
|
||||||
|
setSprite(direction, frameCount);
|
||||||
|
|
||||||
|
nekoPosX -= (diffX / distance) * nekoSpeed;
|
||||||
|
nekoPosY -= (diffY / distance) * nekoSpeed;
|
||||||
|
|
||||||
|
nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16);
|
||||||
|
nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16);
|
||||||
|
|
||||||
|
nekoEl.style.left = `${nekoPosX - 16}px`;
|
||||||
|
nekoEl.style.top = `${nekoPosY - 16}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
})();
|