Compare commits
1 Commits
2f83727b28
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fceabe90b9 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
|||||||
*.glb filter=lfs diff=lfs merge=lfs -text
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
/dist
|
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
BIN
Miata_wheel.glb
(Stored with Git LFS)
BIN
Miata_wheel.glb
(Stored with Git LFS)
Binary file not shown.
36
README.md
Normal file
36
README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||||
16
eslint.config.mjs
Normal file
16
eslint.config.mjs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
});
|
||||||
|
|
||||||
|
const eslintConfig = [
|
||||||
|
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export default eslintConfig;
|
||||||
29
index.html
29
index.html
@@ -1,29 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta name="description" content="Kay Hennig's portfolio" />
|
|
||||||
<meta name="author" content="Kay Hennig" />
|
|
||||||
<meta name="keywords" content="portfolio, web development, design" />
|
|
||||||
<title>Kay Hennig | Portfolio</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
#container {
|
|
||||||
width: 100dvw;
|
|
||||||
height: 100dvh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="container"></div>
|
|
||||||
<script type="module" src="/main.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
477
main.js
477
main.js
@@ -1,477 +0,0 @@
|
|||||||
import * as THREE from 'three';
|
|
||||||
import Stats from 'three/addons/libs/stats.module.js';
|
|
||||||
import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js';
|
|
||||||
import { RapierHelper } from 'three/addons/helpers/RapierHelper.js';
|
|
||||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
||||||
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
|
|
||||||
import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
|
|
||||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
||||||
import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js';
|
|
||||||
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
|
||||||
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
|
|
||||||
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
|
||||||
|
|
||||||
class ThirdPersionControls extends THREE.Controls {
|
|
||||||
constructor(object, domElement) {
|
|
||||||
super(object, domElement);
|
|
||||||
this.offset = new THREE.Vector3(0, 1, -5);
|
|
||||||
this.targetPosition = new THREE.Vector3(0, 0, 0);
|
|
||||||
this.horizontalSpringConstant = 0.5;
|
|
||||||
this.horizontalDampingConstant = 0.3;
|
|
||||||
this.velocity = new THREE.Vector3(0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
update( deltaTime = null ) {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let renderer, camera, scene, stats, container, sun, controls, composer, clock = new THREE.Clock();
|
|
||||||
let car, chassis, wheels, headLightLeft, headLightRight, brakeLightLeft, brakeLightRight;
|
|
||||||
let physics, physicsHelper, vehicleController, movement;
|
|
||||||
let focus = true;
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
stats = new Stats();
|
|
||||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
||||||
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 100);
|
|
||||||
scene = new THREE.Scene();
|
|
||||||
|
|
||||||
|
|
||||||
container.appendChild(stats.dom);
|
|
||||||
container.appendChild(renderer.domElement);
|
|
||||||
|
|
||||||
// setup
|
|
||||||
renderer.setPixelRatio(window.devicePixelRatio);
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
renderer.shadowMap.enabled = true;
|
|
||||||
|
|
||||||
scene.background = new THREE.Color(0xbfe3dd);
|
|
||||||
|
|
||||||
camera.position.set(-5, 3, -5);
|
|
||||||
camera.lookAt(0, 0.5, 0);
|
|
||||||
camera.far = 100;
|
|
||||||
|
|
||||||
controls = new ThirdPersionControls(camera, renderer.domElement);
|
|
||||||
controls.update();
|
|
||||||
|
|
||||||
composer = new EffectComposer( renderer );
|
|
||||||
|
|
||||||
const renderPass = new RenderPass( scene, camera );
|
|
||||||
composer.addPass( renderPass );
|
|
||||||
|
|
||||||
const width = window.innerWidth;
|
|
||||||
const height = window.innerHeight;
|
|
||||||
const ssaoPass = new SSAOPass( scene, camera, width, height );
|
|
||||||
ssaoPass.output = SSAOPass.OUTPUT.Default;
|
|
||||||
ssaoPass.kernelRadius = 64;
|
|
||||||
composer.addPass( ssaoPass );
|
|
||||||
|
|
||||||
const outputPass = new OutputPass();
|
|
||||||
composer.addPass( outputPass );
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupLights() {
|
|
||||||
sun = new THREE.DirectionalLight(0xffffff, 1.5);
|
|
||||||
sun.position.set(10, 10, 3);
|
|
||||||
sun.castShadow = true;
|
|
||||||
sun.shadow.mapSize.width = sun.shadow.mapSize.height = 1024 * 2;
|
|
||||||
sun.shadow.camera.near = 10;
|
|
||||||
sun.shadow.camera.far = 50;
|
|
||||||
sun.shadow.camera.left = -2;
|
|
||||||
sun.shadow.camera.right = 2;
|
|
||||||
sun.shadow.camera.top = 2;
|
|
||||||
sun.shadow.camera.bottom = -2;
|
|
||||||
sun.shadow.bias = -0.001;
|
|
||||||
sun.shadow.radius = 2;
|
|
||||||
scene.add(sun);
|
|
||||||
var ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
|
|
||||||
scene.add(ambientLight);
|
|
||||||
|
|
||||||
//const cameraHelper = new THREE.CameraHelper(sun.shadow.camera);
|
|
||||||
//scene.add(cameraHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupWorld() {
|
|
||||||
var groundPlane = new THREE.Mesh(
|
|
||||||
new THREE.BoxGeometry(100, 0.5, 100),
|
|
||||||
new THREE.MeshStandardMaterial({ color: 0x808080 })
|
|
||||||
);
|
|
||||||
|
|
||||||
groundPlane.receiveShadow = true;
|
|
||||||
groundPlane.castShadow = false;
|
|
||||||
groundPlane.userData = { physics: { mass: 0 } }; // static object
|
|
||||||
groundPlane.position.set(0, -0.25, 0);
|
|
||||||
scene.add(groundPlane);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addWheel(index, pos, carMesh, model) {
|
|
||||||
const wheelRadius = 0.2;
|
|
||||||
const wheelWidth = 0.16;
|
|
||||||
const suspensionRestLength = 0.04;
|
|
||||||
const wheelPosition = pos; // Position relative to chassis
|
|
||||||
const wheelDirection = { x: 0.0, y: - 1.0, z: 0.0 }; // Downward direction
|
|
||||||
const wheelAxle = { x: 1, y: 0.0, z: 0.0 }; // Axle direction
|
|
||||||
|
|
||||||
// Add the wheel to the vehicle controller
|
|
||||||
vehicleController.addWheel(
|
|
||||||
wheelPosition,
|
|
||||||
wheelDirection,
|
|
||||||
wheelAxle,
|
|
||||||
suspensionRestLength,
|
|
||||||
wheelRadius
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set suspension stiffness for wheel
|
|
||||||
vehicleController.setWheelSuspensionStiffness(index, 45.0);
|
|
||||||
vehicleController.setWheelSuspensionCompression(index, 0.8);
|
|
||||||
vehicleController.setWheelSuspensionRelaxation(index, 0.6);
|
|
||||||
|
|
||||||
// Set wheel friction
|
|
||||||
vehicleController.setWheelFrictionSlip(index, 100.0);
|
|
||||||
vehicleController.setWheelSideFrictionStiffness(index, 2.0);
|
|
||||||
|
|
||||||
// Enable steering for the wheel
|
|
||||||
vehicleController.setWheelSteering(index, pos.z > 0);
|
|
||||||
|
|
||||||
// Create a wheel mesh
|
|
||||||
const geometry = new THREE.CylinderGeometry(wheelRadius, wheelRadius, wheelWidth, 16);
|
|
||||||
//geometry.rotateZ(Math.PI * 0.5);
|
|
||||||
const material = new THREE.MeshStandardMaterial({ visible: false });
|
|
||||||
const wheel = new THREE.Mesh(geometry, material);
|
|
||||||
|
|
||||||
var m = model.clone();
|
|
||||||
if (pos.x < 0) {
|
|
||||||
m.rotateZ(Math.PI);
|
|
||||||
}
|
|
||||||
wheel.add(m);
|
|
||||||
|
|
||||||
wheel.castShadow = false;
|
|
||||||
|
|
||||||
wheel.position.copy(pos);
|
|
||||||
|
|
||||||
wheels.push(wheel);
|
|
||||||
carMesh.add(wheel);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCar() {
|
|
||||||
const loader = new GLTFLoader();
|
|
||||||
|
|
||||||
const geometry = new THREE.BoxGeometry(1, 0.45, 2.5);
|
|
||||||
const material = new THREE.MeshStandardMaterial({ visible: false })
|
|
||||||
const mesh = new THREE.Mesh(geometry, material);
|
|
||||||
|
|
||||||
//
|
|
||||||
scene.add(mesh);
|
|
||||||
car = mesh;
|
|
||||||
|
|
||||||
mesh.position.y = 1;
|
|
||||||
|
|
||||||
physics.addMesh(mesh, 1100, 0.2); // addMesh places the RigidBody in the mesh.userData.physics object
|
|
||||||
chassis = mesh.userData.physics.body;
|
|
||||||
//chassis.linearDamping = 0.2;
|
|
||||||
//chassis.angularDamping = 0.3;
|
|
||||||
|
|
||||||
vehicleController = physics.world.createVehicleController(chassis);
|
|
||||||
vehicleController.setIndexForwardAxis = 2;
|
|
||||||
vehicleController.indexUpAxis = 0;
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
wheels = [];
|
|
||||||
|
|
||||||
loader.load('Miata_wheel.glb', (gltf) => {
|
|
||||||
gltf.scene.children[0].traverse((child) => {
|
|
||||||
if (child.isMesh) {
|
|
||||||
child.castShadow = true;
|
|
||||||
child.receiveShadow = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
gltf.scene.children[0].position.set(0, 0, 0);
|
|
||||||
const model = gltf.scene.children[0];
|
|
||||||
addWheel(0, { x: - 0.49, y: -0.2, z: - 0.75 }, mesh, model);
|
|
||||||
addWheel(1, { x: 0.49, y: -0.2, z: - 0.75 }, mesh, model);
|
|
||||||
addWheel(2, { x: - 0.49, y: -0.2, z: 0.8 }, mesh, model);
|
|
||||||
addWheel(3, { x: 0.49, y: -0.2, z: 0.8 }, mesh, model);
|
|
||||||
//vehicleController.setWheelSteering( 2, Math.PI / 4 );
|
|
||||||
//vehicleController.setWheelSteering( 3, Math.PI / 4 );
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
loader.load('Miata.glb', (gltf) => {
|
|
||||||
const m = gltf.scene;
|
|
||||||
m.traverse((child) => {
|
|
||||||
if (child.isMesh) {
|
|
||||||
child.castShadow = true;
|
|
||||||
child.receiveShadow = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
m.position.set(0, -0.35, 0);
|
|
||||||
m.scale.set(1.0, 1.0, 1.0);
|
|
||||||
|
|
||||||
mesh.add(m);
|
|
||||||
});
|
|
||||||
|
|
||||||
headLightLeft = new THREE.SpotLight(0xffffff, 3.0, 20, Math.PI / 4, 0.2);
|
|
||||||
headLightLeft.position.set(-0.35, 0.3, 1.1);
|
|
||||||
headLightLeft.castShadow = true;
|
|
||||||
headLightLeft.shadow.mapSize.width = 1024;
|
|
||||||
headLightLeft.shadow.mapSize.height = 1024;
|
|
||||||
headLightLeft.shadow.camera.near = 0.5;
|
|
||||||
headLightLeft.shadow.camera.far = 10;
|
|
||||||
headLightLeft.shadow.camera.fov = 30;
|
|
||||||
headLightLeft.shadow.bias = -0.001;
|
|
||||||
headLightLeft.shadow.radius = 3;
|
|
||||||
|
|
||||||
var headLightLeftTarget = new THREE.Object3D();
|
|
||||||
headLightLeftTarget.position.set(-0.35, 0.25, 2);
|
|
||||||
headLightLeft.target = headLightLeftTarget;
|
|
||||||
mesh.add(headLightLeftTarget);
|
|
||||||
mesh.add(headLightLeft);
|
|
||||||
|
|
||||||
headLightRight = new THREE.SpotLight(0xffffff, 3.0, 20, Math.PI / 4, 0.2);
|
|
||||||
headLightRight.position.set(0.35, 0.3, 1.1);
|
|
||||||
headLightRight.castShadow = true;
|
|
||||||
headLightRight.shadow.mapSize.width = 1024;
|
|
||||||
headLightRight.shadow.mapSize.height = 1024;
|
|
||||||
headLightRight.shadow.camera.near = 0.5;
|
|
||||||
headLightRight.shadow.camera.far = 10;
|
|
||||||
headLightRight.shadow.camera.fov = 30;
|
|
||||||
headLightRight.shadow.bias = -0.001;
|
|
||||||
headLightRight.shadow.radius = 3;
|
|
||||||
|
|
||||||
var headLightRightTarget = new THREE.Object3D();
|
|
||||||
headLightRightTarget.position.set(0.35, 0.25, 2);
|
|
||||||
headLightRight.target = headLightRightTarget;
|
|
||||||
mesh.add(headLightRightTarget);
|
|
||||||
mesh.add(headLightRight);
|
|
||||||
|
|
||||||
brakeLightLeft = new THREE.SpotLight(0xff0000, 2.5, 5, Math.PI / 4, 0.2);
|
|
||||||
brakeLightLeft.position.set(-0.4, 0.2, -1.3);
|
|
||||||
brakeLightLeft.castShadow = true;
|
|
||||||
brakeLightLeft.shadow.mapSize.width = 1024;
|
|
||||||
brakeLightLeft.shadow.mapSize.height = 1024;
|
|
||||||
brakeLightLeft.shadow.camera.near = 0.5;
|
|
||||||
brakeLightLeft.shadow.camera.far = 10;
|
|
||||||
brakeLightLeft.shadow.camera.fov = 30;
|
|
||||||
brakeLightLeft.shadow.bias = -0.001;
|
|
||||||
brakeLightLeft.shadow.radius = 5;
|
|
||||||
|
|
||||||
var brakeLightLeftTarget = new THREE.Object3D();
|
|
||||||
brakeLightLeftTarget.position.set(-0.4, 0.2, -3);
|
|
||||||
brakeLightLeft.target = brakeLightLeftTarget;
|
|
||||||
mesh.add(brakeLightLeftTarget);
|
|
||||||
mesh.add(brakeLightLeft);
|
|
||||||
|
|
||||||
brakeLightRight = new THREE.SpotLight(0xff0000, 2.5, 5, Math.PI / 4, 0.2);
|
|
||||||
brakeLightRight.position.set(0.4, 0.2, -1.3);
|
|
||||||
brakeLightRight.castShadow = true;
|
|
||||||
brakeLightRight.shadow.mapSize.width = 1024;
|
|
||||||
brakeLightRight.shadow.mapSize.height = 1024;
|
|
||||||
brakeLightRight.shadow.camera.near = 0.5;
|
|
||||||
brakeLightRight.shadow.camera.far = 10;
|
|
||||||
brakeLightRight.shadow.camera.fov = 30;
|
|
||||||
brakeLightRight.shadow.bias = -0.001;
|
|
||||||
brakeLightRight.shadow.radius = 5;
|
|
||||||
|
|
||||||
var brakeLightRightTarget = new THREE.Object3D();
|
|
||||||
brakeLightRightTarget.position.set(0.4, 0.2, -3);
|
|
||||||
brakeLightRight.target = brakeLightRightTarget;
|
|
||||||
mesh.add(brakeLightRightTarget);
|
|
||||||
mesh.add(brakeLightRight);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initPhysics() {
|
|
||||||
physics = await RapierPhysics();
|
|
||||||
physicsHelper = new RapierHelper(physics.world);
|
|
||||||
scene.add(physicsHelper);
|
|
||||||
physics.addScene(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateWheels() {
|
|
||||||
if (vehicleController === undefined) return;
|
|
||||||
|
|
||||||
const wheelSteeringQuat = new THREE.Quaternion();
|
|
||||||
const wheelRotationQuat = new THREE.Quaternion();
|
|
||||||
const up = new THREE.Vector3(0, 1, 0);
|
|
||||||
|
|
||||||
//const chassisPosition = chassis.translation();
|
|
||||||
|
|
||||||
wheels.forEach((wheel, index) => {
|
|
||||||
|
|
||||||
const wheelAxleCs = vehicleController.wheelAxleCs(index);
|
|
||||||
const connection = vehicleController.wheelChassisConnectionPointCs(index).y || 0;
|
|
||||||
const suspension = vehicleController.wheelSuspensionLength(index) || 0;
|
|
||||||
const steering = vehicleController.wheelSteering(index) || 0;
|
|
||||||
const rotationRad = vehicleController.wheelRotation(index) || 0;
|
|
||||||
|
|
||||||
wheel.position.y = connection - suspension;
|
|
||||||
|
|
||||||
wheelSteeringQuat.setFromAxisAngle(up, steering);
|
|
||||||
wheelRotationQuat.setFromAxisAngle(wheelAxleCs, rotationRad);
|
|
||||||
|
|
||||||
wheel.quaternion.set(0, 0, 0, 1);
|
|
||||||
wheel.quaternion.multiply(wheelSteeringQuat);
|
|
||||||
wheel.quaternion.multiply(wheelRotationQuat);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCarControl() {
|
|
||||||
let accelerateForce = 0;
|
|
||||||
if (chassis.isSleeping()) chassis.wakeUp();
|
|
||||||
if (movement.forward < 0) {
|
|
||||||
|
|
||||||
//if (movement.accelerateForce.value === 0) chassis.wakeUp();
|
|
||||||
//accelerateForce = movement.accelerateForce.value - movement.accelerateForce.step;
|
|
||||||
//if (accelerateForce < movement.accelerateForce.min) accelerateForce = movement.accelerateForce.min;
|
|
||||||
accelerateForce = movement.accelerateForce.max;
|
|
||||||
|
|
||||||
} else if (movement.forward > 0) {
|
|
||||||
|
|
||||||
//if (movement.accelerateForce.value === 0) chassis.wakeUp();
|
|
||||||
//accelerateForce = movement.accelerateForce.value + movement.accelerateForce.step;
|
|
||||||
|
|
||||||
//if (accelerateForce > movement.accelerateForce.max) accelerateForce = movement.accelerateForce.max;
|
|
||||||
accelerateForce = movement.accelerateForce.min;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
movement.accelerateForce.value = accelerateForce;
|
|
||||||
|
|
||||||
//console.log(accelerateForce);
|
|
||||||
|
|
||||||
let brakeForce = 0;
|
|
||||||
|
|
||||||
if (movement.brake > 0) {
|
|
||||||
brakeLightLeft.intensity = 2.5;
|
|
||||||
brakeLightRight.intensity = 2.5;
|
|
||||||
brakeForce = movement.brakeForce.value + movement.brakeForce.step;
|
|
||||||
if (brakeForce > movement.brakeForce.max) brakeForce = movement.brakeForce.max;
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
brakeLightLeft.intensity = 0;
|
|
||||||
brakeLightRight.intensity = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
movement.brakeForce.value = brakeForce;
|
|
||||||
|
|
||||||
const engineForce = accelerateForce;
|
|
||||||
|
|
||||||
vehicleController.setWheelEngineForce(0, -engineForce);
|
|
||||||
vehicleController.setWheelEngineForce(1, -engineForce);
|
|
||||||
|
|
||||||
const currentSteering = vehicleController.wheelSteering(2);
|
|
||||||
const steerDirection = movement.right;
|
|
||||||
const steerAngle = Math.PI / 4;
|
|
||||||
|
|
||||||
const steering = THREE.MathUtils.lerp(currentSteering, steerAngle * steerDirection, 0.02);
|
|
||||||
|
|
||||||
vehicleController.setWheelSteering(2, steering);
|
|
||||||
vehicleController.setWheelSteering(3, steering);
|
|
||||||
|
|
||||||
vehicleController
|
|
||||||
|
|
||||||
const wheelBrake = movement.brake * brakeForce;
|
|
||||||
vehicleController.setWheelBrake(0, wheelBrake);
|
|
||||||
vehicleController.setWheelBrake(1, wheelBrake);
|
|
||||||
vehicleController.setWheelBrake(2, wheelBrake);
|
|
||||||
vehicleController.setWheelBrake(3, wheelBrake);
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
container = document.getElementById('container');
|
|
||||||
|
|
||||||
movement = {
|
|
||||||
forward: 0,
|
|
||||||
right: 0,
|
|
||||||
brake: 0,
|
|
||||||
accelerateForce: { value: 0, min: - 300, max: 600, step: 50 },
|
|
||||||
brakeForce: { value: 0, min: 0, max: 20, step: 1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('keydown', (event) => {
|
|
||||||
if (event.key === 'w' || event.key === 'ArrowUp') movement.forward = - 1;
|
|
||||||
if (event.key === 's' || event.key === 'ArrowDown') movement.forward = 1;
|
|
||||||
if (event.key === 'a' || event.key === 'ArrowLeft') movement.right = 1;
|
|
||||||
if (event.key === 'd' || event.key === 'ArrowRight') movement.right = - 1;
|
|
||||||
if (event.key === ' ') movement.brake = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('keyup', (event) => {
|
|
||||||
if ((event.key === 'w' || event.key === 'ArrowUp' ) && movement.forward == -1) movement.forward = 0;
|
|
||||||
if ((event.key === 's' || event.key === 'ArrowDown') && movement.forward == 1) movement.forward = 0;
|
|
||||||
if ((event.key === 'a' || event.key === 'ArrowLeft') && movement.right == 1) movement.right = 0;
|
|
||||||
if ((event.key === 'd' || event.key === 'ArrowRight') && movement.right == -1) movement.right = 0;
|
|
||||||
if (event.key === ' ') movement.brake = 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onresize = function () {
|
|
||||||
camera.aspect = window.innerWidth / window.innerHeight;
|
|
||||||
camera.updateProjectionMatrix();
|
|
||||||
|
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
composer.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('blur', () => {
|
|
||||||
focus = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('focus', () => {
|
|
||||||
focus = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
||||||
setupWorld();
|
|
||||||
|
|
||||||
setupLights();
|
|
||||||
|
|
||||||
await initPhysics();
|
|
||||||
|
|
||||||
createCar();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function loop() {
|
|
||||||
if (document.hidden) {
|
|
||||||
focus = false;
|
|
||||||
}
|
|
||||||
// if window is not focused // not visible, pause the loop
|
|
||||||
if (!focus) {
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let delta = clock.getDelta();
|
|
||||||
delta = Math.min(delta, 1 / 60); // cap delta to avoid large jumps
|
|
||||||
// rotate the car
|
|
||||||
controls.targetPosition.copy(car.position);
|
|
||||||
controls.update(delta);
|
|
||||||
|
|
||||||
// set spotlight target to forward of the car
|
|
||||||
|
|
||||||
sun.target = car;
|
|
||||||
sun.position.copy(car.position).add(new THREE.Vector3(10, 10, 3));
|
|
||||||
|
|
||||||
updateCarControl();
|
|
||||||
vehicleController.updateVehicle(delta);
|
|
||||||
updateWheels();
|
|
||||||
if (physicsHelper) physicsHelper.update();
|
|
||||||
//renderer.render(scene, camera);
|
|
||||||
stats.update();
|
|
||||||
composer.render(renderer, scene, camera);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.setAnimationLoop(loop);
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
|
||||||
8
next.config.ts
Normal file
8
next.config.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
/* config options here */
|
||||||
|
output: "standalone",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
6575
package-lock.json
generated
6575
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@@ -1,25 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "homepage",
|
"name": "homepage",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"main": "index.js",
|
"private": true,
|
||||||
"type": "module",
|
"engines": {
|
||||||
"scripts": {
|
"node": ">=20.0.0"
|
||||||
"dev": "vite",
|
|
||||||
"build": "vite build",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"host": "vite --host"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"scripts": {
|
||||||
"type": "git",
|
"dev": "next dev --turbopack",
|
||||||
"url": "https://git.kayhennig.de/TheFusion21/Homepage.git"
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"three": "^0.178.0"
|
"@heroicons/react": "^2.2.0",
|
||||||
|
"framer-motion": "^12.23.6",
|
||||||
|
"next": "15.4.2",
|
||||||
|
"react": "19.1.0",
|
||||||
|
"react-dom": "19.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vite": "^7.0.5"
|
"@eslint/eslintrc": "^3",
|
||||||
},
|
"@tailwindcss/postcss": "^4",
|
||||||
"author": "Kay Hennig <KayHennig98@gmail.com>",
|
"@types/node": "^20",
|
||||||
"license": "ISC",
|
"@types/react": "^19",
|
||||||
"description": ""
|
"@types/react-dom": "^19",
|
||||||
|
"eslint": "^9",
|
||||||
|
"eslint-config-next": "15.4.2",
|
||||||
|
"tailwindcss": "^4",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
postcss.config.mjs
Normal file
5
postcss.config.mjs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const config = {
|
||||||
|
plugins: ["@tailwindcss/postcss"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
14
src/app/globals.css
Normal file
14
src/app/globals.css
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
body {
|
||||||
|
@apply bg-gray-900 text-gray-200 antialiased;
|
||||||
|
}
|
||||||
|
html, body {
|
||||||
|
@apply h-full overflow-hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/app/layout.tsx
Normal file
21
src/app/layout.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import "./globals.css";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Kay Hennig | Portfolio",
|
||||||
|
description: "Showcasing the work of Kay Hennig",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
95
src/app/page.tsx
Normal file
95
src/app/page.tsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
import { ChevronDoubleDownIcon } from '@heroicons/react/24/solid';
|
||||||
|
import Slide from '@/components/Slide';
|
||||||
|
import Slide1 from '@/components/Slide1';
|
||||||
|
import Slide2 from '@/components/Slide2';
|
||||||
|
import Slide3 from '@/components/Slide3';
|
||||||
|
import Slide4 from '@/components/Slide4';
|
||||||
|
import Slide5 from '@/components/Slide5';
|
||||||
|
|
||||||
|
|
||||||
|
const slides = [
|
||||||
|
<Slide1 key={1} />,
|
||||||
|
<Slide2 key={2} />,
|
||||||
|
//<Slide3 key={3} />,
|
||||||
|
//<Slide4 key={4} />,
|
||||||
|
//<Slide5 key={5} />,
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [current, setCurrent] = useState(0);
|
||||||
|
const [touchStart, setTouchStart] = useState({ x: undefined, y: undefined } as { x: number | undefined, y: number | undefined });
|
||||||
|
const [touchEnd, setTouchEnd] = useState({ x: undefined, y: undefined } as { x: number | undefined, y: number | undefined });
|
||||||
|
|
||||||
|
const nextSlide = useCallback(() => {
|
||||||
|
if (current < slides.length - 1) setCurrent(current + 1);
|
||||||
|
}, [current])
|
||||||
|
|
||||||
|
const prevSlide = useCallback(() => {
|
||||||
|
if (current > 0) setCurrent(current - 1);
|
||||||
|
}, [current])
|
||||||
|
|
||||||
|
const handleTouchStart = useCallback((e: TouchEvent) => {
|
||||||
|
const firstTouch = e.touches[0];
|
||||||
|
setTouchStart({ x: firstTouch.clientX, y: firstTouch.clientY });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleTouchEnd = useCallback((e: TouchEvent) => {
|
||||||
|
if (!touchStart.x || !touchStart.y || !touchEnd.x || !touchEnd.y) return;
|
||||||
|
|
||||||
|
const xDiff = touchEnd.x - touchStart.x;
|
||||||
|
const yDiff = touchEnd.y - touchStart.y;
|
||||||
|
|
||||||
|
if (Math.abs(xDiff) < Math.abs(yDiff)) {
|
||||||
|
if (yDiff > 0) prevSlide(); // Swipe down
|
||||||
|
else nextSlide(); // Swipe up
|
||||||
|
}
|
||||||
|
|
||||||
|
setTouchStart({ x: undefined, y: undefined });
|
||||||
|
setTouchEnd({ x: undefined, y: undefined });
|
||||||
|
}, [touchStart, touchEnd, prevSlide, nextSlide]);
|
||||||
|
|
||||||
|
const handleTouchMove = useCallback((e: TouchEvent) => {
|
||||||
|
setTouchEnd({ x: e.touches[0].clientX, y: e.touches[0].clientY });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = (e: WheelEvent) => {
|
||||||
|
if (e.deltaY > 0) nextSlide();
|
||||||
|
else prevSlide();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKey = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'ArrowDown') nextSlide();
|
||||||
|
if (e.key === 'ArrowUp') prevSlide();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('wheel', handleScroll);
|
||||||
|
window.addEventListener('keydown', handleKey);
|
||||||
|
window.addEventListener('touchstart', handleTouchStart);
|
||||||
|
window.addEventListener('touchend', handleTouchEnd);
|
||||||
|
window.addEventListener('touchmove', handleTouchMove);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('wheel', handleScroll);;
|
||||||
|
window.removeEventListener('keydown', handleKey)
|
||||||
|
}
|
||||||
|
}, [current, nextSlide, prevSlide, handleTouchStart, handleTouchEnd, handleTouchMove])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="relative h-screen w-screen overflow-hidden">
|
||||||
|
{slides.map((slide, idx) => (
|
||||||
|
<Slide key={idx} index={idx} currentSlide={current}>
|
||||||
|
{slide}
|
||||||
|
</Slide>
|
||||||
|
))}
|
||||||
|
<div className={`absolute bottom-4 left-1/2 transform -translate-y-8 -translate-x-1/2 text-white ${current === slides.length - 1 ? 'hidden' : ''}`}>
|
||||||
|
<ChevronDoubleDownIcon className="w-12 h-12 animate-bounce cursor-pointer" onClick={nextSlide} />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
src/components/Slide.tsx
Normal file
25
src/components/Slide.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
type SlideProps = {
|
||||||
|
children: ReactNode
|
||||||
|
index: number
|
||||||
|
currentSlide: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Slide({ children, index, currentSlide }: SlideProps) {
|
||||||
|
const isActive = index === currentSlide
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className={`absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center text-center gap-2 text-3xl`}
|
||||||
|
initial={{ opacity: 0 }} // Start off-screen
|
||||||
|
// x to left-100% for previous slides, 0 for current, and right-100% for next slides
|
||||||
|
animate={{ y: isActive ? 0 : index < currentSlide ? '-100%' : '100%', opacity: isActive ? 1 : 0 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
)
|
||||||
|
}
|
||||||
106
src/components/Slide1.tsx
Normal file
106
src/components/Slide1.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { motion, MotionValue, useTime, useTransform } from 'framer-motion';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
const titles = [
|
||||||
|
'Full Stack Developer',
|
||||||
|
'DevOps Engineer',
|
||||||
|
'Game Developer',
|
||||||
|
'Open Source Contributor',
|
||||||
|
'Tech Enthusiast',
|
||||||
|
'Gamer',
|
||||||
|
];
|
||||||
|
|
||||||
|
function Title({ title, progress, range, ys, os }: { title: string, progress: MotionValue<number>, range: number[], ys: string[], os: number[] }) {
|
||||||
|
const y = useTransform(progress, range, ys);
|
||||||
|
const opacity = useTransform(progress, range, os);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="absolute w-full text-center text-4xl font-bold"
|
||||||
|
style={{
|
||||||
|
y,
|
||||||
|
opacity
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Slide1() {
|
||||||
|
const time = useTime();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const genRanges = useMemo(() => {
|
||||||
|
const ranges = [];
|
||||||
|
for (let i = 0; i < titles.length+1; i++) {
|
||||||
|
const start = i * 1500 + 100 * i;
|
||||||
|
const end = start + 1500;
|
||||||
|
ranges.push(start, end);
|
||||||
|
}
|
||||||
|
return ranges;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const genYs = useMemo(() => {
|
||||||
|
const ys = [];
|
||||||
|
for (let i = 0; i < titles.length; i++) {
|
||||||
|
ys.push([] as string[]);
|
||||||
|
for (let j = 0; j < titles.length+1; j++) {
|
||||||
|
if ((j % titles.length) === i) {
|
||||||
|
ys[i].push('0%', '0%'); // Current title
|
||||||
|
} else if (j < i) {
|
||||||
|
ys[i].push('100%', '100%'); // Previous titles
|
||||||
|
} else if (j == i + 1) {
|
||||||
|
ys[i].push('-100%', '100%'); // Next title
|
||||||
|
} else {
|
||||||
|
ys[i].push('100%', '100%'); // Titles after next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ys;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const genOs = useMemo(() => {
|
||||||
|
const os = [];
|
||||||
|
for (let i = 0; i < titles.length; i++) {
|
||||||
|
os.push([] as number[]);
|
||||||
|
for (let j = 0; j < titles.length+1; j++) {
|
||||||
|
if ((j % titles.length) === i) {
|
||||||
|
os[i].push(1, 1); // Current title
|
||||||
|
} else if (j < i) {
|
||||||
|
os[i].push(0, 0); // Previous titles
|
||||||
|
} else if (j == i + 1) {
|
||||||
|
os[i].push(0, 0); // Next title
|
||||||
|
} else {
|
||||||
|
os[i].push(0, 0); // Titles after next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const progress = useTransform(time, (t) => ((t * 1) % genRanges[genRanges.length - 2]));
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>I'm Kay</p>
|
||||||
|
<div className="relative flex flex-col h-10 w-full overflow-visible">
|
||||||
|
{titles.map((title, i) => (
|
||||||
|
<Title
|
||||||
|
key={i}
|
||||||
|
title={title}
|
||||||
|
progress={progress}
|
||||||
|
range={genRanges}
|
||||||
|
ys={genYs[i]}
|
||||||
|
os={genOs[i]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
35
src/components/Slide2.tsx
Normal file
35
src/components/Slide2.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
export default function Slide2() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col lg:flex-row items-center lg:items-start justify-center w-full gap-8">
|
||||||
|
<div className="lg:text-right">
|
||||||
|
<h1 className="text-4xl font-bold">Languages</h1>
|
||||||
|
<ul className="mt-4 text-xl">
|
||||||
|
<li>C++</li>
|
||||||
|
<li>C#</li>
|
||||||
|
<li>TypeScript</li>
|
||||||
|
<li>JavaScript</li>
|
||||||
|
<li>SQL</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="lg:text-center">
|
||||||
|
<h1 className="text-4xl font-bold">Frameworks</h1>
|
||||||
|
<ul className="mt-4 text-xl">
|
||||||
|
<li>react/next.js</li>
|
||||||
|
<li>ASP.NET</li>
|
||||||
|
<li>Entity Framework Core</li>
|
||||||
|
<li>Razor</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="lg:text-left">
|
||||||
|
<h1 className="text-4xl font-bold">Tools</h1>
|
||||||
|
<ul className="mt-4 text-xl">
|
||||||
|
<li>Docker</li>
|
||||||
|
<li>Git/Gitea/Github</li>
|
||||||
|
<li>Nginx</li>
|
||||||
|
<li>MySQL/MS SQL</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
8
src/components/Slide3.tsx
Normal file
8
src/components/Slide3.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default function Slide3() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
8
src/components/Slide4.tsx
Normal file
8
src/components/Slide4.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default function Slide4() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
8
src/components/Slide5.tsx
Normal file
8
src/components/Slide5.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export default function Slide5() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
41
tsconfig.json
Normal file
41
tsconfig.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
"next-env.d.ts",
|
||||||
|
"build/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user