├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── assets │ ├── icons │ │ └── github.svg │ └── textures │ │ └── lense.png ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── components │ ├── App.tsx │ ├── LinkIconButton.tsx │ └── three │ │ ├── Background.tsx │ │ ├── Lense.tsx │ │ ├── TCanvas.tsx │ │ ├── TextPlane.tsx │ │ └── drawer.ts ├── index.css ├── index.tsx ├── modules │ └── glsl │ │ ├── noise.ts │ │ └── shader.ts ├── react-app-env.d.ts └── reportWebVitals.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | This application reproduces the effect of [Monopo London](https://monopo.london/). 3 | 4 | https://nemutas.github.io/r3f-monopo-london/ 5 | 6 | ![output(video-cutter-js com) (4)](https://user-images.githubusercontent.com/46724121/163683407-7a22bcb5-88e8-4ef8-b3d7-282284d5c8aa.gif) 7 | 8 | # Technology 9 | 10 | - TypeScript 11 | - React(Create React App) 12 | - React Three Fiber(Three.js) 13 | - Canvas Texture 14 | - Shader 15 | 16 | # License 17 | 18 | This source code is not MIT License. 19 | 20 | ❌ Commercial use is prohibited.
21 | ❌ Redistribution is prohibited.
22 | ❌ Diversion is prohibited.(Incorporate all of the code into the project, etc.)
23 | ✅ You can look at the application and reproduce the representation.
24 | ✅ You can use parts of the code. 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "r3f-monopo-london", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://nemutas.github.io/r3f-monopo-london/", 6 | "dependencies": { 7 | "@emotion/css": "^11.9.0", 8 | "@react-three/drei": "^8.20.2", 9 | "@react-three/fiber": "^7.0.27", 10 | "@testing-library/jest-dom": "^5.16.4", 11 | "@testing-library/react": "^12.1.4", 12 | "@testing-library/user-event": "^13.5.0", 13 | "@types/jest": "^27.4.1", 14 | "@types/node": "^16.11.26", 15 | "@types/react": "^17.0.44", 16 | "@types/react-dom": "^17.0.14", 17 | "gsap": "^3.10.3", 18 | "lil-gui": "^0.16.1", 19 | "react": "^18.0.0", 20 | "react-dom": "^18.0.0", 21 | "react-scripts": "5.0.0", 22 | "three": "^0.139.2", 23 | "typescript": "^4.6.3", 24 | "valtio": "^1.5.2", 25 | "web-vitals": "^2.1.4" 26 | }, 27 | "scripts": { 28 | "start": "react-scripts start", 29 | "build": "react-scripts build", 30 | "deploy": "npm run build && gh-pages -d build" 31 | }, 32 | "eslintConfig": { 33 | "extends": [ 34 | "react-app", 35 | "react-app/jest" 36 | ] 37 | }, 38 | "browserslist": { 39 | "production": [ 40 | ">0.2%", 41 | "not dead", 42 | "not op_mini all" 43 | ], 44 | "development": [ 45 | "last 1 chrome version", 46 | "last 1 firefox version", 47 | "last 1 safari version" 48 | ] 49 | }, 50 | "devDependencies": { 51 | "@types/three": "^0.139.0", 52 | "gh-pages": "^3.2.3" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/textures/lense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nemutas/r3f-monopo-london/b6c34d114531654c4ef806e0c9e3b251293de88e/public/assets/textures/lense.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nemutas/r3f-monopo-london/b6c34d114531654c4ef806e0c9e3b251293de88e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nemutas/r3f-monopo-london/b6c34d114531654c4ef806e0c9e3b251293de88e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nemutas/r3f-monopo-london/b6c34d114531654c4ef806e0c9e3b251293de88e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { VFC } from 'react'; 2 | import { css } from '@emotion/css'; 3 | import { LinkIconButton } from './LinkIconButton'; 4 | import { TCanvas } from './three/TCanvas'; 5 | 6 | export const App: VFC = () => { 7 | return ( 8 |
9 | 10 | 11 |
12 | ) 13 | } 14 | 15 | const styles = { 16 | container: css` 17 | position: relative; 18 | width: 100vw; 19 | height: 100vh; 20 | ` 21 | } 22 | -------------------------------------------------------------------------------- /src/components/LinkIconButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, VFC } from 'react'; 2 | 3 | type LinkIconButtonProps = { 4 | /** 5 | * Resource path directly under the public folder. 6 | * @example '/assets/icons/github.svg' 7 | */ 8 | imagePath: string 9 | /** 10 | * @example 'https://github.com' 11 | */ 12 | linkPath: string 13 | /** 14 | * @default 'bottom-right' 15 | */ 16 | position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' 17 | /** 18 | * @default [50, 50] - width:50px, height:50px 19 | */ 20 | size?: [number, number] 21 | } 22 | 23 | export const LinkIconButton: VFC = props => { 24 | const { imagePath, linkPath, position = 'bottom-right', size = [50, 50] } = props 25 | const [hover, setHover] = useState(false) 26 | 27 | const publicImagePath = process.env.PUBLIC_URL + imagePath 28 | 29 | let positionStyle 30 | switch (position) { 31 | case 'top-left': 32 | positionStyle = styles.topLeft 33 | break 34 | case 'top-right': 35 | positionStyle = styles.topRight 36 | break 37 | case 'bottom-left': 38 | positionStyle = styles.bottomLeft 39 | break 40 | default: 41 | positionStyle = styles.bottomRight 42 | } 43 | 44 | return ( 45 | setHover(true)} 51 | onMouseLeave={() => setHover(false)}> 52 | 53 | 54 | ) 55 | } 56 | 57 | // ======================================================== 58 | // styles 59 | 60 | type Styles = { [key in string]: React.CSSProperties } 61 | 62 | const temp: Styles = { 63 | container: { 64 | position: 'fixed', 65 | bottom: '0', 66 | right: '0', 67 | fontSize: '0' 68 | } 69 | } 70 | 71 | const styles: Styles = { 72 | topLeft: { 73 | ...temp.container, 74 | top: '10px', 75 | left: '10px' 76 | }, 77 | topRight: { 78 | ...temp.container, 79 | top: '10px', 80 | right: '10px' 81 | }, 82 | bottomLeft: { 83 | ...temp.container, 84 | bottom: '10px', 85 | left: '10px' 86 | }, 87 | bottomRight: { 88 | ...temp.container, 89 | bottom: '10px', 90 | right: '10px' 91 | }, 92 | img: { 93 | objectFit: 'cover', 94 | opacity: '0.5', 95 | transform: 'rotate(0deg)', 96 | transition: 'all 0.3s' 97 | } 98 | } 99 | 100 | const hoverStyles: Styles = { 101 | img: { 102 | ...styles.img, 103 | opacity: '1', 104 | transform: 'rotate(360deg)' 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/components/three/Background.tsx: -------------------------------------------------------------------------------- 1 | import { VFC } from 'react'; 2 | import * as THREE from 'three'; 3 | import { Plane } from '@react-three/drei'; 4 | import { useFrame } from '@react-three/fiber'; 5 | import { cnoise21 } from '../../modules/glsl/noise'; 6 | 7 | export const Background: VFC = () => { 8 | const shader: THREE.Shader = { 9 | uniforms: { 10 | u_time: { value: 0 }, 11 | u_mouse: { value: new THREE.Vector2() } 12 | }, 13 | vertexShader, 14 | fragmentShader 15 | } 16 | 17 | const target = new THREE.Vector2() 18 | useFrame(({ mouse }) => { 19 | shader.uniforms.u_time.value += 0.005 20 | target.set((mouse.x + 1) * 0.5, (mouse.y + 1) * 0.5) 21 | shader.uniforms.u_mouse.value.lerp(target, 0.2) 22 | }) 23 | 24 | return ( 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | const vertexShader = ` 32 | varying vec2 v_uv; 33 | 34 | void main() { 35 | v_uv = uv; 36 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 37 | } 38 | ` 39 | 40 | const fragmentShader = ` 41 | uniform float u_time; 42 | uniform vec2 u_mouse; 43 | varying vec2 v_uv; 44 | 45 | ${cnoise21} 46 | 47 | float random(vec2 p) { 48 | vec2 k1 = vec2( 49 | 23.14069263277926, // e^pi (Gelfond's constant) 50 | 2.665144142690225 // 2^sqrt(2) (Gelfond–Schneider constant) 51 | ); 52 | return fract( 53 | cos(dot(p, k1)) * 12345.6789 54 | ); 55 | } 56 | 57 | const vec3 black = vec3(0.0); 58 | const vec3 color1 = vec3(0.89, 0.34, 0.11); 59 | const vec3 color2 = vec3(0.56, 0.64, 0.64); 60 | const vec3 color3 = vec3(0.16, 0.26, 0.47); 61 | 62 | void main() { 63 | vec2 seed = v_uv * 1.5 * (u_mouse + 0.3 * (length(u_mouse) + 0.5)); 64 | float n = cnoise21(seed) + length(u_mouse) * 0.9; 65 | 66 | float ml = pow(length(u_mouse), 2.5) * 0.15; 67 | float n1 = smoothstep(0.0, 0.0 + 0.2, n); 68 | vec3 color = mix(black, color1, n1); 69 | 70 | float n2 = smoothstep(0.1 + ml, 0.1 + ml + 0.2, n); 71 | color = mix(color, color2, n2); 72 | 73 | float n3 = smoothstep(0.2 + ml, 0.2 + ml + 0.2, n); 74 | color = mix(color, color3, n3); 75 | 76 | float n4 = smoothstep(0.3 + ml, 0.3 + ml + 0.2, n); 77 | color = mix(color, black, n4); 78 | 79 | vec2 uvrandom = v_uv; 80 | uvrandom.y *= random(vec2(uvrandom.y, 0.4)); 81 | color.rgb += random(uvrandom) * 0.05; 82 | 83 | gl_FragColor = vec4(color, 1.0); 84 | } 85 | ` 86 | -------------------------------------------------------------------------------- /src/components/three/Lense.tsx: -------------------------------------------------------------------------------- 1 | import { useRef, VFC } from 'react'; 2 | import * as THREE from 'three'; 3 | import { Circle, useTexture } from '@react-three/drei'; 4 | import { useFrame, useThree } from '@react-three/fiber'; 5 | 6 | export const Lense: VFC = () => { 7 | const ref = useRef(null) 8 | const texture = useTexture(process.env.PUBLIC_URL + '/assets/textures/lense.png') 9 | const { aspect } = useThree(({ viewport }) => viewport) 10 | 11 | const target = new THREE.Vector3() 12 | useFrame(({ mouse }) => { 13 | target.set(mouse.x, mouse.y, 0.01) 14 | ref.current!.position.lerp(target, 0.1) 15 | }) 16 | 17 | return ( 18 | 19 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/components/three/TCanvas.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense, VFC } from 'react'; 2 | import * as THREE from 'three'; 3 | import { Canvas } from '@react-three/fiber'; 4 | import { 5 | enFragmentShader, enVertexShader, jpFragmentShader, jpVertexShader 6 | } from '../../modules/glsl/shader'; 7 | import { Background } from './Background'; 8 | import { Lense } from './Lense'; 9 | import { TextPlane } from './TextPlane'; 10 | 11 | export const TCanvas: VFC = () => { 12 | const OrthographicCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, -10, 10) 13 | 14 | return ( 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | {/* helper */} 27 | {/* */} 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/components/three/TextPlane.tsx: -------------------------------------------------------------------------------- 1 | import { VFC } from 'react'; 2 | import * as THREE from 'three'; 3 | import { Plane } from '@react-three/drei'; 4 | import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; 5 | import { Drawer } from './drawer'; 6 | 7 | type TextPlaneProps = { 8 | text: [string, string] 9 | vertexShader: string 10 | fragmentShader: string 11 | } 12 | 13 | export const TextPlane: VFC = props => { 14 | const { text, vertexShader, fragmentShader } = props 15 | 16 | const drawer = new Drawer(text[0], text[1]) 17 | drawer.draw() 18 | 19 | const { aspect } = useThree(({ viewport }) => viewport) 20 | 21 | const shader: THREE.Shader = { 22 | uniforms: { 23 | u_texture: { value: drawer.texture }, 24 | u_mouse: { value: new THREE.Vector2() }, 25 | u_aspect: { value: drawer.aspect }, 26 | u_enable: { value: false } 27 | }, 28 | vertexShader, 29 | fragmentShader 30 | } 31 | 32 | const target = new THREE.Vector2() 33 | useFrame(() => { 34 | shader.uniforms.u_mouse.value.lerp(target, 0.1) 35 | }) 36 | 37 | const handlePointerMove = (e: ThreeEvent) => { 38 | target.copy(e.uv!) 39 | } 40 | 41 | const handlePointerEnter = (e: ThreeEvent) => { 42 | shader.uniforms.u_mouse.value.copy(e.uv!) 43 | shader.uniforms.u_enable.value = true 44 | } 45 | 46 | const handlePointerLeave = () => { 47 | shader.uniforms.u_enable.value = false 48 | } 49 | 50 | return ( 51 | 57 | 58 | 59 | ) 60 | } 61 | -------------------------------------------------------------------------------- /src/components/three/drawer.ts: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | 3 | export class Drawer { 4 | public texture 5 | public aspect 6 | 7 | private _ctx 8 | private readonly _margin = 130 9 | 10 | constructor(private _text1: string, private _text2: string) { 11 | const canvas = document.createElement('canvas') 12 | canvas.width = 1024 13 | canvas.height = canvas.width / 2.2 14 | this._ctx = canvas.getContext('2d')! 15 | this.aspect = canvas.width / canvas.height 16 | this.texture = new THREE.CanvasTexture(canvas) 17 | } 18 | 19 | draw = () => { 20 | const ctx = this._ctx 21 | const { width, height } = this._ctx.canvas 22 | 23 | ctx.clearRect(0, 0, width, height) 24 | 25 | const fontSize = 85 26 | 27 | ctx.textAlign = 'left' 28 | ctx.textBaseline = 'hanging' 29 | 30 | ctx.font = `bold ${fontSize}px 'Poppins'` 31 | ctx.fillStyle = '#fff' 32 | 33 | const text2Metrics = ctx.measureText(this._text2) 34 | 35 | ctx.fillText(this._text1, this._margin, this._margin) 36 | ctx.fillText(this._text2, width - text2Metrics.width - this._margin, height - (fontSize + this._margin)) 37 | 38 | // ctx.lineWidth = 3 39 | // ctx.strokeStyle = '#f00' 40 | // ctx.strokeRect(0, 0, width, height) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300&display=swap'); 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: 'Poppins', sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | font-size: 62.5%; 10 | } 11 | 12 | code { 13 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; 14 | } 15 | 16 | * { 17 | box-sizing: border-box; 18 | overflow: hidden; 19 | } 20 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import './index.css'; 2 | import React from 'react'; 3 | import * as ReactDOMClient from 'react-dom/client'; 4 | import { App } from './components/App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOMClient.createRoot(document.getElementById('root')!) 8 | root.render( 9 | 10 | 11 | 12 | ) 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals() 18 | -------------------------------------------------------------------------------- /src/modules/glsl/noise.ts: -------------------------------------------------------------------------------- 1 | export const cnoise21 = ` 2 | // Classic Perlin 2D Noise 3 | // by Stefan Gustavson 4 | // 5 | vec2 fade(vec2 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);} 6 | vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);} 7 | 8 | float cnoise21(vec2 P){ 9 | vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0); 10 | vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0); 11 | Pi = mod(Pi, 289.0); // To avoid truncation effects in permutation 12 | vec4 ix = Pi.xzxz; 13 | vec4 iy = Pi.yyww; 14 | vec4 fx = Pf.xzxz; 15 | vec4 fy = Pf.yyww; 16 | vec4 i = permute(permute(ix) + iy); 17 | vec4 gx = 2.0 * fract(i * 0.0243902439) - 1.0; // 1/41 = 0.024... 18 | vec4 gy = abs(gx) - 0.5; 19 | vec4 tx = floor(gx + 0.5); 20 | gx = gx - tx; 21 | vec2 g00 = vec2(gx.x,gy.x); 22 | vec2 g10 = vec2(gx.y,gy.y); 23 | vec2 g01 = vec2(gx.z,gy.z); 24 | vec2 g11 = vec2(gx.w,gy.w); 25 | vec4 norm = 1.79284291400159 - 0.85373472095314 * vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)); 26 | g00 *= norm.x; 27 | g01 *= norm.y; 28 | g10 *= norm.z; 29 | g11 *= norm.w; 30 | float n00 = dot(g00, vec2(fx.x, fy.x)); 31 | float n10 = dot(g10, vec2(fx.y, fy.y)); 32 | float n01 = dot(g01, vec2(fx.z, fy.z)); 33 | float n11 = dot(g11, vec2(fx.w, fy.w)); 34 | vec2 fade_xy = fade(Pf.xy); 35 | vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x); 36 | float n_xy = mix(n_x.x, n_x.y, fade_xy.y); 37 | return 2.3 * n_xy; 38 | } 39 | ` 40 | -------------------------------------------------------------------------------- /src/modules/glsl/shader.ts: -------------------------------------------------------------------------------- 1 | export const enVertexShader = ` 2 | varying vec2 v_uv; 3 | 4 | void main() { 5 | v_uv = uv; 6 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 7 | } 8 | ` 9 | 10 | export const enFragmentShader = ` 11 | uniform sampler2D u_texture; 12 | uniform vec2 u_mouse; 13 | uniform float u_aspect; 14 | uniform bool u_enable; 15 | varying vec2 v_uv; 16 | 17 | void main() { 18 | vec4 tex = texture2D(u_texture, v_uv); 19 | 20 | vec2 aspect = vec2(u_aspect, 1.0); 21 | float radius = 0.19; 22 | float dist = distance(u_mouse * aspect, v_uv * aspect); 23 | float d = 1.0 - smoothstep(radius, radius + 0.005, dist); 24 | 25 | if (u_enable) { 26 | tex.a = mix(tex.a, 0.0, d); 27 | } 28 | 29 | gl_FragColor = tex; 30 | } 31 | ` 32 | 33 | // ======================================================== 34 | export const jpVertexShader = ` 35 | varying vec2 v_uv; 36 | 37 | void main() { 38 | v_uv = uv; 39 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 40 | } 41 | ` 42 | 43 | export const jpFragmentShader = ` 44 | uniform sampler2D u_texture; 45 | uniform vec2 u_mouse; 46 | uniform float u_aspect; 47 | uniform bool u_enable; 48 | varying vec2 v_uv; 49 | 50 | void main() { 51 | vec2 aspect = vec2(u_aspect, 1.0); 52 | float radius = 0.19; 53 | float dist = distance(u_mouse * aspect, v_uv * aspect); 54 | float d = smoothstep(radius, radius + 0.005, dist); 55 | 56 | vec2 sub = u_mouse - v_uv; 57 | sub *= aspect; 58 | 59 | vec2 uv = v_uv - sub * pow(dist * 0.7, 0.7); 60 | vec4 tex_r = texture2D(u_texture, uv); 61 | vec4 tex_g = texture2D(u_texture, uv + sub * 0.03); 62 | vec4 tex_b = texture2D(u_texture, uv + sub * 0.01); 63 | float a = max(max(tex_r.a, tex_g.a), tex_b.a); 64 | vec4 tex = vec4(tex_r.r, tex_g.g, tex_b.b, a); 65 | 66 | tex.a = mix(tex.a, 0.0, d); 67 | 68 | if (!u_enable) { 69 | tex.a = 0.0; 70 | } 71 | 72 | gl_FragColor = tex; 73 | } 74 | ` 75 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------