├── .DS_Store
├── .gitignore
├── Readme.md
├── assets
├── .DS_Store
├── classic-watch.glb
├── logo.svg
└── scene.glb
├── index.html
├── package.json
├── src
├── index.ts
└── styles.css
└── tsconfig.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yvesvinckier/threejs-scrollable-page/746adbe6d6daa3aeea957b71d511940122169f1f/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 |
106 | # Docusaurus cache and generated files
107 | .docusaurus
108 |
109 | # Serverless directories
110 | .serverless/
111 |
112 | # FuseBox cache
113 | .fusebox/
114 |
115 | # DynamoDB Local files
116 | .dynamodb/
117 |
118 | # TernJS port file
119 | .tern-port
120 |
121 | # Stores VSCode versions used for testing VSCode extensions
122 | .vscode-test
123 |
124 | # yarn v2
125 | .yarn/cache
126 | .yarn/unplugged
127 | .yarn/build-state.yml
128 | .yarn/install-state.gz
129 | .pnp.*
130 |
131 | package-lock.json
132 | yarn.lock
133 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # WebGi starter project
2 | A template for a vanilla(no ui-framework) project with webgi engine in typescript using parcel bundler.
3 |
4 | About webgi: [https://webgi.xyz/](https://webgi.xyz/)
5 |
6 | ## Running
7 | First install the dependencies:
8 | ```bash
9 | npm install
10 | ```
11 |
12 | To run the project in development mode:
13 | ```bash
14 | npm start
15 | ```
16 | Then navigate to [http://localhost:1234/index.html](http://localhost:1234/index.html) in a web browser to see the default scene in a viewer.
17 |
18 | The assets are stored in the `assets` directory.
19 |
20 | To build the project for production:
21 | ```bash
22 | npm run build
23 | ```
24 |
25 | ## Updates
26 | Check the [webgi manual](https://webgi.xyz/docs/manual/#sdk-links) for the latest version.
27 | To use the different version:
28 | * Update the version number in `package.json` file for both `webgi` and `@types/webgi`.
29 | * Run `npm install` to update the dependencies.
30 | * Delete `.cache` folder created by parcel bundler: `rm -rf .cache`
31 | * Run `npm start` or `npm run build` to run or build the project.
32 |
33 | ## Documentation
34 | For the latest version and documentation: [WebGi Docs](https://webgi.xyz/docs/).
35 |
36 | ## License
37 | For license and terms of use, see the [SDK License](https://webgi.xyz/docs/license).
38 |
--------------------------------------------------------------------------------
/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yvesvinckier/threejs-scrollable-page/746adbe6d6daa3aeea957b71d511940122169f1f/assets/.DS_Store
--------------------------------------------------------------------------------
/assets/classic-watch.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yvesvinckier/threejs-scrollable-page/746adbe6d6daa3aeea957b71d511940122169f1f/assets/classic-watch.glb
--------------------------------------------------------------------------------
/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
20 |
21 |
29 |
30 |
31 |
35 |
39 |
40 |
41 |
42 |
46 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/assets/scene.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yvesvinckier/threejs-scrollable-page/746adbe6d6daa3aeea957b71d511940122169f1f/assets/scene.glb
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Threejs scrollable page
5 |
6 |
7 |
11 |
12 |
13 |
17 |
21 |
22 |
23 |
24 |
25 |
26 | BUY NOW
27 |
28 |
29 |
30 |
31 |
32 |
Doris Armchair
33 |
34 | Before they sold out salvia aesthetic, hexagon disrupt sustainable
35 | vaporware crucifix succulents kale chips. Selvage knausgaard
36 | scenester.
37 |
38 |
KNOW MORE
39 |
40 |
41 |
42 |
43 |
44 |
Smooth design
45 |
46 | Humblebrag pickled listicle yes plz williamsburg shoreditch tumblr,
47 | put a bird on it cred knausgaard snackwave scenester. Before they
48 | sold out salvia aesthetic.
49 |
50 |
51 |
52 |
53 |
54 |
55 |
Softness of the upholstery
56 |
57 | Humblebrag pickled listicle yes plz williamsburg shoreditch tumblr,
58 | put a bird on it cred knausgaard snackwave scenester. Before they
59 | sold out salvia aesthetic.
60 |
61 |
CUSTOMIZE
62 |
63 |
64 |
65 |
76 |
77 |
78 |
79 |
80 | EXIT
81 |
82 |
83 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webgi-vanilla-typescript",
3 | "version": "1.0.0",
4 | "description": "WebGi + TypeScript example starter project",
5 | "main": "index.html",
6 | "scripts": {
7 | "start": "parcel index.html",
8 | "build": "parcel build index.html --no-minify --public-url ./"
9 | },
10 | "devDependencies": {
11 | "@types/three": "^0.139.0",
12 | "@types/webgi": "https://storage.googleapis.com/dist.pixotronics.com/webgi/runtime/bundle-types-0.5.8.tgz",
13 | "parcel-bundler": "^1.6.1",
14 | "parcel-plugin-static-files-copy": "^2.6.0",
15 | "typescript": "4.4.4",
16 | "webgi": "https://storage.googleapis.com/dist.pixotronics.com/webgi/runtime/bundle-0.5.8.tgz"
17 | },
18 | "resolutions": {
19 | "@babel/preset-env": "7.13.8"
20 | },
21 | "keywords": [
22 | "typescript",
23 | "javascript"
24 | ],
25 | "staticFiles": {
26 | "staticPath": [
27 | {
28 | "staticPath": "assets",
29 | "staticOutDir": "assets"
30 | }
31 | ]
32 | },
33 | "dependencies": {
34 | "gsap": "^3.11.3"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ViewerApp,
3 | AssetManagerPlugin,
4 | GBufferPlugin,
5 | ProgressivePlugin,
6 | TonemapPlugin,
7 | SSRPlugin,
8 | SSAOPlugin,
9 | BloomPlugin,
10 | GammaCorrectionPlugin,
11 | MeshBasicMaterial2,
12 | Color,
13 | // Color, // Import THREE.js internals
14 | // Texture, // Import THREE.js internals
15 | } from "webgi";
16 | import "./styles.css";
17 |
18 | import gsap from "gsap";
19 | import { ScrollTrigger } from "gsap/ScrollTrigger";
20 |
21 | gsap.registerPlugin(ScrollTrigger);
22 |
23 | async function setupViewer() {
24 | // Initialize the viewer
25 | const viewer = new ViewerApp({
26 | canvas: document.getElementById("webgi-canvas") as HTMLCanvasElement,
27 | // isAntialiased: true,
28 | });
29 |
30 | // Add some plugins
31 | const manager = await viewer.addPlugin(AssetManagerPlugin);
32 | const camera = viewer.scene.activeCamera;
33 | const position = camera.position;
34 | const target = camera.target;
35 | const exitButton = document.querySelector(".button--exit") as HTMLElement;
36 | const customizerInterface = document.querySelector(
37 | ".customizer--container"
38 | ) as HTMLElement;
39 |
40 | // Add plugins individually.
41 | await viewer.addPlugin(GBufferPlugin);
42 | await viewer.addPlugin(new ProgressivePlugin(32));
43 | // await viewer.addPlugin(new TonemapPlugin(true));
44 | await viewer.addPlugin(new TonemapPlugin(!viewer.useRgbm));
45 | await viewer.addPlugin(GammaCorrectionPlugin);
46 | await viewer.addPlugin(SSRPlugin);
47 | await viewer.addPlugin(SSAOPlugin);
48 | await viewer.addPlugin(BloomPlugin);
49 |
50 | // This must be called once after all plugins are added.
51 | viewer.renderer.refreshPipeline();
52 |
53 | await manager.addFromPath("./assets/scene.glb");
54 |
55 | const drillMaterial = manager.materials!.findMaterialsByName(
56 | "FABRIC"
57 | )[0] as MeshBasicMaterial2;
58 |
59 | // viewer.getPlugin(TonemapPlugin)!.config!.clipBackground = true; // in case its set to false in the glb
60 |
61 | viewer.scene.activeCamera.setCameraOptions({ controlsEnabled: false });
62 |
63 | onUpdate();
64 |
65 | function setupScrollanimation() {
66 | const tl = gsap.timeline();
67 |
68 | // First section
69 | tl.to(position, {
70 | x: 0.0,
71 | y: 0.0,
72 | z: 3.25,
73 | scrollTrigger: {
74 | trigger: ".second",
75 | start: "top bottom",
76 | end: "top top",
77 | scrub: true,
78 | immediateRender: false,
79 | },
80 | onUpdate,
81 | })
82 | .to(".section--one--container", {
83 | yPercent: "-150",
84 | opacity: 0,
85 | scrollTrigger: {
86 | trigger: ".second",
87 | start: "top bottom",
88 | end: "top top",
89 | scrub: 1,
90 | immediateRender: false,
91 | },
92 | })
93 | .to(target, {
94 | x: 0,
95 | y: 0,
96 | z: 0,
97 | scrollTrigger: {
98 | trigger: ".second",
99 | start: "top bottom",
100 | end: "top top",
101 | scrub: true,
102 | immediateRender: false,
103 | },
104 | })
105 | // Last section
106 |
107 | .to(position, {
108 | x: -3.0,
109 | y: 0.0,
110 | z: 0.0,
111 | scrollTrigger: {
112 | trigger: ".third",
113 | start: "top bottom",
114 | end: "top top",
115 | scrub: true,
116 | immediateRender: false,
117 | },
118 | onUpdate,
119 | })
120 | .to(target, {
121 | x: 0,
122 | y: 0,
123 | z: 0,
124 | scrollTrigger: {
125 | trigger: ".third",
126 | start: "top bottom",
127 | end: "top top",
128 | scrub: true,
129 | immediateRender: false,
130 | },
131 | });
132 | }
133 |
134 | setupScrollanimation();
135 |
136 | // WEBBI UPDATE
137 | let needsUpdate = true;
138 |
139 | function onUpdate() {
140 | needsUpdate = true;
141 | viewer.renderer.resetShadows();
142 | }
143 |
144 | viewer.addEventListener("preFrame", () => {
145 | if (needsUpdate) {
146 | camera.positionTargetUpdated(true);
147 | needsUpdate = false;
148 | }
149 | });
150 |
151 | // KNOW MORE EVENT
152 | document.querySelector(".button--hero")?.addEventListener("click", () => {
153 | const element = document.querySelector(".second");
154 | window.scrollTo({
155 | top: element?.getBoundingClientRect().top,
156 | left: 0,
157 | behavior: "smooth",
158 | });
159 | });
160 |
161 | // SCROLL TO TOP
162 | document.querySelectorAll(".button--footer")?.forEach((item) => {
163 | item.addEventListener("click", () => {
164 | window.scrollTo({ top: 0, left: 0, behavior: "smooth" });
165 | });
166 | });
167 |
168 | // CUSTOMIZE
169 | const sections = document.querySelector(".container") as HTMLElement;
170 | const webgiCanvasContainer = document.getElementById(
171 | "webgi-canvas-container"
172 | ) as HTMLElement;
173 | document
174 | .querySelector(".button--customize")
175 | ?.addEventListener("click", () => {
176 | sections.style.visibility = "hidden";
177 | webgiCanvasContainer.style.pointerEvents = "all";
178 | document.body.style.cursor = "grab";
179 | gsap.to(position, {
180 | x: -1.95,
181 | y: 1.54,
182 | z: 2.29,
183 | duration: 2,
184 | ease: "power3.inOut",
185 | onUpdate,
186 | });
187 | gsap.to(target, {
188 | x: 0,
189 | y: 0,
190 | z: 0,
191 | duration: 2,
192 | ease: "power3.inOut",
193 | onUpdate,
194 | onComplete: enableControlers,
195 | });
196 | });
197 |
198 | function enableControlers() {
199 | exitButton.style.visibility = "visible";
200 | customizerInterface.style.visibility = "visible";
201 | viewer.scene.activeCamera.setCameraOptions({ controlsEnabled: true });
202 | }
203 |
204 | // EXIT CUSTOMIZE
205 | exitButton.addEventListener("click", () => {
206 | gsap.to(position, {
207 | x: -3.0,
208 | y: 0.0,
209 | z: 0.0,
210 | duration: 1,
211 | ease: "power3.inOut",
212 | onUpdate,
213 | });
214 | gsap.to(target, {
215 | x: 0,
216 | y: 0,
217 | z: 0,
218 | duration: 1,
219 | ease: "power3.inOut",
220 | onUpdate,
221 | });
222 | viewer.scene.activeCamera.setCameraOptions({ controlsEnabled: false });
223 | sections.style.visibility = "visible";
224 | webgiCanvasContainer.style.pointerEvents = "none";
225 | document.body.style.cursor = "default";
226 | exitButton.style.visibility = "hidden";
227 | customizerInterface.style.visibility = "hidden";
228 | });
229 |
230 | document
231 | .querySelector(".button--colors.violet")
232 | ?.addEventListener("click", () => {
233 | changeColor(new Color(0xaa83aa).convertSRGBToLinear());
234 | });
235 |
236 | document
237 | .querySelector(".button--colors.red")
238 | ?.addEventListener("click", () => {
239 | changeColor(new Color(0xa57d7d).convertSRGBToLinear());
240 | });
241 |
242 | document
243 | .querySelector(".button--colors.green")
244 | ?.addEventListener("click", () => {
245 | changeColor(new Color(0x96b2a1).convertSRGBToLinear());
246 | });
247 |
248 | function changeColor(_colorToBeChanged: Color) {
249 | drillMaterial.color = _colorToBeChanged;
250 | viewer.scene.setDirty();
251 | }
252 | }
253 |
254 | setupViewer();
255 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "DM Sans", sans-serif;
3 | overflow-x: hidden;
4 | margin: 0;
5 | padding: 0;
6 | overscroll-behavior: none;
7 | }
8 |
9 | .nav--bar {
10 | position: absolute;
11 | display: flex;
12 | align-content: space-between;
13 | align-items: center;
14 | width: 100%;
15 | justify-content: space-between;
16 | margin-top: 30px;
17 | z-index: 1;
18 | }
19 |
20 | .logo {
21 | margin-left: 40px;
22 | height: 80px;
23 | }
24 |
25 | .section {
26 | display: flex;
27 | width: 100vw;
28 | height: 100vh;
29 | align-items: center;
30 | position: relative;
31 | z-index: 1;
32 | }
33 |
34 | .section--one--container {
35 | display: flex;
36 | flex-direction: column;
37 | align-items: center;
38 | width: 100%;
39 | }
40 |
41 | .first h1 {
42 | font-size: 8rem;
43 | line-height: 8rem;
44 | letter-spacing: -0.3rem;
45 | margin: 0 0 30px -10px;
46 | text-align: center;
47 | }
48 |
49 | .first p {
50 | max-width: 920px;
51 | margin: 0 auto;
52 | text-align: center;
53 | font-family: "Roboto Mono", monospace;
54 | }
55 |
56 | .button--hero {
57 | background: #000000;
58 | color: #ffffff;
59 | border: none;
60 | font-weight: 600;
61 | border-radius: 50px;
62 | padding: 15px 30px;
63 | margin-top: 24px;
64 | margin-right: 40px;
65 | font-size: 0.6rem;
66 | cursor: pointer;
67 | transition: all 0.8s ease;
68 | }
69 |
70 | .button--customize {
71 | background: #000000;
72 | color: #ffffffff;
73 | border: none;
74 | font-weight: 600;
75 | border-radius: 50px;
76 | padding: 15px 30px;
77 | margin-top: 24px;
78 | margin-right: 40px;
79 | font-size: 0.6rem;
80 | cursor: pointer;
81 | transition: all 0.8s ease;
82 | }
83 | .button--hero:hover {
84 | background: black;
85 | color: white;
86 | }
87 |
88 | .button--navbar {
89 | background: black;
90 | color: white;
91 | border: none;
92 | border-radius: 50px;
93 | padding: 10px 30px;
94 | margin-right: 40px;
95 | font-size: 0.6rem;
96 | height: 42px;
97 | }
98 | svg {
99 | fill: #ffffff;
100 | }
101 |
102 | .second {
103 | justify-content: flex-end;
104 | position: relative;
105 | }
106 |
107 | .section--two--container {
108 | width: 360px;
109 | margin-right: 10%;
110 | z-index: 1;
111 | }
112 |
113 | p {
114 | font-family: "Roboto Mono", monospace;
115 | }
116 |
117 | .section--third--container {
118 | width: 360px;
119 | margin-left: 10%;
120 | z-index: 1;
121 | }
122 |
123 | h2 {
124 | font-size: 3rem;
125 | letter-spacing: -2px;
126 | line-height: 2.5rem;
127 | }
128 |
129 | footer {
130 | background: black;
131 | padding: 70px;
132 | display: flex;
133 | flex-direction: column;
134 | justify-content: center;
135 | align-items: center;
136 | position: relative;
137 | z-index: 1;
138 | }
139 | footer p {
140 | padding-top: 40px;
141 | text-align: center;
142 | }
143 |
144 | .button--footer {
145 | background: white;
146 | color: black;
147 | border: none;
148 | border-radius: 50px;
149 | padding: 10px 30px;
150 | font-size: 0.6rem;
151 | cursor: pointer;
152 | }
153 |
154 | #webgi-canvas {
155 | width: 100%;
156 | height: 100%;
157 | position: fixed;
158 | top: 0;
159 | }
160 |
161 | #webgi-canvas-container {
162 | width: 100vw;
163 | height: 100vh;
164 | pointer-events: none;
165 | position: fixed;
166 | display: flex;
167 | justify-content: flex-end;
168 | flex-direction: column;
169 | align-items: center;
170 | }
171 |
172 | .button--exit {
173 | width: 90px;
174 | margin-bottom: 5%;
175 | z-index: 5;
176 | position: fixed;
177 | bottom: 0;
178 | visibility: hidden;
179 | }
180 |
181 | .customizer--container {
182 | position: fixed;
183 | top: 50%;
184 | visibility: hidden;
185 | }
186 |
187 | .strap--colors {
188 | list-style: none;
189 | }
190 |
191 | .button--colors {
192 | width: 30px;
193 | height: 30px;
194 | border-radius: 50px;
195 | padding: 0;
196 | margin-bottom: 10px;
197 | cursor: pointer;
198 | transition: all 0.8s ease;
199 | }
200 |
201 | .button--colors:hover {
202 | filter: drop-shadow(0 0 0.4rem rgba(79, 79, 79, 0.704));
203 | transform: scale(1.1);
204 | }
205 |
206 | .green {
207 | background-color: rgb(150, 178, 161);
208 | }
209 | /* 0x96b2a1 */
210 |
211 | .red {
212 | background-color: rgb(165, 125, 125);
213 | }
214 | /* 0xa57d7d */
215 |
216 | .violet {
217 | background-color: rgb(170, 131, 170);
218 | }
219 | /* 0xaa83aa */
220 |
221 | .credit {
222 | color: #ffffff;
223 | font-size: 0.6rem;
224 | }
225 |
226 | .credit a {
227 | color: #ffffff;
228 | }
229 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "module": "commonjs",
5 | "jsx": "preserve",
6 | "esModuleInterop": true,
7 | "sourceMap": true,
8 | "allowJs": true,
9 | "lib": [
10 | "es6",
11 | "dom"
12 | ],
13 | "rootDir": "src",
14 | "moduleResolution": "node"
15 | }
16 | }
--------------------------------------------------------------------------------