├── .gitignore
├── README.md
├── css
└── base.css
├── index.html
├── js
└── bundle.umd.js
├── package.json
└── src
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | node_modules
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebGL based transitions on scroll
2 |
3 | A demo that demonstrates how a smooth WebGL transition can be realized using Phenomenon (based on three.js).
4 |
5 | 
6 |
7 | [Article on Codrops](https://tympanus.net/codrops/?p=38923)
8 |
9 | [Demo](https://tympanus.net/Tutorials/scroll-transitions-webgl)
10 |
11 | ## Credits
12 |
13 | - [three.js](https://threejs.org/) by Ricardo Cabello
14 | - [phenomenon](https://github.com/vaneenige/THREE.Phenomenon) by Colin van Eenige
15 |
16 | ## License
17 | This resource can be used freely if integrated or build upon in personal or commercial projects such as websites, web apps and web templates intended for sale. It is not allowed to take the resource "as-is" and sell it, redistribute, re-publish it, or sell "pluginized" versions of it. Free plugins built using this resource should have a visible mention and link to the original work. Always consider the licenses of all included libraries, scripts and images used.
18 |
19 | ## Misc
20 | Follow Colin: [Twitter](https://twitter.com/cvaneenige), [Codepen](https://codepen.io/cvaneenige), [GitHub](https://github.com/vaneenige)
21 |
22 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [Google+](https://plus.google.com/101095823814290637419), [GitHub](https://github.com/codrops), [Pinterest](http://www.pinterest.com/codrops/), [Instagram](https://www.instagram.com/codropsss/)
23 |
24 | [© Codrops 2018](http://www.codrops.com)
25 |
--------------------------------------------------------------------------------
/css/base.css:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}body{margin:0;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:0.67em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em;}pre{white-space:pre-wrap;}q{quotes:"\201C" "\201D" "\2018" "\2019";}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}img{border:0;}svg:not(:root){overflow:hidden;}figure{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}
2 | *,
3 | *::after,
4 | *::before {
5 | box-sizing: border-box;
6 | }
7 |
8 | :root {
9 | font-size: 15px;
10 | }
11 |
12 | html {
13 | height: 100%;
14 | }
15 |
16 | body {
17 | height: 100%;
18 | --color-text: #fff;
19 | --color-bg: #000;
20 | --color-link: #697c7e;
21 | --color-link-hover: #fff;
22 | color: var(--color-text);
23 | background-color: var(--color-bg);
24 | font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Cantarell,
25 | Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
26 | -webkit-font-smoothing: antialiased;
27 | -moz-osx-font-smoothing: grayscale;
28 | text-rendering: optimizeLegibility;
29 | min-height: 10000px;
30 | }
31 |
32 | canvas {
33 | position: fixed;
34 | top: 0;
35 | left: 0;
36 | }
37 |
38 | a {
39 | text-decoration: none;
40 | color: var(--color-link);
41 | outline: none;
42 | }
43 |
44 | a:hover,
45 | a:focus {
46 | color: var(--color-link-hover);
47 | outline: none;
48 | }
49 |
50 | .frame {
51 | padding: 3rem 5vw;
52 | text-align: center;
53 | position: relative;
54 | z-index: 1000;
55 | }
56 |
57 | .frame__title {
58 | font-size: 1rem;
59 | margin: 0 0 1rem;
60 | font-weight: normal;
61 | }
62 |
63 | .frame__links {
64 | display: inline;
65 | }
66 |
67 | .frame__github,
68 | .frame__links a:not(:last-child),
69 | .frame__demos a:not(:last-child) {
70 | margin-right: 1rem;
71 | }
72 |
73 | .frame__demos {
74 | margin: 1rem 0;
75 | }
76 |
77 | .frame__demo--current,
78 | .frame__demo--current:hover {
79 | text-decoration: underline;
80 | color: var(--color-link);
81 | }
82 |
83 | .content {
84 | display: flex;
85 | flex-direction: column;
86 | width: 100vw;
87 | height: 100vh;
88 | justify-content: center;
89 | position: relative;
90 | align-items: center;
91 | }
92 |
93 | .header {
94 | width: 100%;
95 | display: flex;
96 | justify-content: center;
97 | align-items: center;
98 | flex-direction: column;
99 | position: relative;
100 | z-index: 1;
101 | will-change: opacity;
102 | opacity: 0;
103 | }
104 |
105 | .header__title {
106 | font-size: 2rem;
107 | text-transform: uppercase;
108 | margin-bottom: 0px;
109 | letter-spacing: 0.15rem;
110 | background: linear-gradient(40deg, #fdf680 0%,#d6716d 50%, #a0fafb 100%);
111 | background-size: cover;
112 | -webkit-background-clip: text;
113 | -webkit-text-fill-color: transparent;
114 | background-clip: text;
115 | text-fill-color: transparent;
116 | font-weight: 100;
117 | }
118 |
119 | .header__text {
120 | font-size: 1.25rem;
121 | color: #393c46;
122 | }
123 |
124 | .heading {
125 | display: flex;
126 | justify-content: center;
127 | align-items: center;
128 | flex-direction: column;
129 | position: fixed;
130 | z-index: 1;
131 | will-change: opacity;
132 | opacity: 0;
133 | top: 0;
134 | line-height: 1.2;
135 | font-size: 1.5rem;
136 | text-align: center;
137 | color: white;
138 | max-width: calc(100% - 80px);
139 | left: 50%;
140 | transform: translateX(-50%);
141 | padding: 0 40px;
142 | }
143 |
144 | @media screen and (min-width: 53em) {
145 | .message {
146 | display: none;
147 | }
148 | .frame {
149 | position: fixed;
150 | text-align: left;
151 | z-index: 10000;
152 | top: 0;
153 | left: 0;
154 | display: grid;
155 | align-content: space-between;
156 | width: 100%;
157 | max-width: none;
158 | height: 100vh;
159 | padding: 2rem 2.25rem;
160 | pointer-events: none;
161 | grid-template-columns: 25% 50% 25%;
162 | grid-template-rows: auto auto auto;
163 | grid-template-areas: 'title title ... '
164 | '... ... ...'
165 | 'github demos links';
166 | }
167 | .frame__title-wrap {
168 | grid-area: title;
169 | display: flex;
170 | }
171 | .frame__title {
172 | margin: 0;
173 | }
174 | .frame__github {
175 | grid-area: github;
176 | justify-self: start;
177 | margin: 0;
178 | }
179 | .frame__demos {
180 | margin: 0;
181 | grid-area: demos;
182 | justify-self: center;
183 | }
184 | .frame__links {
185 | grid-area: links;
186 | padding: 0;
187 | justify-self: end;
188 | }
189 | .frame a {
190 | pointer-events: auto;
191 | }
192 | .header__title {
193 | font-size: 7vw;
194 | letter-spacing: 0.5vw;
195 | }
196 | .heading {
197 | line-height: 1.2;
198 | font-size: 2.5vw;
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | WebGL based transitions on scroll | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
33 |
34 |
35 |
36 |
37 |
38 |
WebGL Transitions on Scroll
39 |
40 |
GitHub
41 |
45 |
46 |
47 |
51 |
52 | A tiny wrapper around three.js built for high-performance WebGL experiences.
53 |
54 |
55 | GPU based for smooth transitions like scale, rotation and movement.
56 |
57 |
Modify default geometry and material for infinite possibilities.
58 |
That's it, thank you for scrolling! :)
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/js/bundle.umd.js:
--------------------------------------------------------------------------------
1 | !function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){function e(e,n){return e.map(function(e){return e+(-(t=n)+Math.random()*t*2);var t})}function n(e){var n=e.rainbow;void 0===n&&(n=!1);var t=e.geometry,o=e.material,r=e.multiplier,a=e.duration,s=e.points,u=[{name:"aPositionStart",data:s[0],size:3},{name:"aControlPointOne",data:s[1],size:3},{name:"aControlPointTwo",data:s[2],size:3},{name:"aPositionEnd",data:s[3],size:3},{name:"aOffset",data:function(e){return[e*((1-a)/(r-1))]},size:1}];n&&u.push({name:"aColor",data:function(e,n){var t=new THREE.Color;return t.setHSL(e/n,.6,.7),[t.r,t.g,t.b]},size:3});var c="\n attribute vec3 aPositionStart;\n attribute vec3 aControlPointOne;\n attribute vec3 aControlPointTwo;\n attribute vec3 aPositionEnd;\n attribute float aOffset;\n uniform float time;\n "+(n?"attribute vec3 aColor; varying vec3 vColor;":"")+"\n\n float easeInOutSin(float t){\n return (1.0 + sin("+Math.PI+" * t - "+Math.PI+" / 2.0)) / 2.0;\n }\n\n vec4 quatFromAxisAngle(vec3 axis, float angle) {\n float halfAngle = angle * 0.5;\n return vec4(axis.xyz * sin(halfAngle), cos(halfAngle));\n }\n\n vec3 rotateVector(vec4 q, vec3 v) {\n return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);\n }\n\n vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) {\n return mix(mix(mix(a, b, t), mix(b, c, t), t), mix(mix(b, c, t), mix(c, d, t), t), t);\n }\n\n void main(){\n float tProgress = easeInOutSin(min(1.0, max(0.0, (time - aOffset)) / "+a+"));\n vec4 quatX = quatFromAxisAngle(vec3(1.0, 0.0, 0.0), -5.0 * tProgress);\n vec4 quatY = quatFromAxisAngle(vec3(0.0, 1.0, 0.0), -5.0 * tProgress);\n vec3 basePosition = rotateVector(quatX, rotateVector(quatY, position));\n vec3 newPosition = bezier4(aPositionStart, aControlPointOne, aControlPointTwo, aPositionEnd, tProgress);\n float scale = tProgress * 2.0 - 1.0;\n scale = 1.0 - scale * scale;\n basePosition *= scale;\n gl_Position = basePosition + newPosition;\n "+(n?"vColor = aColor;":"")+"\n }\n ",l=new THREE.Phenomenon({attributes:u,uniforms:{time:{value:0}},vertex:c,geometry:t,multiplier:r,material:o,fragment:n?[["#define PHONG","varying vec3 vColor;"],["vec4( diffuse, opacity )","vec4( vColor, opacity )"],["vec3 totalEmissiveRadiance = emissive;","vec3 totalEmissiveRadiance = vColor;"]]:[]});return i.add(l.mesh),l}var t=new THREE.WebGLRenderer({antialias:!0});t.setClearColor(2171169,0),t.setSize(window.innerWidth,window.innerHeight),t.setPixelRatio(window.devicePixelRatio>=2?2:1),document.body.appendChild(t.domElement);var i=new THREE.Scene,o=new THREE.PerspectiveCamera(40,window.innerWidth/window.innerHeight,.1,1e4);o.position.set(0,20,35),o.lookAt(i.position),i.add(o);var r=new THREE.AmbientLight("#ffffff",.1);i.add(r);var a=new THREE.SpotLight(16777215,1,80,.25*Math.PI,1,2);function s(){t.render(i,o)}function u(e){return"#"+(new THREE.Color).setHSL(e,.5,.5).getHexString()}a.position.set(0,40,0),i.add(a);var c=[n({geometry:new THREE.IcosahedronGeometry(1,0),material:new THREE.MeshPhongMaterial({emissive:u(0),specular:"#efefef",shininess:20,flatShading:!0}),multiplier:200,duration:.7,points:[function(){return e([0,0,0],10)},function(){return e([0,0,0],8)},function(){return e([0,0,0],2)},function(){return e([0,0,0],12)}]}),n({geometry:new THREE.BoxGeometry(1,1,1),material:new THREE.MeshPhongMaterial({emissive:u(.5/3),specular:"#efefef",shininess:20,flatShading:!0}),multiplier:200,duration:.4,points:[function(){return e([-10,0,0],4)},function(){return e([-2.5,-10,0],4)},function(){return e([2.5,10,0],4)},function(){return e([10,0,0],4)}]}),n({geometry:new THREE.TetrahedronGeometry(1),material:new THREE.MeshPhongMaterial({emissive:u(.5),specular:"#efefef",shininess:20,flatShading:!0}),multiplier:400,duration:.4,points:[function(){return e([0,10,0],10)},function(){return e([0,0,0],10)},function(){return e([0,0,0],10)},function(){return e([0,-10,0],10)}]}),n({rainbow:!0,geometry:new THREE.IcosahedronGeometry(.8,3),material:new THREE.MeshPhongMaterial({emissive:u(2/3),specular:"#efefef",shininess:20,flatShading:!0}),multiplier:400,duration:.4,points:[function(){return e([0,-20,0],0)},function(){return e([0,10,0],0)},function(){return e([0,-10,0],10)},function(){return e([0,15,0],15)}]})],l=document.querySelectorAll(".heading"),f=document.querySelector(".header");uos(0,1,function(e){return s()}),uos(0,.05,function(e){return f.style.opacity=1-e});for(var d=1/c.length,m=function(e){var n=e*d,t=n+.5*d,i=(e+1)*d;uos(n,t,function(n){return c[e].uniforms.time.value=n}),uos(t,i,function(n){var t=2*n-1;t=1-t*t,l[e].style.opacity=e===c.length-1?1.5*n:1.5*t})},v=0;v item + getRandomBetween(noise));
8 | }
9 |
10 | function getRandomFromArray(array) {
11 | return array[Math.floor(Math.random() * array.length)];
12 | }
13 |
14 | function createInstance({ rainbow = false, geometry, material, multiplier, duration, points }) {
15 | const attributes = [
16 | {
17 | name: 'aPositionStart',
18 | data: points[0],
19 | size: 3,
20 | },
21 | {
22 | name: 'aControlPointOne',
23 | data: points[1],
24 | size: 3,
25 | },
26 | {
27 | name: 'aControlPointTwo',
28 | data: points[2],
29 | size: 3,
30 | },
31 | {
32 | name: 'aPositionEnd',
33 | data: points[3],
34 | size: 3,
35 | },
36 | {
37 | name: 'aOffset',
38 | data: i => [i * ((1 - duration) / (multiplier - 1))],
39 | size: 1,
40 | },
41 | ];
42 |
43 | if (rainbow) {
44 | attributes.push({
45 | name: 'aColor',
46 | data: (i, total) => {
47 | const color = new THREE.Color();
48 | color.setHSL(i / total, 0.6, 0.7);
49 | return [color.r, color.g, color.b];
50 | },
51 | size: 3,
52 | });
53 | }
54 |
55 | const uniforms = {
56 | time: {
57 | value: 0,
58 | },
59 | };
60 |
61 | const vertex = `
62 | attribute vec3 aPositionStart;
63 | attribute vec3 aControlPointOne;
64 | attribute vec3 aControlPointTwo;
65 | attribute vec3 aPositionEnd;
66 | attribute float aOffset;
67 | uniform float time;
68 | ${rainbow ? `attribute vec3 aColor; varying vec3 vColor;` : ``}
69 |
70 | float easeInOutSin(float t){
71 | return (1.0 + sin(${Math.PI} * t - ${Math.PI} / 2.0)) / 2.0;
72 | }
73 |
74 | vec4 quatFromAxisAngle(vec3 axis, float angle) {
75 | float halfAngle = angle * 0.5;
76 | return vec4(axis.xyz * sin(halfAngle), cos(halfAngle));
77 | }
78 |
79 | vec3 rotateVector(vec4 q, vec3 v) {
80 | return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
81 | }
82 |
83 | vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3 d, float t) {
84 | return mix(mix(mix(a, b, t), mix(b, c, t), t), mix(mix(b, c, t), mix(c, d, t), t), t);
85 | }
86 |
87 | void main(){
88 | float tProgress = easeInOutSin(min(1.0, max(0.0, (time - aOffset)) / ${duration}));
89 | vec4 quatX = quatFromAxisAngle(vec3(1.0, 0.0, 0.0), -5.0 * tProgress);
90 | vec4 quatY = quatFromAxisAngle(vec3(0.0, 1.0, 0.0), -5.0 * tProgress);
91 | vec3 basePosition = rotateVector(quatX, rotateVector(quatY, position));
92 | vec3 newPosition = bezier4(aPositionStart, aControlPointOne, aControlPointTwo, aPositionEnd, tProgress);
93 | float scale = tProgress * 2.0 - 1.0;
94 | scale = 1.0 - scale * scale;
95 | basePosition *= scale;
96 | gl_Position = basePosition + newPosition;
97 | ${rainbow ? `vColor = aColor;` : ``}
98 | }
99 | `;
100 |
101 | const fragment = rainbow
102 | ? [
103 | ['#define PHONG', 'varying vec3 vColor;'],
104 | ['vec4( diffuse, opacity )', 'vec4( vColor, opacity )'],
105 | ['vec3 totalEmissiveRadiance = emissive;', 'vec3 totalEmissiveRadiance = vColor;'],
106 | ]
107 | : [];
108 |
109 | const instance = new THREE.Phenomenon({
110 | attributes,
111 | uniforms,
112 | vertex,
113 | geometry,
114 | multiplier,
115 | material,
116 | fragment,
117 | });
118 |
119 | scene.add(instance.mesh);
120 | return instance;
121 | }
122 |
123 | const renderer = new THREE.WebGLRenderer({
124 | antialias: true,
125 | });
126 |
127 | renderer.setClearColor(0x212121, 0);
128 | renderer.setSize(window.innerWidth, window.innerHeight);
129 | renderer.setPixelRatio(window.devicePixelRatio >= 2 ? 2 : 1);
130 |
131 | document.body.appendChild(renderer.domElement);
132 |
133 | const scene = new THREE.Scene();
134 |
135 | const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 10000);
136 | camera.position.set(0, 20 * 1, 35 * 1);
137 | camera.lookAt(scene.position);
138 | scene.add(camera);
139 |
140 | const ambientLight = new THREE.AmbientLight('#ffffff', 0.1);
141 | scene.add(ambientLight);
142 |
143 | const light = new THREE.SpotLight(0xffffff, 1, 80, Math.PI * 0.25, 1, 2);
144 | light.position.set(0, 40, 0);
145 |
146 | scene.add(light);
147 |
148 | function render() {
149 | renderer.render(scene, camera);
150 | }
151 |
152 | function hcfp(percent) {
153 | return `#${new THREE.Color().setHSL(percent, 0.5, 0.5).getHexString()}`;
154 | }
155 |
156 | const instances = [
157 | createInstance({
158 | geometry: new THREE.IcosahedronGeometry(1, 0),
159 | material: new THREE.MeshPhongMaterial({
160 | emissive: hcfp(0),
161 | specular: '#efefef',
162 | shininess: 20,
163 | flatShading: true,
164 | }),
165 | multiplier: 200,
166 | duration: 0.7,
167 | points: [
168 | () => getArrayWithNoise([0, 0, 0], 10),
169 | () => getArrayWithNoise([0, 0, 0], 8),
170 | () => getArrayWithNoise([0, 0, 0], 2),
171 | () => getArrayWithNoise([0, 0, 0], 12),
172 | ],
173 | }),
174 | createInstance({
175 | geometry: new THREE.BoxGeometry(1, 1, 1),
176 | material: new THREE.MeshPhongMaterial({
177 | emissive: hcfp(0.5 / 3),
178 | specular: '#efefef',
179 | shininess: 20,
180 | flatShading: true,
181 | }),
182 | multiplier: 200,
183 | duration: 0.4,
184 | points: [
185 | () => getArrayWithNoise([-10, 0, 0], 4),
186 | () => getArrayWithNoise([-2.5, -10, 0], 4),
187 | () => getArrayWithNoise([2.5, 10, 0], 4),
188 | () => getArrayWithNoise([10, 0, 0], 4),
189 | ],
190 | }),
191 | createInstance({
192 | geometry: new THREE.TetrahedronGeometry(1),
193 | material: new THREE.MeshPhongMaterial({
194 | emissive: hcfp(1.5 / 3),
195 | specular: '#efefef',
196 | shininess: 20,
197 | flatShading: true,
198 | }),
199 | multiplier: 400,
200 | duration: 0.4,
201 | points: [
202 | () => getArrayWithNoise([0, 10, 0], 10),
203 | () => getArrayWithNoise([0, 0, 0], 10),
204 | () => getArrayWithNoise([0, 0, 0], 10),
205 | () => getArrayWithNoise([0, -10, 0], 10),
206 | ],
207 | }),
208 | createInstance({
209 | rainbow: true,
210 | geometry: new THREE.IcosahedronGeometry(0.8, 3),
211 | material: new THREE.MeshPhongMaterial({
212 | emissive: hcfp(2 / 3),
213 | specular: '#efefef',
214 | shininess: 20,
215 | flatShading: true,
216 | }),
217 | multiplier: 400,
218 | duration: 0.4,
219 | points: [
220 | () => getArrayWithNoise([0, -20, 0], 0),
221 | () => getArrayWithNoise([0, 10, 0], 0),
222 | () => getArrayWithNoise([0, -10, 0], 10),
223 | () => getArrayWithNoise([0, 15, 0], 15),
224 | ],
225 | }),
226 | ];
227 |
228 | const headings = document.querySelectorAll('.heading');
229 | const header = document.querySelector('.header');
230 |
231 | uos(0, 1, p => render());
232 |
233 | uos(0, 0.05, p => (header.style.opacity = 1 - p));
234 |
235 | const step = 1 / instances.length;
236 | for (let i = 0; i < instances.length; i += 1) {
237 | const transitionBegin = i * step;
238 | const transitionEnd = transitionBegin + step * 0.5;
239 | const textEnd = (i + 1) * step;
240 | uos(transitionBegin, transitionEnd, p => (instances[i].uniforms.time.value = p));
241 | uos(transitionEnd, textEnd, p => {
242 | let np = p * 2.0 - 1.0;
243 | np = 1.0 - np * np;
244 | headings[i].style.opacity = i === instances.length - 1 ? p * 1.5 : np * 1.5;
245 | });
246 | }
247 |
248 | function resize() {
249 | camera.aspect = window.innerWidth / window.innerHeight;
250 | camera.updateProjectionMatrix();
251 | renderer.setSize(window.innerWidth, window.innerHeight);
252 | const divs = document.querySelectorAll('.heading');
253 | for (let i = 0; i < divs.length; i += 1) {
254 | divs[i].style.height = `${window.innerHeight}px`;
255 | }
256 | render();
257 | }
258 |
259 | requestAnimationFrame(() => {
260 | window.scrollTo(0, 0);
261 | resize();
262 | header.style.opacity = 1;
263 | });
264 |
265 | window.addEventListener('resize', resize, false);
266 |
--------------------------------------------------------------------------------