1
0

update three-vrm-3

This commit is contained in:
syui 2024-08-04 17:56:35 +09:00
parent 2fb62a68ac
commit f5fa7a9a0c
Signed by: syui
GPG Key ID: 5417CFEBAD92DF56
58 changed files with 12078 additions and 6 deletions

7
.gitignore vendored
View File

@ -1,6 +1,9 @@
node_modules
package-lock.json
*package-lock.json
example
yarn.lock
*yarn.lock
**DS_Store
dist/*.js
*/node_modules
*/public/models
*/dist/models

View File

@ -16,3 +16,20 @@ $ yarn dev
$ yarn build
```
## min-react
```
$ cd min-react
$ npm i
$ npm run start
```
## min-ts
minimum configuration
```sh
$ cd min-ts
$ yarn install
$ yarn dev
```

1
min-react/.env Normal file
View File

@ -0,0 +1 @@
GENERATE_SOURCEMAP=false

23
min-react/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

51
min-react/package.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "vrm2",
"version": "0.1.0",
"private": true,
"dependencies": {
"@pixiv/three-vrm": "^3.0.0",
"@pixiv/three-vrm-animation": "^3.0.0",
"@react-three/drei": "^9.109.2",
"@react-three/fiber": "^8.16.8",
"@react-three/postprocessing": "^2.16.2",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.104",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/three": "^0.167.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"three": "^0.167.1",
"three-stdlib": "^2.30.5",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href=".%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href=".%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href=".%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

20
min-react/readme.md Normal file
View File

@ -0,0 +1,20 @@
react-three-fiber + three-vrm
```sh
$ npm i
$ npm run start
```
## install
```sh
# npx create-react-app vrm1 --template typescript
# npm install three three-stdlib @types/three @react-three/fiber @react-three/fiber @react-three/drei @react-three/postprocessing
```
## three-vrm
`vrm 1.0`
unity export

38
min-react/src/App.css Normal file
View File

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

10
min-react/src/App.tsx Normal file
View File

@ -0,0 +1,10 @@
import React from 'react'
import VRMModelCanvas from './pages/vrm_model'
const App = () => {
return (
<VRMModelCanvas/>
)
}
export default App;

18
min-react/src/index.css Normal file
View File

@ -0,0 +1,18 @@
body {
height: 100%;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
html {
height: 100%;
}

19
min-react/src/index.tsx Normal file
View File

@ -0,0 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
min-react/src/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,84 @@
import * as THREE from 'three'
import React, { useState, useEffect, useRef } from 'react';
import { OrbitControls } from '@react-three/drei'
import { useFrame, Canvas } from '@react-three/fiber';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { VRM, VRMUtils, VRMLoaderPlugin } from '@pixiv/three-vrm';
import { VRMAnimationLoaderPlugin, VRMAnimation, createVRMAnimationClip } from "@pixiv/three-vrm-animation";
interface ModelProps {
url: string
url_anim: string
}
const VRMModel: React.FC<ModelProps> = ({ url, url_anim }) => {
const [vrm, setVrm] = useState<VRM | null>(null);
const mixerRef = useRef<THREE.AnimationMixer | null>(null);
useEffect(() => {
const loader = new GLTFLoader();
loader.register((parser) => new VRMLoaderPlugin(parser));
loader.register((parser) => new VRMAnimationLoaderPlugin(parser));
loader.load(url, (gltf) => {
const vrmModel = gltf.userData.vrm as VRM;
VRMUtils.removeUnnecessaryJoints(vrmModel.scene);
setVrm(vrmModel);
const mixer = new THREE.AnimationMixer(vrmModel.scene);
mixerRef.current = mixer;
loader.load(url_anim, (animGltf) => {
const vrmAnimations = animGltf.userData.vrmAnimations as VRMAnimation[];
if (vrmAnimations && vrmAnimations.length > 0) {
const clip = createVRMAnimationClip(vrmAnimations[0], vrmModel);
mixer.clipAction(clip).play();
}
});
});
}, [url, url_anim]);
useFrame((state, delta) => {
if (mixerRef.current) mixerRef.current.update(delta);
if (vrm) vrm.update(delta);
});
return vrm ? <primitive object={vrm.scene} /> : null;
};
export const VRMModelCanvas = () => {
return (
<div style={{ height: '100vh', width: '100vw' }}>
<Canvas
shadows
gl={{
//toneMapping: THREE.ACESFilmicToneMapping,
//toneMapping: THREE.ReinhardToneMapping,
toneMapping: THREE.NeutralToneMapping,
toneMappingExposure: 1.5,
alpha: true,
powerPreference: "high-performance",
antialias: true,
//stencil: false,
//depth: false
}}
camera={{ position: [1, 1, 1] }}>
<directionalLight
color="white"
castShadow
position={[0, 10, 0]}
intensity={1.5}
shadow-mapSize={[1024, 1024]}/>
<OrbitControls />
<ambientLight intensity={1} />
<pointLight position={[10, 10, 10]} />
<VRMModel url="./models/ai_vrm10.vrm" url_anim="./models/default.vrma" />
</Canvas>
</div>
)
}
export default VRMModelCanvas;

1
min-react/src/react-app-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

26
min-react/tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}

1
min-ts/dist/CNAME vendored Normal file
View File

@ -0,0 +1 @@
vrm.syui.ai

94
min-ts/dist/css/style.css vendored Normal file
View File

@ -0,0 +1,94 @@
body {
margin: 0;
overflow: hidden;
}
progress {
width: 100%;
height:8px;
position: absolute;
border-radius: 0px;
}
::-webkit-progress-bar {
border-radius: 0px;
background-color: #e6e6fa;
}
::-webkit-progress-value {
background-color: #4682b4;
}
::-moz-progress-bar {
border-radius: 0px;
background-color: #e6e6fa;
}
::-moz-progress-value {
background-color: #4682b4;
}
::-ms-progress-bar {
border-radius: 0px;
background-color: #e6e6fa;
}
::-ms-progress-value {
background-color: #4682b4;
}
div#menu {
padding: 20px;
border-bottom:solid 1px #ccc;
}
button {
border: none;
margin: 0;
padding: 0 20px 0 20px;
width: auto;
overflow: visible;
background: transparent;
color: inherit;
font: inherit;
line-height: normal;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
appearance: none;
-webkit-appearance: none;
cursor: pointer;
user-select: none;
}
a {
text-decoration: none;
color: #000;
}
a:hover{
/* color: #fff700; */
color: #847e00;
}
footer {
border-top:solid 1px #ccc;
padding: 20px 0 20px 0;
font-size:20px;
text-align: center;
}
span.menu-nav button#nav {
display: none;
}
@media screen and (max-width:1000px) {
span#nav-mobile {
display: none;
}
span.menu-nav button#nav {
right:0;
display: block;
position: absolute;
top:20px;
}
}

BIN
min-ts/dist/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
min-ts/dist/img/0.hdr vendored Normal file

Binary file not shown.

10744
min-ts/dist/img/1.hdr vendored Normal file

File diff suppressed because one or more lines are too long

BIN
min-ts/dist/img/2.hdr vendored Normal file

Binary file not shown.

BIN
min-ts/dist/img/og.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

21
min-ts/dist/index.html vendored Normal file
View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>ai</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta property="og:url" content="https://vrm.syui.ai">
<meta property="og:title" content="ai/vrm">
<meta property="og:description" content="ai[vrm] model viewer">
<meta property="og:image" content="/img/og.png">
<link rel="icon" href="/favicon.ico"/>
<link rel="stylesheet" href="/css/style.css"/>
<link rel="stylesheet" href="https://syui.ai/bower_components/icomoon/style.css" />
<link rel="stylesheet" href="https://syui.ai/bower_components/font-awesome/css/all.min.css" />
<script src="main.js"></script>
</head>
<body>
<progress value="0" max="100" id="progressBar"></progress>
<div id="canvas"></div>
<footer>© syui</footer>
</body>
</html>

120
min-ts/docs/wiki.md Normal file
View File

@ -0,0 +1,120 @@
## example three-vrm
- vrm : [download](https://hub.vroid.com/characters/675572020956181239/models/7175071267176594918)
- vrma : [download](https://vroid.booth.pm/items/5512385)
> ./dist/vrma
```js
load("/vrma/model.vrm");
load("/vrma/VRMA_01.vrma");
```
## iframe
```html
<div class="vrm-model">
<iframe src="https://vrm.syui.ai" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen scrolling="no"></iframe>
</div>
<style>
.vrm-model iframe {
margin:30px 0 30px 0;
width: 100%;
height: 640px;
}
</style>
```
## head
```js
let head = currentVrm.humanoid.getRawBoneNode("head");
```
## blink
```js
currentVrm.expressionManager.setValue('blink', 0);
```
## bloom
```js
//import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
//import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass';
//import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
// https://github.com/pmndrs/postprocessing
import { BloomEffect, EffectComposer, EffectPass, RenderPass } from "postprocessing";
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
composer.addPass(new EffectPass(camera, new BloomEffect()));
requestAnimationFrame(function render() {
requestAnimationFrame(render);
composer.render();
});
```
## progress
```js
// https://sbcode.net/threejs/progress-indicator
let manager = new THREE.LoadingManager();
let progressBar = document.getElementById('progressBar') as HTMLProgressElement
// https://threejs.org/docs/#api/en/loaders/managers/LoadingManager
manager.onStart = function ( url, itemsLoaded, itemsTotal ) {
//console.log( 'Started loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
progressBar.style.display = 'block'
let percentComplete = (itemsLoaded / itemsTotal) * 100
progressBar.value = percentComplete === Infinity ? 100 : percentComplete
};
manager.onLoad = function ( ) {
//console.log( 'Loading complete!');
progressBar.style.display = 'none'
};
manager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
let percentComplete = (itemsLoaded / itemsTotal) * 100
progressBar.value = percentComplete === Infinity ? 100 : percentComplete
//console.log( 'Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.' );
};
manager.onError = function ( url ) {
//console.log( 'There was an error loading ' + url );
progressBar.style.display = 'block'
};
```
```html
<progress value="0" max="100" id="progressBar"></progress>
```
```css
progress {
width: 100%;
height:8px;
position: absolute;
border-radius: 0px;
}
::-webkit-progress-bar {
border-radius: 0px;
background-color: #e6e6fa;
}
::-webkit-progress-value {
background-color: #4682b4;
}
```
## link
- https://vrm.dev/
- https://threejs.org/docs/
- https://github.com/pixiv/three-vrm
- https://github.com/vrm-c/vrm-specification/blob/master/specification/
- https://github.com/pmndrs/postprocessing

23
min-ts/package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "vrm",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --open"
},
"devDependencies": {
"ts-loader": "^9.5.1",
"typescript": "^5.4.2",
"webpack": "^5.90.3",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.3"
},
"dependencies": {
"@pixiv/three-vrm": "^3.0.0",
"@pixiv/three-vrm-animation": "^3.0.0",
"@pixiv/three-vrm-springbone": "^3.0.0",
"postprocessing": "^6.35.2",
"three": "^0.167.1"
}
}

123
min-ts/src/index.ts Normal file
View File

@ -0,0 +1,123 @@
import * as THREE from "three"
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { VRMLoaderPlugin } from "@pixiv/three-vrm";
import { createVRMAnimationClip, VRMAnimationLoaderPlugin } from "@pixiv/three-vrm-animation";
import { GridHelper, Mesh, MeshLambertMaterial, BoxGeometry, Vector3, Vector2, Color, DirectionalLight, Fog, HemisphereLight, HalfFloatType } from 'three';
import { BloomEffect, EffectComposer, EffectPass, RenderPass } from "postprocessing";
window.addEventListener("DOMContentLoaded", () => {
let manager = new THREE.LoadingManager();
const canvas = document.getElementById("canvas");
if (canvas == null) return;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 70, canvas.clientWidth/canvas.clientHeight, 0.1, 2000);
camera.position.set(0, 1.5, -1.5)
camera.rotation.set(0.0, Math.PI, 0.0)
camera.lookAt(new THREE.Vector3(0, 0, 0));
const renderer = new THREE.WebGLRenderer({
alpha: true,
powerPreference: "high-performance",
antialias: true,
stencil: false,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.toneMapping = THREE.NeutralToneMapping;
canvas.appendChild(renderer.domElement);
renderer.toneMappingExposure = 1.5;
renderer.setClearColor(0xffffff, 1.0);
const light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.position.set(1.0, 1.0, 1.0);
scene.add(light);
let currentVrm: any = undefined;
let currentVrmAnimation: any = undefined;
let currentMixer:any = undefined;
let percentComplete = null;
function load(url: string) {
loader.load(
url,
(gltf) => {
tryInitVRM(gltf);
tryInitVRMA(gltf);
},
(xhr) => {
percentComplete = (xhr.loaded / xhr.total) * 100
},
(error) => console.error(error)
);
}
function tryInitVRM(gltf: any) {
const vrm = gltf.userData.vrm;
if ( vrm == null ) {
return;
}
currentVrm = vrm;
scene.add(vrm.scene);
initAnimationClip();
}
function tryInitVRMA(gltf: any) {
const vrmAnimations = gltf.userData.vrmAnimations;
if (vrmAnimations == null) {
return;
}
currentVrmAnimation = vrmAnimations[0] ?? null;
initAnimationClip();
}
function initAnimationClip() {
if (currentVrm && currentVrmAnimation) {
currentMixer = new THREE.AnimationMixer(currentVrm.scene);
const clip = createVRMAnimationClip(currentVrmAnimation, currentVrm);
currentMixer.clipAction(clip).play();
}
}
const loader = new GLTFLoader(manager);
loader.register((parser) => {
return new VRMLoaderPlugin(parser);
});
loader.register((parser) => {
return new VRMAnimationLoaderPlugin(parser);
});
load("/models/default.vrm");
load("/models/default.vrma");
const clock = new THREE.Clock();
clock.start();
onResize();
window.addEventListener('resize', onResize);
function onResize() {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
animate();
function animate() {
const delta = clock.getDelta();
if (currentMixer) {
currentMixer.update(delta);
}
if (currentVrm) {
currentVrm.update(delta);
}
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
})

7
min-ts/tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"skipLibCheck": true
}
}

26
min-ts/webpack.config.js Normal file
View File

@ -0,0 +1,26 @@
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.ts',
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader'
}
]
},
resolve: {
extensions: ['.ts', '.js']
},
output: {
filename: 'main.js',
path: path.join(__dirname, "dist")
},
devServer: {
static: {
directory: path.join(__dirname, "dist"),
}
}
}

View File

@ -14,10 +14,10 @@
"webpack-dev-server": "^5.0.3"
},
"dependencies": {
"@pixiv/three-vrm": "^2.1.1",
"@pixiv/three-vrm-animation": "^2.1.1",
"@pixiv/three-vrm-springbone": "^2.1.1",
"@pixiv/three-vrm": "^3.0.0",
"@pixiv/three-vrm-animation": "^3.0.0",
"@pixiv/three-vrm-springbone": "^3.0.0",
"postprocessing": "^6.35.2",
"three": "^0.162.0"
"three": "^0.167.1"
}
}

1
planet/.env Normal file
View File

@ -0,0 +1 @@
GENERATE_SOURCEMAP=false

23
planet/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

51
planet/package.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "vrm2",
"version": "0.1.0",
"private": true,
"dependencies": {
"@pixiv/three-vrm": "^3.0.0",
"@pixiv/three-vrm-animation": "^3.0.0",
"@react-three/drei": "^9.109.2",
"@react-three/fiber": "^8.16.8",
"@react-three/postprocessing": "^2.16.2",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.104",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/three": "^0.167.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"three": "^0.167.1",
"three-stdlib": "^2.30.5",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

BIN
planet/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

43
planet/public/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href=".%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href=".%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href=".%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
planet/public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
planet/public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
planet/public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

20
planet/readme.md Normal file
View File

@ -0,0 +1,20 @@
react-three-fiber + three-vrm
```sh
$ npm i
$ npm run start
```
## install
```sh
# npx create-react-app vrm1 --template typescript
# npm install three three-stdlib @types/three @react-three/fiber @react-three/fiber @react-three/drei @react-three/postprocessing
```
## three-vrm
`vrm 1.0`
unity export

38
planet/src/App.css Normal file
View File

@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

9
planet/src/App.test.tsx Normal file
View File

@ -0,0 +1,9 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

10
planet/src/App.tsx Normal file
View File

@ -0,0 +1,10 @@
import React from 'react'
import VRMModelCanvas from './pages/vrm_model'
const App = () => {
return (
<VRMModelCanvas/>
)
}
export default App;

19
planet/src/index.css Normal file
View File

@ -0,0 +1,19 @@
body {
background-color: #000;
height: 100%;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
html {
height: 100%;
}

19
planet/src/index.tsx Normal file
View File

@ -0,0 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
planet/src/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,192 @@
import * as THREE from 'three'
import React, { useState, useEffect, useRef,useMemo } from 'react';
import { Points, OrbitControls, useGLTF } from '@react-three/drei'
import { useFrame, Canvas } from '@react-three/fiber';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { VRM, VRMUtils, VRMLoaderPlugin } from '@pixiv/three-vrm';
import { VRMAnimationLoaderPlugin, VRMAnimation, createVRMAnimationClip } from "@pixiv/three-vrm-animation";
import { EffectComposer, SelectiveBloom } from '@react-three/postprocessing'
import { SphereGeometry } from 'three';
import { GLTF } from 'three-stdlib'
interface ModelProps {
url: string
url_anim: string
}
const VRMModel: React.FC<ModelProps> = ({ url = "./models/ai.vrm", url_anim="./models/default.vrma" }) => {
const [vrm, setVrm] = useState<VRM | null>(null);
const mixerRef = useRef<THREE.AnimationMixer | null>(null);
useEffect(() => {
const loader = new GLTFLoader();
loader.register((parser) => new VRMLoaderPlugin(parser));
loader.register((parser) => new VRMAnimationLoaderPlugin(parser));
loader.load(url, (gltf) => {
const vrmModel = gltf.userData.vrm as VRM;
VRMUtils.removeUnnecessaryJoints(vrmModel.scene);
setVrm(vrmModel);
const mixer = new THREE.AnimationMixer(vrmModel.scene);
mixerRef.current = mixer;
loader.load(url_anim, (animGltf) => {
const vrmAnimations = animGltf.userData.vrmAnimations as VRMAnimation[];
if (vrmAnimations && vrmAnimations.length > 0) {
const clip = createVRMAnimationClip(vrmAnimations[0], vrmModel);
mixer.clipAction(clip).play();
}
});
});
}, [url, url_anim]);
useFrame((state, delta) => {
if (mixerRef.current) mixerRef.current.update(delta);
if (vrm) vrm.update(delta);
});
return vrm ? <primitive object={vrm.scene} /> : null;
};
type GLTFResult = GLTF & {
nodes: {
Object_2: THREE.Mesh
}
materials: {
['Scene_-_Root']: THREE.PointsMaterial
}
}
export function Galaxy(props: JSX.IntrinsicElements['group']) {
const ref = useRef<THREE.Group>(null!)
const galaxyCenterLightRef = useRef<THREE.PointLight>(null!)
const searchParams = new URLSearchParams(window.location.search);
var g = searchParams.get('g') ?? 'galaxy';
var model_path = "./models/" + g + ".glb"
var model_scale = 0.05;
const { nodes } = useGLTF(model_path) as GLTFResult
const [positions, colors] = useMemo(() => {
nodes.Object_2.geometry.center()
const positions = new Float32Array(
nodes.Object_2.geometry.attributes.position.array.buffer
)
const colors = new Float32Array(positions.length)
const getDistanceToCenter = (x: number, y: number, z: number) =>
Math.sqrt(x * x + y * y + z * z)
var model_color = 100;
if (g === 'sun'){
const sphereGeometry = new SphereGeometry(1, 332, 332);
nodes.Object_2.geometry = sphereGeometry;
model_scale = 1;
} else if (g === 'moon'){
const sphereGeometry = new SphereGeometry(1, 132, 132);
nodes.Object_2.geometry = sphereGeometry;
model_color = 1;
model_scale = 0.1;
} else if (g === 'earth'){
const sphereGeometry = new SphereGeometry(1, 232, 232);
nodes.Object_2.geometry = sphereGeometry;
model_color = 0.5;
model_scale = 0.3;
} else if (g === 'asteroid') {
const sphereGeometry = new SphereGeometry(1, 32, 32);
nodes.Object_2.geometry = sphereGeometry;
model_color = 1;
model_scale = 0.1;
} else if (g === 'neutron') {
model_color = 1;
}
// make colors closer to 0,0,0 be more reddish and colors further away be more blueish
const color = new THREE.Color()
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i]
const y = positions[i + 1]
const z = positions[i + 2]
const distanceToCenter = getDistanceToCenter(x, y, z)
const normalizedDistanceToCenter = distanceToCenter / model_color
// make colors closer to 0,0,0 be more reddish and colors further away be more blueish (do not use hsl)
color.setHSL(
(0.15 * (0.21 + Math.cos(distanceToCenter * 0.02))) / 2,
0.75,
0.6
)
color.setRGB(
Math.cos(normalizedDistanceToCenter),
THREE.MathUtils.randFloat(0, 0.8),
Math.sin(normalizedDistanceToCenter)
)
color.toArray(colors, i)
}
return [positions, colors]
}, [nodes])
useFrame(({ clock }) => {
// zoom in and out
ref.current.rotation.z = clock.getElapsedTime() / 2
ref.current.scale.setScalar(Math.sin(clock.getElapsedTime() / 10) + 1.2)
})
return (
<group {...props} dispose={null} ref={ref}>
<pointLight
position={[0, 0, 0]}
ref={galaxyCenterLightRef}
intensity={0.5}
/>
<Points scale={model_scale} positions={positions} colors={colors}>
<pointsMaterial
//map={starTexture}
transparent
depthWrite={false}
vertexColors
opacity={0.4}
depthTest
size={0.01}
/>
</Points>
<EffectComposer autoClear={false}>
<SelectiveBloom
intensity={5}
luminanceThreshold={0.01}
luminanceSmoothing={0.225}
lights={[galaxyCenterLightRef]}
/>
</EffectComposer>
<ambientLight intensity={1} />
</group>
)
}
export const VRMModelCanvas = () => {
return (
<div style={{ height: '100vh', width: '100vw' }}>
<Canvas
shadows
gl={{
toneMapping: THREE.NeutralToneMapping,
toneMappingExposure: 1.5,
alpha: true,
powerPreference: "high-performance",
antialias: true,
}}
camera={{ position: [1, 1, 1] }}>
<directionalLight
color="white"
castShadow
position={[0, 10, 0]}
intensity={1.5}
shadow-mapSize={[1024, 1024]}/>
<OrbitControls />
<ambientLight intensity={1} />
<pointLight position={[10, 10, 10]} />
<VRMModel url="./models/ai.vrm" url_anim="./models/default.vrma" />
<Galaxy position={[0, 9, 0]} />
</Canvas>
</div>
)
}
export default VRMModelCanvas;

1
planet/src/react-app-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

5
planet/src/setupTests.ts Normal file
View File

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

26
planet/tsconfig.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}