5 Commits

Author SHA1 Message Date
Morten Olsen
4606cbec9c more links 2021-08-27 22:06:10 +02:00
Morten Olsen
52b2572949 Improved animation 2021-08-27 21:51:54 +02:00
Morten Olsen
5f3017c7cf Improvements 2021-08-27 21:47:53 +02:00
Morten Olsen
84f4de421f fixes 2021-08-27 21:11:42 +02:00
Morten Olsen
337cc4e71b updates 2021-08-27 21:02:36 +02:00
9 changed files with 215 additions and 137 deletions

View File

@@ -37,7 +37,7 @@ jobs:
node-version: '12' node-version: '12'
- name: Build READM.md - name: Build READM.md
run: node readme/index.js > README.md run: node scripts/update-readme.js > README.md
- uses: EndBug/add-and-commit@v7 - uses: EndBug/add-and-commit@v7
with: with:

5
.gitignore vendored
View File

@@ -2,3 +2,8 @@
/*.log /*.log
/out /out
/.next /.next
/latex/**/*.aux
/latex/**/*.log
/latex/**/*.in
/latex/**/*.pdf
/latex/_markdown_*

View File

@@ -1,131 +1,103 @@
// https://redstapler.co/cool-nebula-background-effect-three-js/ // https://redstapler.co/cool-nebula-background-effect-three-js/
// https://redstapler.co/cool-nebula-background-effect-three-js/
import * as THREE from 'three'; import * as THREE from 'three';
import { Canvas, useFrame, useThree } from '@react-three/fiber'; import React, { useEffect } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
interface CloudProps { const setup = () => {
texture: THREE.Texture; let scene, camera, renderer;
position: [number, number, number]; let cloudParticles = [];
rotation: {
x: number;
y: number;
z: number;
};
}
const initialClouds = new Array(30).fill(undefined).map((_, i) => ({ function init() {
name: `id_${i}`, scene = new THREE.Scene();
position: [ camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,1,1000);
Math.random() * 800 - 400, camera.position.z = 1;
camera.rotation.x = 1.16;
camera.rotation.y = -0.12;
camera.rotation.z = 0.27;
let ambient = new THREE.AmbientLight(0x555555);
scene.add(ambient);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,window.innerHeight);
scene.fog = new THREE.FogExp2(0x03544e, 0.001);
renderer.setClearColor(scene.fog.color);
renderer.domElement.style.position = 'fixed';
renderer.domElement.style.top = '0';
renderer.domElement.style.left = '0';
renderer.domElement.style.width = '100%';
renderer.domElement.style.height = '100%';
renderer.domElement.style.zIndex = -1;
renderer.domElement.style.animationName = 'fadein';
renderer.domElement.style.animationDuration = '2s';
document.body.appendChild(renderer.domElement);
addParticles();
addLights();
render();
requestAnimationFrame(() => {
//renderer.domElement.style.opacity = 1;
})
}
const addParticles = () => {
let loader = new THREE.TextureLoader();
loader.load("/images/smoke.png", (texture) => {
const cloudGeo = new THREE.PlaneBufferGeometry(500,500);
const cloudMaterial = new THREE.MeshLambertMaterial({
map:texture,
transparent: true
});
for(let p=0; p<50; p++) {
let cloud = new THREE.Mesh(cloudGeo, cloudMaterial);
cloud.position.set(
Math.random()*800 -400,
500, 500,
Math.random() * 500 - 500, Math.random()*500-500
] as [number, number, number],
rotation: {
x: 1.16,
y: -0.12,
z: Math.random() * 2 * Math.PI,
},
}));
const Cloud: React.FC<CloudProps> = ({ texture, position, rotation }) => {
return (
<mesh position={position} rotation={[ rotation.x, rotation.y, rotation.z ]}>
<planeBufferGeometry args={[500, 500]} />
<meshLambertMaterial opacity={.5} map={texture} transparent={true} />
</mesh>
); );
cloud.rotation.x = 1.16;
cloud.rotation.y = -0.12;
cloud.rotation.z = Math.random()*2*Math.PI;
cloud.material.opacity = 0.55;
cloudParticles.push(cloud);
scene.add(cloud);
}
});
}
const addLights = () => {
let directionalLight = new THREE.DirectionalLight(0xff8c19);
directionalLight.position.set(0,0,1);
scene.add(directionalLight);
let orangeLight = new THREE.PointLight(0xcc6600,50,450,1.7);
orangeLight.position.set(200,300,100);
scene.add(orangeLight);
let redLight = new THREE.PointLight(0xd8547e,50,450,1.7);
redLight.position.set(100,300,100);
scene.add(redLight);
let blueLight = new THREE.PointLight(0x3677ac,50,450,1.7);
blueLight.position.set(300,300,200);
scene.add(blueLight);
};
function render() {
cloudParticles.forEach(p => {
p.rotation.z -=0.001;
});
renderer.render(scene,camera);
requestAnimationFrame(render);
}
init();
}; };
const Clouds: React.FC<{}> = () => {
const { camera } = useThree();
const loader = useMemo(() => new THREE.TextureLoader(), []);
const [texture, setTexture] = useState<THREE.Texture | undefined>();
const [clouds, setClouds] = useState(initialClouds);
useEffect(() => {
console.log(camera);
camera.rotateX(1.16);
camera.rotateY(-0.12);
camera.rotateZ(0.27);
}, []);
useEffect(() => {
loader.load('images/smoke.png', (loadedTexture) => {
setTexture(loadedTexture);
});
}, []);
useFrame(() => {
setClouds(current => current.map(c => ({
...c,
rotation: {
...c.rotation,
z: c.rotation.z -= 0.001,
}
})));
});
if (!texture) {
return <></>;
}
return (
<>
{clouds.map((cloud) => (
<Cloud
key={cloud.name}
texture={texture}
position={cloud.position}
rotation={cloud.rotation}
/>
))}
</>
)
}
const style = {
width: '100%',
height: '100%',
top: 0,
left: 0,
position: 'fixed' as any,
zIndex: -1,
}
const Background: React.FC<{}> = () => { const Background: React.FC<{}> = () => {
const fog = useMemo(() => new THREE.FogExp2(0x03544e, 0.001), []);
const [aspect, setAspect] = useState(global.window ? window.innerWidth / window.innerHeight : 1);
if (!global.window) {
return <Canvas style={style}><></></Canvas>
}
useEffect(() => { useEffect(() => {
const update = () => { setup();
setAspect(window.innerWidth / window.innerHeight);
};
window.addEventListener('resize', update);
return () => {
window.removeEventListener('resize', update);
}
}, []); }, []);
return ( return <></>
<Canvas style={style}>
<color attach="background" args={[0x03544e]} />
<scene fog={fog}>
<perspectiveCamera
args={[60, aspect, 1, 1000]}
aspect={aspect}
position={[0, 0, 0]}
/>
<ambientLight args={[0x555555]} />
<directionalLight args={[0xff8c19]} position={[0,0,1]} />
<pointLight args={[0xcc6600, 50, 450, 1.7]} position={[200,300,100]} />
<pointLight args={[0xd8547e, 50, 450, 1.7]} position={[100,300,100]} />
<pointLight args={[0x3677ac, 50, 450, 1.7]} position={[300,300,200]} />
<Clouds />
</scene>
</Canvas>
);
}; };
export default Background; export default Background;

View File

@@ -1,6 +1,5 @@
import React from 'react'; import React, { useRef, useState, useEffect } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import Image from 'next/image'
import image from '../public/images/me.jpg'; import image from '../public/images/me.jpg';
const Wrapper = styled.div` const Wrapper = styled.div`
@@ -11,14 +10,18 @@ const Wrapper = styled.div`
padding: 80px; padding: 80px;
`; `;
const ImageWrapper = styled.div` const ImageWrapper = styled.div<{loaded: boolean}>`
border-radius: 50%; border-radius: 50%;
border: solid 30px rgba(255, 255, 255, .5); border: solid 10px rgba(255, 255, 255, .5);
box-shadow: 0 0 35px rgba(255, 255, 255, .5); box-shadow: 0 0 35px rgba(0, 0, 0, .5);
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
max-width: 360px; max-width: 300px;
position: relative; position: relative;
transition: all .6s;
transform: rotateY(180deg);
opacity: ${({ loaded }) => loaded ? '1' : '0'};
transform: ${({ loaded }) => loaded ? 'rotateY(180deg)' : 'rotateY(90deg) translateX(-300px)'};
`; `;
const Spacer = styled.div` const Spacer = styled.div`
@@ -35,6 +38,12 @@ const Title = styled.h1`
0 0 5px rgba(255, 255, 255, .5); 0 0 5px rgba(255, 255, 255, .5);
0 0 10px rgba(0, 0, 0, .5); 0 0 10px rgba(0, 0, 0, .5);
margin-bottom: 0px; margin-bottom: 0px;
letter-spacing: 1px;
letter-spacing: 7px;
&::first-letter {
font-size: 36px;
}
`; `;
const SubTitle = styled.h2` const SubTitle = styled.h2`
@@ -57,16 +66,40 @@ const Divider = styled.div`
box-shadow: 0 0 30px rgba(255, 255, 255, .7); box-shadow: 0 0 30px rgba(255, 255, 255, .7);
`; `;
const Me: React.FC<{}> = () => ( const Image = styled.img<{blurDataURL: string}>`
background: url("${({ blurDataURL }) => blurDataURL}");
position: absolute;
background-size: cover;
top: 0;
left: 0;
width: 100%;
height: 100%;
`
const Me: React.FC<{}> = () => {
const imgRef = useRef<HTMLImageElement>();
const [loaded, setLoaded] = useState(false);
useEffect(() => {
if (imgRef.current) {
setLoaded(imgRef.current.complete);
}
}, [imgRef]);
return (
<Wrapper> <Wrapper>
<ImageWrapper> <ImageWrapper loaded={loaded}>
<Image layout="fill" {...image} /> <Image
ref={imgRef}
src={image.src}
blurDataURL={image.blurDataURL}
onLoad={() => setLoaded(true)}
/>
<Spacer /> <Spacer />
</ImageWrapper> </ImageWrapper>
<Title>Morten Olsen</Title> <Title>Morten Olsen</Title>
<SubTitle>...One part genius, one part crazy</SubTitle> <SubTitle>...One part genius, one part crazy</SubTitle>
<Divider /> <Divider />
</Wrapper> </Wrapper>
); );
};
export default Me; export default Me;

View File

@@ -2,7 +2,7 @@
"type": "info", "type": "info",
"data": { "data": {
"name": "Morten Olsen", "name": "Morten Olsen",
"image": "assets/me.jpg", "image": "public/images/me.jpg",
"info": [{ "info": [{
"name": "E-mail", "name": "E-mail",
"value": "hello@buy-me.coffee" "value": "hello@buy-me.coffee"

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { createGlobalStyle } from 'styled-components';
import Head from 'next/head'; import Head from 'next/head';
import Background from '../components/Background'; import Background from '../components/Background';
import Me from '../components/Me'; import Me from '../components/Me';
@@ -7,6 +8,19 @@ import htbLogo from '../public/images/logos/htb.svg';
import githubLogo from '../public/images/logos/github.svg'; import githubLogo from '../public/images/logos/github.svg';
import linkedinLogo from '../public/images/logos/linkedin.svg'; import linkedinLogo from '../public/images/logos/linkedin.svg';
import stackOverflowLogo from '../public/images/logos/stackoverflow.svg'; import stackOverflowLogo from '../public/images/logos/stackoverflow.svg';
import codinGameLogo from '../public/images/logos/codingame.svg';
import resumeLogo from '../public/images/logos/resume.svg';
const Globals = createGlobalStyle`
body {
background: #03544e;
}
@keyframes fadein {
from {opacity: 0}
to {opacity: 1}
}
`;
const Frontpage: React.FC<{}> = () => { const Frontpage: React.FC<{}> = () => {
return ( return (
@@ -17,6 +31,7 @@ const Frontpage: React.FC<{}> = () => {
<link rel="preconnect" href="https://fonts.gstatic.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300&display=swap" rel="stylesheet" />
</Head> </Head>
<Globals />
<Background /> <Background />
<Me /> <Me />
<Social <Social
@@ -32,6 +47,14 @@ const Frontpage: React.FC<{}> = () => {
title: 'Stack Overflow', title: 'Stack Overflow',
link: 'https://stackoverflow.com/users/1689055/morten-olsen', link: 'https://stackoverflow.com/users/1689055/morten-olsen',
logo: stackOverflowLogo, logo: stackOverflowLogo,
}, {
title: 'Codingame',
link: 'https://www.codingame.com/profile/8b34b1812baa75715c972bfe190c0aed4552622',
logo: codinGameLogo,
}, {
title: 'Resumè',
link: 'https://github.com/morten-olsen/morten-olsen.github.io/releases/',
logo: resumeLogo,
}, { }, {
title: 'Linkedin', title: 'Linkedin',
link: 'https://www.linkedin.com/in/mortenolsendk', link: 'https://www.linkedin.com/in/mortenolsendk',

View File

@@ -0,0 +1 @@
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 2083 2083" xmlns="http://www.w3.org/2000/svg" width="2500" height="2500"><path d="M0 0h2083v2083H0z" fill="transparent"/><g fill="#1f2528"><path d="M0 1636c411.554-97.771 692.897-283.177 724-637 10.714-131.212 67.364-243.777 216-319 207.675-80.617 305.728-52.164 390-10 92.668 81.722 119.468 199.94 50 371.5-31.914 100.266-291.001 223.867-410 258.5-392.539 175.893-595.504 430.282-561 783H0z"/><ellipse cx="1666.5" cy="408" rx="196.5" ry="192"/></g></svg>

After

Width:  |  Height:  |  Size: 560 B

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 370.123 370.123" style="enable-background:new 0 0 370.123 370.123;" xml:space="preserve">
<path d="M321.321,18.74H310.78c0-10.352-8.39-18.74-18.74-18.74H78.083c-10.352,0-18.74,8.389-18.74,18.74H48.802
c-10.351,0-18.74,8.389-18.74,18.74c0,10.351,8.39,18.74,18.74,18.74h12.279l22.132,296.557c0.73,9.781,8.879,17.346,18.688,17.346
h166.322c9.809,0,17.957-7.564,18.686-17.346l22.133-296.557h12.279c10.351,0,18.74-8.39,18.74-18.74
C340.061,27.129,331.672,18.74,321.321,18.74z M119.297,332.643l-7.47-100.061h34.125c8.242,17.324,22.939,28.11,39.109,28.11
c16.17,0,30.867-10.786,39.109-28.11h34.125l-7.471,100.061H119.297z M263.007,169.469h-38.836
c-8.242-17.324-22.939-28.114-39.109-28.114c-16.17,0-30.867,10.79-39.109,28.114h-38.836L98.665,56.221h172.793L263.007,169.469z"
/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -3,8 +3,9 @@
FORMAT=$1; shift FORMAT=$1; shift
docker run \ docker run \
--workdir "/var/texlive/latex" \
--user "$UID:$GID" \ --user "$UID:$GID" \
--net=none \ --net=none \
-v $(pwd):/var/texlive \ -v $(pwd):/var/texlive \
blang/latex:ubuntu blang/latex:ubuntu \
lualatex "latex/$FORMAT.tex" lualatex "$FORMAT.tex"