├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── css
└── base.css
├── dist
├── base.e3190653.css
├── base.e3190653.css.map
├── favicon.2e5f6236.ico
├── index.html
├── index2.8108f201.js
├── index2.8108f201.js.map
├── index2.html
├── index3.51e7060d.js
├── index3.51e7060d.js.map
├── index3.html
├── noise.0c992c6e.png
├── scripts.3bcec604.js
└── scripts.3bcec604.js.map
├── favicon.ico
├── img
└── noise.png
├── index.html
├── index2.html
├── index3.html
├── package.json
└── scripts
├── gl
├── Blob.js
├── index.js
└── shaders
│ ├── fragment.glsl
│ └── vertex.glsl
├── index.js
├── index2.js
└── index3.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.cache
3 | package-lock.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2009 - 2020 [Codrops](https://tympanus.net/codrops)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blobs with Three.js
2 |
3 | Demos for the tutorial on how to deform and color spheres in Three.js. By [Mario Carrillo](https://twitter.com/marioecg).
4 |
5 | 
6 |
7 | [Article on Codrops](https://tympanus.net/codrops/?p=52932)
8 |
9 | [Demo](http://tympanus.net/Tutorials/WebGLBlobs/)
10 |
11 |
12 | ## Installation
13 |
14 | Install dependencies:
15 |
16 | ```
17 | npm install
18 | ```
19 |
20 | Compile the code for development and start a local server:
21 |
22 | ```
23 | npm start
24 | ```
25 |
26 | Create the build:
27 |
28 | ```
29 | npm run build
30 | ```
31 |
32 | ## Misc
33 |
34 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [GitHub](https://github.com/codrops), [Instagram](https://www.instagram.com/codropsss/)
35 |
36 | ## License
37 | [MIT](LICENSE)
38 |
39 | Made with :blue_heart: by [Codrops](http://www.codrops.com)
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/css/base.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | font-size: 15px;
9 | }
10 |
11 | body {
12 | margin: 0;
13 | --color-text: #d8d8d8;
14 | --color-bg: #060606;
15 | --color-link: #fff;
16 | --color-link-hover: #fff;
17 | --color-line: rgba(82,77,73,0.38);
18 | color: var(--color-text);
19 | background-color: var(--color-bg);
20 | font-family: termina, sans-serif;
21 | -webkit-font-smoothing: antialiased;
22 | -moz-osx-font-smoothing: grayscale;
23 | }
24 |
25 | .demo-2 {
26 | --color-text: #000000;
27 | --color-bg: #c7c7c7;
28 | }
29 |
30 | /* Page Loader */
31 | .js .loading::before,
32 | .js .loading::after {
33 | content: '';
34 | position: fixed;
35 | z-index: 1000;
36 | }
37 |
38 | .js .loading::before {
39 | top: 0;
40 | left: 0;
41 | width: 100%;
42 | height: 100%;
43 | background: var(--color-bg);
44 | }
45 |
46 | .js .loading::after {
47 | top: 50%;
48 | left: 50%;
49 | width: 60px;
50 | height: 60px;
51 | margin: -30px 0 0 -30px;
52 | border-radius: 50%;
53 | opacity: 0.4;
54 | background: var(--color-link);
55 | animation: loaderAnim 0.7s linear infinite alternate forwards;
56 |
57 | }
58 |
59 | @keyframes loaderAnim {
60 | to {
61 | opacity: 1;
62 | transform: scale3d(0.5,0.5,1);
63 | }
64 | }
65 |
66 | a {
67 | text-decoration: underline;
68 | color: var(--color-link);
69 | outline: none;
70 | }
71 |
72 | a:hover,
73 | a:focus {
74 | color: var(--color-link-hover);
75 | outline: none;
76 | text-decoration: none;
77 | }
78 |
79 | main {
80 | text-align: center;
81 | padding: 2rem;
82 | }
83 |
84 | /* Grainy texture animation by Geoff Graham https://css-tricks.com/snippets/css/animated-grainy-texture/ */
85 |
86 | main::before {
87 | animation: grain 8s steps(10) infinite;
88 | background-image: url(../img/noise.png);
89 | content: '';
90 | height: 300%;
91 | left: -50%;
92 | opacity: 0.5;
93 | position: fixed;
94 | top: -100%;
95 | width: 300%;
96 | pointer-events: none;
97 | }
98 |
99 | @keyframes grain {
100 | 0%, 100% { transform:translate(0, 0); }
101 | 10% { transform:translate(-5%, -10%); }
102 | 20% { transform:translate(-15%, 5%); }
103 | 30% { transform:translate(7%, -25%); }
104 | 40% { transform:translate(-5%, 25%); }
105 | 50% { transform:translate(-15%, 10%); }
106 | 60% { transform:translate(15%, 0%); }
107 | 70% { transform:translate(0%, 15%); }
108 | 80% { transform:translate(3%, 35%); }
109 | 90% { transform:translate(-10%, 10%); }
110 | }
111 |
112 | .logo {
113 | grid-area: logo;
114 | text-decoration: none;
115 | font-size: 3rem;
116 | font-weight: 700;
117 | align-self: center;
118 | justify-self: center;
119 | }
120 |
121 | .page-title {
122 | grid-area: pagetitle;
123 | margin: 0;
124 | font-weight: 600;
125 | font-size: 1rem;
126 | padding: 1rem 0;
127 | }
128 |
129 | .page-title div {
130 | clip-path: polygon(0 0, var(--clip) 0, var(--clip) 100%, 0% 100%);
131 | }
132 |
133 | .demos {
134 | grid-area: demos;
135 | }
136 |
137 | .links {
138 | grid-area: links;
139 | padding-top: 1rem;
140 | }
141 |
142 | .demos div:not(:first-child),
143 | .links div:not(:first-child) {
144 | margin-left: 1rem;
145 | }
146 |
147 | .frame__demo {
148 | display: inline-block;
149 | }
150 |
151 | .frame__demo--current {
152 | opacity: 0.8;
153 | text-decoration: none;
154 | }
155 |
156 | .title {
157 | grid-area: title;
158 | font-family: dystopian, sans-serif;
159 | font-weight: 700;
160 | font-size: 11.25vw;
161 | margin: 0;
162 | line-height: 0.9;
163 | text-indent: -0.9vw;
164 | }
165 |
166 | .subtitle {
167 | grid-area: subtitle;
168 | font-size: 4vw;
169 | margin: 0.5rem 0 0 0;
170 | line-height: 1;
171 | font-weight: 200;
172 | text-indent: -0.4vw;
173 | }
174 |
175 | .menu {
176 | grid-area: menu;
177 | align-self: start;
178 | }
179 |
180 | .menu__inner {
181 | font-family: dystopian, sans-serif;
182 | font-weight: 700;
183 | line-height: 1;
184 | font-size: 1.5rem;
185 | padding: 1rem 0;
186 | display: block;
187 | }
188 |
189 | .content {
190 | margin: 0;
191 | font-size: 1.15rem;
192 | font-size: clamp(1rem, 2vh, 3rem);
193 | grid-area: content;
194 | padding: 2rem 0;
195 | }
196 |
197 | .content span {
198 | clip-path: polygon(0 0, 100% 0, 100% var(--clip), 0% var(--clip));
199 | }
200 |
201 | .play {
202 | grid-area: play;
203 | font-size: 12vw;
204 | align-self: center;
205 | justify-self: center;
206 | cursor: default;
207 | display: block;
208 | }
209 |
210 | .year {
211 | grid-area: year;
212 | }
213 |
214 | .credits--site {
215 | grid-area: credits-1;
216 | font-weight: 600;
217 | text-decoration: none;
218 | }
219 |
220 | .credits--author {
221 | grid-area: credits-2;
222 | }
223 |
224 | .credits--author div {
225 | clip-path: polygon(0 0, var(--clip) 0, var(--clip) 100%, 0% 100%);
226 | }
227 |
228 | @media screen and (min-width: 60em) {
229 | main {
230 | text-align: left;
231 | padding: 0;
232 | overflow: hidden;
233 | height: 100vh;
234 | display: grid;
235 | grid-template-columns: 8rem 12vh 9rem 1fr 1fr;
236 | grid-template-rows: 4rem 4rem min-content min-content 1fr 2.5rem;
237 | grid-template-areas: 'logo ... ... pagetitle links'
238 | 'logo ... ... demos ...'
239 | '... ... ... title title'
240 | '... ... ... ... subtitle'
241 | '... menu ... content play'
242 | 'year ... credits-1 credits-2 ...';
243 | }
244 | .line {
245 | position: relative;
246 | }
247 |
248 | .line::before {
249 | content: '';
250 | position: absolute;
251 | background: var(--color-line);
252 | }
253 |
254 | .line--vertical::before {
255 | left: 0;
256 | width: 1px;
257 | height: 500vh;
258 | top: -250vh;
259 | }
260 |
261 | .line--horizontal::before {
262 | left: -250vw;
263 | width: 500vw;
264 | top: 0;
265 | height: 1px;
266 | }
267 |
268 | .content {
269 | width: 90%;
270 | padding: 0;
271 | }
272 |
273 | .menu__inner {
274 | writing-mode: vertical-lr;
275 | transform: rotate(180deg);
276 | font-size: 12vh;
277 | padding: 0;
278 | }
279 |
280 | }
281 |
282 | .webgl {
283 | position: fixed;
284 | top: 0;
285 | left: 0;
286 | z-index: -1;
287 | }
288 |
289 | /* Style class utilities */
290 | .oh {
291 | overflow: hidden;
292 | }
293 |
294 | .dib {
295 | display: inline-block;
296 | }
297 |
298 | .db {
299 | display: block;
300 | }
--------------------------------------------------------------------------------
/dist/base.e3190653.css:
--------------------------------------------------------------------------------
1 | *,:after,:before{box-sizing:border-box}:root{font-size:15px}body{margin:0;--color-text:#d8d8d8;--color-bg:#060606;--color-link:#fff;--color-link-hover:#fff;--color-line:rgba(82,77,73,0.38);color:var(--color-text);background-color:var(--color-bg);font-family:termina,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.demo-2{--color-text:#000;--color-bg:#c7c7c7}.js .loading:after,.js .loading:before{content:"";position:fixed;z-index:1000}.js .loading:before{top:0;left:0;width:100%;height:100%;background:var(--color-bg)}.js .loading:after{top:50%;left:50%;width:60px;height:60px;margin:-30px 0 0 -30px;border-radius:50%;opacity:.4;background:var(--color-link);animation:loaderAnim .7s linear infinite alternate forwards}@keyframes loaderAnim{to{opacity:1;transform:scale3d(.5,.5,1)}}a{text-decoration:underline;color:var(--color-link);outline:none}a:focus,a:hover{color:var(--color-link-hover);outline:none;text-decoration:none}main{text-align:center;padding:2rem}main:before{animation:grain 8s steps(10) infinite;background-image:url(/noise.0c992c6e.png);content:"";height:300%;left:-50%;opacity:.5;position:fixed;top:-100%;width:300%;pointer-events:none}@keyframes grain{0%,to{transform:translate(0)}10%{transform:translate(-5%,-10%)}20%{transform:translate(-15%,5%)}30%{transform:translate(7%,-25%)}40%{transform:translate(-5%,25%)}50%{transform:translate(-15%,10%)}60%{transform:translate(15%)}70%{transform:translateY(15%)}80%{transform:translate(3%,35%)}90%{transform:translate(-10%,10%)}}.logo{grid-area:logo;text-decoration:none;font-size:3rem;font-weight:700;align-self:center;justify-self:center}.page-title{grid-area:pagetitle;margin:0;font-weight:600;font-size:1rem;padding:1rem 0}.page-title div{clip-path:polygon(0 0,var(--clip) 0,var(--clip) 100%,0 100%)}.demos{grid-area:demos}.links{grid-area:links;padding-top:1rem}.demos div:not(:first-child),.links div:not(:first-child){margin-left:1rem}.frame__demo{display:inline-block}.frame__demo--current{opacity:.8;text-decoration:none}.title{grid-area:title;font-family:dystopian,sans-serif;font-weight:700;font-size:11.25vw;margin:0;line-height:.9;text-indent:-.9vw}.subtitle{grid-area:subtitle;font-size:4vw;margin:.5rem 0 0;line-height:1;font-weight:200;text-indent:-.4vw}.menu{grid-area:menu;align-self:start}.menu__inner{font-family:dystopian,sans-serif;font-weight:700;line-height:1;font-size:1.5rem;padding:1rem 0;display:block}.content{margin:0;font-size:1.15rem;font-size:clamp(1rem,2vh,3rem);grid-area:content;padding:2rem 0}.content span{clip-path:polygon(0 0,100% 0,100% var(--clip),0 var(--clip))}.play{grid-area:play;font-size:12vw;align-self:center;justify-self:center;cursor:default;display:block}.year{grid-area:year}.credits--site{grid-area:credits-1;font-weight:600;text-decoration:none}.credits--author{grid-area:credits-2}.credits--author div{clip-path:polygon(0 0,var(--clip) 0,var(--clip) 100%,0 100%)}@media screen and (min-width:60em){main{text-align:left;padding:0;overflow:hidden;height:100vh;display:grid;grid-template-columns:8rem 12vh 9rem 1fr 1fr;grid-template-rows:4rem 4rem min-content min-content 1fr 2.5rem;grid-template-areas:"logo ... ... pagetitle links" "logo ... ... demos ..." "... ... ... title title" "... ... ... ... subtitle" "... menu ... content play" "year ... credits-1 credits-2 ..."}.line{position:relative}.line:before{content:"";position:absolute;background:var(--color-line)}.line--vertical:before{left:0;width:1px;height:500vh;top:-250vh}.line--horizontal:before{left:-250vw;width:500vw;top:0;height:1px}.content{width:90%;padding:0}.menu__inner{writing-mode:vertical-lr;transform:rotate(180deg);font-size:12vh;padding:0}}.webgl{position:fixed;top:0;left:0;z-index:-1}.oh{overflow:hidden}.dib{display:inline-block}.db{display:block}
2 | /*# sourceMappingURL=/base.e3190653.css.map */
--------------------------------------------------------------------------------
/dist/base.e3190653.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["base.css"],"names":[],"mappings":"AAAA,iBAGC,qBACD,CAEA,MACC,cACD,CAEA,KACC,QAAS,CACT,oBAAqB,CACrB,kBAAmB,CACnB,iBAAkB,CAClB,uBAAwB,CACxB,gCAAiC,CACjC,uBAAwB,CACxB,gCAAiC,CACjC,8BAAgC,CAChC,kCAAmC,CACnC,iCACD,CAEA,QACC,iBAAqB,CAClB,kBACJ,CAGA,uCAEC,UAAW,CACX,cAAe,CACf,YACD,CAEA,oBACC,KAAM,CACN,MAAO,CACP,UAAW,CACX,WAAY,CACZ,0BACD,CAEA,mBACC,OAAQ,CACR,QAAS,CACT,UAAW,CACX,WAAY,CACZ,sBAAuB,CACvB,iBAAkB,CAClB,UAAY,CACZ,4BAA6B,CAC7B,2DAED,CAEA,sBACC,GACC,SAAU,CACV,0BACD,CACD,CAEA,EACC,yBAA0B,CAC1B,uBAAwB,CACxB,YACD,CAEA,gBAEC,6BAA8B,CAC9B,YAAa,CACb,oBACD,CAEA,KACC,iBAAkB,CAClB,YACD,CAIA,YACC,qCAAsC,CACtC,2DAAuC,CACvC,UAAW,CACX,WAAY,CACZ,SAAU,CACV,UAAY,CACZ,cAAe,CACf,SAAU,CACV,UAAW,CACX,mBACD,CAEA,iBACE,MAAW,sBAA2B,CACtC,IAAM,6BAAgC,CACtC,IAAM,4BAA+B,CACrC,IAAM,4BAA+B,CACrC,IAAM,4BAA+B,CACrC,IAAM,6BAAgC,CACtC,IAAM,wBAA8B,CACpC,IAAM,yBAA8B,CACpC,IAAM,2BAA8B,CACpC,IAAM,6BAAgC,CACxC,CAEA,MACC,cAAe,CACf,oBAAqB,CACrB,cAAe,CACf,eAAgB,CAChB,iBAAkB,CAClB,mBACD,CAEA,YACC,mBAAoB,CACpB,QAAS,CACT,eAAgB,CAChB,cAAe,CACf,cACD,CAEA,gBACC,4DACD,CAEA,OACC,eACD,CAEA,OACC,eAAgB,CAChB,gBACD,CAEA,0DAEC,gBACD,CAEA,aACC,oBACD,CAEA,sBACC,UAAY,CACZ,oBACD,CAEA,OACC,eAAgB,CAChB,gCAAkC,CAClC,eAAgB,CAChB,iBAAkB,CAClB,QAAS,CACT,cAAgB,CAChB,iBACD,CAEA,UACC,kBAAmB,CACnB,aAAc,CACd,gBAAoB,CACpB,aAAc,CACd,eAAgB,CAChB,iBACD,CAEA,MACC,cAAe,CACf,gBACD,CAEA,aACC,gCAAkC,CAClC,eAAgB,CACb,aAAc,CACd,gBAAiB,CACjB,cAAe,CACf,aACJ,CAEA,SACC,QAAS,CACT,iBAAkB,CAClB,8BAAiC,CACjC,iBAAkB,CAClB,cACD,CAEA,cACC,4DACD,CAEA,MACC,cAAe,CACf,cAAe,CACf,iBAAkB,CAClB,mBAAoB,CACpB,cAAe,CACf,aACD,CAEA,MACC,cACD,CAEA,eACC,mBAAoB,CACpB,eAAgB,CAChB,oBACD,CAEA,iBACC,mBACD,CAEA,qBACC,4DACD,CAEA,mCACC,KACC,eAAgB,CAChB,SAAU,CACV,eAAgB,CAChB,YAAa,CACb,YAAa,CACb,4CAA6C,CAC7C,+DAAgE,CAChE,+LAMD,CACA,MACC,iBACD,CAEA,aACC,UAAW,CACX,iBAAkB,CAClB,4BACD,CAEA,uBACC,MAAO,CACP,SAAU,CACV,YAAa,CACb,UACD,CAEA,yBACC,WAAY,CACZ,WAAY,CACZ,KAAM,CACN,UACD,CAEA,SACC,SAAU,CACV,SACD,CAEA,aACC,wBAAyB,CACzB,wBAAyB,CACzB,cAAe,CACf,SACD,CAED,CAEA,OACC,cAAe,CACf,KAAM,CACN,MAAO,CACP,UACD,CAGA,IACC,eACD,CAEA,KACC,oBACD,CAEA,IACC,aACD","file":"base.e3190653.css","sourceRoot":"..","sourcesContent":["*,\n*::after,\n*::before {\n\tbox-sizing: border-box;\n}\n\n:root {\n\tfont-size: 15px;\n}\n\nbody {\n\tmargin: 0;\n\t--color-text: #d8d8d8;\n\t--color-bg: #060606;\n\t--color-link: #fff;\n\t--color-link-hover: #fff;\n\t--color-line: rgba(82,77,73,0.38);\n\tcolor: var(--color-text);\n\tbackground-color: var(--color-bg);\n\tfont-family: termina, sans-serif;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n}\n\n.demo-2 {\n\t--color-text: #000000;\n --color-bg: #c7c7c7;\n}\n\n/* Page Loader */\n.js .loading::before,\n.js .loading::after {\n\tcontent: '';\n\tposition: fixed;\n\tz-index: 1000;\n}\n\n.js .loading::before {\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: var(--color-bg);\n}\n\n.js .loading::after {\n\ttop: 50%;\n\tleft: 50%;\n\twidth: 60px;\n\theight: 60px;\n\tmargin: -30px 0 0 -30px;\n\tborder-radius: 50%;\n\topacity: 0.4;\n\tbackground: var(--color-link);\n\tanimation: loaderAnim 0.7s linear infinite alternate forwards;\n\n}\n\n@keyframes loaderAnim {\n\tto {\n\t\topacity: 1;\n\t\ttransform: scale3d(0.5,0.5,1);\n\t}\n}\n\na {\n\ttext-decoration: underline;\n\tcolor: var(--color-link);\n\toutline: none;\n}\n\na:hover,\na:focus {\n\tcolor: var(--color-link-hover);\n\toutline: none;\n\ttext-decoration: none;\n}\n\nmain {\n\ttext-align: center;\n\tpadding: 2rem;\n}\n\n/* Grainy texture animation by Geoff Graham https://css-tricks.com/snippets/css/animated-grainy-texture/ */\n\nmain::before {\n\tanimation: grain 8s steps(10) infinite;\n\tbackground-image: url(../img/noise.png);\n\tcontent: '';\n\theight: 300%;\n\tleft: -50%;\n\topacity: 0.5;\n\tposition: fixed;\n\ttop: -100%;\n\twidth: 300%;\n\tpointer-events: none;\n}\n\n@keyframes grain {\n 0%, 100% { transform:translate(0, 0); }\n 10% { transform:translate(-5%, -10%); }\n 20% { transform:translate(-15%, 5%); }\n 30% { transform:translate(7%, -25%); }\n 40% { transform:translate(-5%, 25%); }\n 50% { transform:translate(-15%, 10%); }\n 60% { transform:translate(15%, 0%); }\n 70% { transform:translate(0%, 15%); }\n 80% { transform:translate(3%, 35%); }\n 90% { transform:translate(-10%, 10%); }\n}\n\n.logo {\n\tgrid-area: logo;\n\ttext-decoration: none;\n\tfont-size: 3rem;\n\tfont-weight: 700;\n\talign-self: center;\n\tjustify-self: center;\n}\n\n.page-title {\n\tgrid-area: pagetitle;\n\tmargin: 0;\n\tfont-weight: 600;\n\tfont-size: 1rem;\n\tpadding: 1rem 0;\n}\n\n.page-title div {\n\tclip-path: polygon(0 0, var(--clip) 0, var(--clip) 100%, 0% 100%);\n}\n\n.demos {\n\tgrid-area: demos;\n}\n\n.links {\n\tgrid-area: links;\n\tpadding-top: 1rem;\n}\n\n.demos div:not(:first-child),\n.links div:not(:first-child) {\n\tmargin-left: 1rem;\n}\n\n.frame__demo {\n\tdisplay: inline-block;\n}\n\n.frame__demo--current {\n\topacity: 0.8;\n\ttext-decoration: none;\n}\n\n.title {\n\tgrid-area: title;\n\tfont-family: dystopian, sans-serif;\n\tfont-weight: 700;\n\tfont-size: 11.25vw;\n\tmargin: 0;\n\tline-height: 0.9;\n\ttext-indent: -0.9vw;\n}\n\n.subtitle {\n\tgrid-area: subtitle;\n\tfont-size: 4vw;\n\tmargin: 0.5rem 0 0 0;\n\tline-height: 1;\n\tfont-weight: 200;\n\ttext-indent: -0.4vw;\n}\n\n.menu {\n\tgrid-area: menu;\n\talign-self: start;\n}\n\n.menu__inner {\n\tfont-family: dystopian, sans-serif;\n\tfont-weight: 700;\n line-height: 1;\n font-size: 1.5rem;\n padding: 1rem 0;\n display: block;\n}\n\n.content {\n\tmargin: 0;\n\tfont-size: 1.15rem;\n\tfont-size: clamp(1rem, 2vh, 3rem);\n\tgrid-area: content;\n\tpadding: 2rem 0;\n}\n\n.content span {\n\tclip-path: polygon(0 0, 100% 0, 100% var(--clip), 0% var(--clip));\n}\n\n.play {\n\tgrid-area: play;\n\tfont-size: 12vw;\n\talign-self: center;\n\tjustify-self: center;\n\tcursor: default;\n\tdisplay: block;\n}\n\n.year {\n\tgrid-area: year;\n}\n\n.credits--site {\n\tgrid-area: credits-1;\n\tfont-weight: 600;\n\ttext-decoration: none;\n}\n\n.credits--author {\n\tgrid-area: credits-2;\n}\n\n.credits--author div {\n\tclip-path: polygon(0 0, var(--clip) 0, var(--clip) 100%, 0% 100%);\t\n}\n\n@media screen and (min-width: 60em) {\n\tmain {\n\t\ttext-align: left;\n\t\tpadding: 0;\n\t\toverflow: hidden;\n\t\theight: 100vh;\n\t\tdisplay: grid;\n\t\tgrid-template-columns: 8rem 12vh 9rem 1fr 1fr;\n\t\tgrid-template-rows: 4rem 4rem min-content min-content 1fr 2.5rem;\n\t\tgrid-template-areas: 'logo ... ... pagetitle links'\n\t\t\t\t\t'logo ... ... demos ...'\n\t\t\t\t\t'... ... ... title title'\n\t\t\t\t\t'... ... ... ... subtitle'\n\t\t\t\t\t'... menu ... content play'\n\t\t\t\t\t'year ... credits-1 credits-2 ...';\n\t}\n\t.line {\n\t\tposition: relative;\n\t}\n\n\t.line::before {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tbackground: var(--color-line);\n\t}\n\n\t.line--vertical::before {\n\t\tleft: 0;\n\t\twidth: 1px;\n\t\theight: 500vh;\n\t\ttop: -250vh;\n\t}\n\n\t.line--horizontal::before {\n\t\tleft: -250vw;\n\t\twidth: 500vw;\n\t\ttop: 0;\n\t\theight: 1px;\n\t}\n\n\t.content {\n\t\twidth: 90%;\n\t\tpadding: 0;\n\t}\n\n\t.menu__inner {\n\t\twriting-mode: vertical-lr;\n\t\ttransform: rotate(180deg);\n\t\tfont-size: 12vh;\n\t\tpadding: 0;\n\t}\n\t\n}\n\n.webgl {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\tz-index: -1;\n}\n\n/* Style class utilities */\n.oh {\n\toverflow: hidden;\n}\n\n.dib {\n\tdisplay: inline-block;\n}\n\n.db {\n\tdisplay: block;\n}"]}
--------------------------------------------------------------------------------
/dist/favicon.2e5f6236.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/WebGLBlobs/613a5baea7c2169ab42e5a335887d5475ad96079/dist/favicon.2e5f6236.ico
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
Creative WebGL Blobs | Demo 1 | Codrops ~ Creative WebGL Blobs
Insomnia
The main reliance, however, in the Emmanuel treatment is on faith, reinforced first by hetero-suggestion and then by patient and persistent auto-suggestion. The man who would be permanently free from insomnia must be an optimist. He must have a philosophy of life wholesome enough to keep him buoyant, cheerful, and serene amid all the changes and the chances of this mortal life.
►
--------------------------------------------------------------------------------
/dist/index2.html:
--------------------------------------------------------------------------------
1 | Creative WebGL Blobs | Demo 2 | Codrops ~ Creative WebGL Blobs
Insomnia
The main reliance, however, in the Emmanuel treatment is on faith, reinforced first by hetero-suggestion and then by patient and persistent auto-suggestion. The man who would be permanently free from insomnia must be an optimist. He must have a philosophy of life wholesome enough to keep him buoyant, cheerful, and serene amid all the changes and the chances of this mortal life.
►
--------------------------------------------------------------------------------
/dist/index3.html:
--------------------------------------------------------------------------------
1 | Creative WebGL Blobs | Demo 3 | Codrops ~ Creative WebGL Blobs
Insomnia
The main reliance, however, in the Emmanuel treatment is on faith, reinforced first by hetero-suggestion and then by patient and persistent auto-suggestion. The man who would be permanently free from insomnia must be an optimist. He must have a philosophy of life wholesome enough to keep him buoyant, cheerful, and serene amid all the changes and the chances of this mortal life.
►
--------------------------------------------------------------------------------
/dist/noise.0c992c6e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/WebGLBlobs/613a5baea7c2169ab42e5a335887d5475ad96079/dist/noise.0c992c6e.png
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/WebGLBlobs/613a5baea7c2169ab42e5a335887d5475ad96079/favicon.ico
--------------------------------------------------------------------------------
/img/noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/WebGLBlobs/613a5baea7c2169ab42e5a335887d5475ad96079/img/noise.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Creative WebGL Blobs | Demo 1 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ~
18 | Creative WebGL Blobs
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Insomnia
31 |
32 |
33 | The main reliance, however, in the Emmanuel treatment is on faith, reinforced first by hetero-suggestion and then by patient and persistent auto-suggestion. The man who would be permanently free from insomnia must be an optimist. He must have a philosophy of life wholesome enough to keep him buoyant, cheerful, and serene amid all the changes and the chances of this mortal life.
34 |
35 | ►
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Creative WebGL Blobs | Demo 2 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ~
18 | Creative WebGL Blobs
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Insomnia
31 |
32 |
33 | The main reliance, however, in the Emmanuel treatment is on faith, reinforced first by hetero-suggestion and then by patient and persistent auto-suggestion. The man who would be permanently free from insomnia must be an optimist. He must have a philosophy of life wholesome enough to keep him buoyant, cheerful, and serene amid all the changes and the chances of this mortal life.
34 |
35 | ►
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/index3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Creative WebGL Blobs | Demo 3 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ~
18 | Creative WebGL Blobs
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Insomnia
31 |
32 |
33 | The main reliance, however, in the Emmanuel treatment is on faith, reinforced first by hetero-suggestion and then by patient and persistent auto-suggestion. The man who would be permanently free from insomnia must be an optimist. He must have a philosophy of life wholesome enough to keep him buoyant, cheerful, and serene amid all the changes and the chances of this mortal life.
34 |
35 | ►
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "WebGLBlobs",
3 | "version": "1.0.0",
4 | "description": "Distorted blob spheres using Three.js",
5 | "main": "index.js",
6 | "scripts": {
7 | "clean": "rm -rf dist",
8 | "start": "parcel *.html",
9 | "build": "parcel build *.html"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/codrops/WebGLBlobs.git"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/codrops/WebGLBlobs/issues"
20 | },
21 | "homepage": "https://github.com/codrops/WebGLBlobs#readme",
22 | "dependencies": {
23 | "dat.gui": "^0.7.7",
24 | "glsl-noise": "^0.0.0",
25 | "glsl-rotate": "^1.1.0",
26 | "gsap": "^3.5.1",
27 | "parcel-bundler": "^1.12.4",
28 | "three": "^0.123.0"
29 | },
30 | "devDependencies": {
31 | "glslify-bundle": "^5.1.1",
32 | "glslify-deps": "^1.3.2"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/scripts/gl/Blob.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import vertexShader from './shaders/vertex.glsl';
3 | import fragmentShader from './shaders/fragment.glsl';
4 |
5 | export default class Blob extends THREE.Object3D {
6 | constructor(size, speed, color, density, strength, offset) {
7 | super();
8 |
9 | this.geometry = new THREE.IcosahedronGeometry(size, 64);
10 | this.material = new THREE.ShaderMaterial({
11 | vertexShader,
12 | fragmentShader,
13 | uniforms: {
14 | uTime: { value: 0 },
15 | uSpeed: { value: speed },
16 | uNoiseDensity: { value: density },
17 | uNoiseStrength: { value: strength },
18 | uFreq: { value: 3 },
19 | uAmp: { value: 6 },
20 | uHue: { value: color },
21 | uOffset: { value: offset },
22 | red: { value: 0 },
23 | green: { value: 0 },
24 | blue: { value: 0 },
25 | uAlpha: { value: 1.0 },
26 | },
27 | defines: {
28 | PI: Math.PI
29 | },
30 | // wireframe: true,
31 | // side: THREE.DoubleSide
32 | transparent: true,
33 | });
34 |
35 | this.mesh = new THREE.Mesh(this.geometry, this.material);
36 |
37 | this.add(this.mesh);
38 | }
39 | }
--------------------------------------------------------------------------------
/scripts/gl/index.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
3 | import Blob from './Blob';
4 | import gsap from 'gsap';
5 |
6 | export default new class Gl {
7 | constructor() {
8 | this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
9 | this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
10 | this.renderer.setSize(window.innerWidth, window.innerHeight);
11 | this.renderer.setClearColor( 0xffffff, 0 );
12 |
13 | this.camera = new THREE.PerspectiveCamera(
14 | 45,
15 | window.innerWidth / window.innerHeight,
16 | 0.1,
17 | 1000
18 | );
19 | this.camera.position.set(0, 0, 18);
20 |
21 | this.scene = new THREE.Scene();
22 |
23 |
24 | // this.controls = new OrbitControls(this.camera, this.renderer.domElement);
25 |
26 | this.clock = new THREE.Clock();
27 |
28 | this.mouse = new THREE.Vector2();
29 | this.mouseTarget = new THREE.Vector2();
30 |
31 | this.init();
32 | this.animate();
33 | }
34 |
35 | init() {
36 | this.addCanvas();
37 | this.addEvents();
38 | }
39 |
40 | addCanvas() {
41 | const canvas = this.renderer.domElement;
42 | canvas.classList.add('webgl');
43 | document.body.appendChild(canvas);
44 | }
45 |
46 | addEvents() {
47 | window.addEventListener('resize', this.resize.bind(this));
48 | window.addEventListener('mousemove', this.mouseMove.bind(this));
49 | }
50 |
51 | resize() {
52 | let width = window.innerWidth;
53 | let height = window.innerHeight;
54 |
55 | this.camera.aspect = width / height;
56 | this.renderer.setSize(width, height);
57 |
58 | this.camera.updateProjectionMatrix();
59 | }
60 |
61 | mouseMove(e) {
62 | // Calculate mouse position in normalized device coordinates
63 | // (-1 to +1) for both components
64 | this.mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
65 | this.mouse.y = - (e.clientY / window.innerHeight) * 2 + 1;
66 | }
67 |
68 | animate() {
69 | requestAnimationFrame(this.animate.bind(this));
70 | this.render();
71 | }
72 |
73 | render() {
74 | // this.controls.update();
75 |
76 | // Remove loading class when scene has objects
77 | if (this.scene.children.length > 0) {
78 | document.body.classList.remove('loading');
79 | }
80 |
81 | // Update uniforms
82 | this.scene.children.forEach(mesh => {
83 | mesh.material.uniforms.uTime.value = this.clock.getElapsedTime();
84 | });
85 |
86 | // Lerp movement
87 | this.mouseTarget.x = gsap.utils.interpolate(this.mouseTarget.x, this.mouse.x, 0.03);
88 | this.mouseTarget.y = gsap.utils.interpolate(this.mouseTarget.y, this.mouse.y, 0.03);
89 |
90 | this.scene.rotation.set(
91 | this.mouseTarget.y * 0.25,
92 | this.mouseTarget.x * 0.25,
93 | 0
94 | );
95 |
96 | this.renderer.render(this.scene, this.camera);
97 | }
98 | }
--------------------------------------------------------------------------------
/scripts/gl/shaders/fragment.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | varying float vDistort;
3 |
4 | uniform float uTime;
5 | uniform float uHue;
6 | uniform float uAlpha;
7 |
8 | vec3 cosPalette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
9 | return a + b * cos(6.28318 * (c * t + d));
10 | }
11 |
12 | void main() {
13 | float distort = vDistort * 2.0;
14 |
15 | vec3 brightness = vec3(0.5, 0.5, 0.5);
16 | vec3 contrast = vec3(0.5, 0.5, 0.5);
17 | vec3 oscilation = vec3(1.0, 1.0, 1.0);
18 | vec3 phase = vec3(0.0, 0.1, 0.2);
19 |
20 | vec3 color = cosPalette(uHue + distort, brightness, contrast, oscilation, phase);
21 |
22 | gl_FragColor = vec4(color, uAlpha);
23 | }
--------------------------------------------------------------------------------
/scripts/gl/shaders/vertex.glsl:
--------------------------------------------------------------------------------
1 | varying vec2 vUv;
2 | varying float vDistort;
3 |
4 | uniform float uTime;
5 | uniform float uSpeed;
6 | uniform float uNoiseStrength;
7 | uniform float uNoiseDensity;
8 | uniform float uFreq;
9 | uniform float uAmp;
10 | uniform float uOffset;
11 |
12 | #pragma glslify: noise = require(glsl-noise/classic/3d)
13 | #pragma glslify: pnoise = require(glsl-noise/periodic/3d)
14 | #pragma glslify: rotateY = require(glsl-rotate/rotateY)
15 |
16 | float map(float value, float inMin, float inMax, float outMin, float outMax) {
17 | return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin);
18 | }
19 |
20 | void main() {
21 | vUv = uv;
22 |
23 | float t = uTime * uSpeed;
24 | float distortion = pnoise((normal + t) * uNoiseDensity, vec3(10.0)) * uNoiseStrength;
25 |
26 | vec3 pos = position + (normal * distortion);
27 | float angle = sin(uv.y * uFreq + t) * uAmp;
28 | pos = rotateY(pos, angle);
29 |
30 | pos *= map(sin(uTime + uOffset), -1.0, 1.0, 1.0, 1.2);
31 |
32 | vDistort = distortion;
33 |
34 | gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.);
35 | }
--------------------------------------------------------------------------------
/scripts/index.js:
--------------------------------------------------------------------------------
1 | import Gl from './gl';
2 | import Blob from './gl/Blob';
3 |
4 | import gsap from 'gsap';
5 |
6 | class App {
7 | constructor() {
8 | this.blobs = [];
9 | this.addBlobs();
10 |
11 | // Main animation tl
12 | this.tl = gsap.timeline({
13 | delay: 0.25,
14 | });
15 |
16 | this.tl
17 | .add(this.article())
18 | .add(this.animBlobs(), '-=1.5');
19 | }
20 |
21 | addBlobs() {
22 | // size, speed, color, freq, density, strength, offset
23 | const blob1 = new Blob(1.75, 0.3, 0.5, 1.5, 0.12, Math.PI * 1);
24 | const blob2 = new Blob(6.0, 0.15, 0.4, 2.0, 0.3, Math.PI * 2);
25 | const blob3 = new Blob(0.8, 0.5, 0.1, 2.0, 0.05, Math.PI * 0.5);
26 |
27 | blob1.position.set(-8.5, 3.25, 2);
28 | blob2.position.set(11, -3, -10);
29 | blob3.position.set(-1, -4, 4);
30 |
31 | blob1.rotation.set(-0.4, 0, 0.5);
32 | blob2.rotation.set(0.4, 1.0, -0.4);
33 | blob3.rotation.set(0, 0, 0);
34 |
35 | this.blobs = [blob1, blob2, blob3];
36 |
37 | Gl.scene.add(...this.blobs);
38 | }
39 |
40 | article() {
41 | // Main content
42 | const tl = gsap.timeline({
43 | defaults: {
44 | ease: 'power3.inOut',
45 | }
46 | });
47 |
48 | // Content clip
49 | const content = document.querySelector('.content span');
50 | const contentClip = { x: 0 };
51 |
52 | tl
53 | .from('.title div, .subtitle div', {
54 | duration: 2,
55 | xPercent: -100,
56 | // stagger: 0.1,
57 | })
58 | .from('.menu__inner-translate', {
59 | duration: 1.5,
60 | yPercent: -100,
61 | }, '-=1.5')
62 | .to(contentClip, {
63 | duration: 1.5,
64 | x: 100,
65 | onUpdate: () => {
66 | content.style.setProperty('--clip', `${contentClip.x}%`);
67 | },
68 | }, '-=1.25')
69 | .from('.play', {
70 | duration: 1,
71 | scale: 0,
72 | rotate: '-62deg',
73 | }, '-=1.5');
74 |
75 | return tl;
76 | }
77 |
78 | animBlobs() {
79 | // Move Threejs Blobs
80 | const tl = gsap.timeline({
81 | defaults: {
82 | duration: 2,
83 | ease: 'power3.inOut'
84 | },
85 | });
86 |
87 | const uniformAlphas = [
88 | this.blobs[0].mesh.material.uniforms.uAlpha,
89 | this.blobs[1].mesh.material.uniforms.uAlpha,
90 | this.blobs[2].mesh.material.uniforms.uAlpha,
91 | ];
92 |
93 | tl
94 | .from(this.blobs[0].position, { z: -5 })
95 | .from(this.blobs[1].position, { z: -30 }, '-=1.75')
96 | .from(this.blobs[2].position, { z: 12 }, '-=1.75')
97 | .from(uniformAlphas, {
98 | value: 0,
99 | stagger: 0.2,
100 | ease: 'power3.inOut'
101 | }, 0);
102 |
103 | return tl;
104 | }
105 | }
106 |
107 | new App();
--------------------------------------------------------------------------------
/scripts/index2.js:
--------------------------------------------------------------------------------
1 | import Gl from './gl';
2 | import Blob from './gl/Blob';
3 |
4 | import gsap from 'gsap';
5 |
6 | class App {
7 | constructor() {
8 | this.blob = null;
9 | this.addBlobs();
10 |
11 | // Main animation tl
12 | this.tl = gsap.timeline({
13 | delay: 0.25,
14 | });
15 |
16 | this.tl
17 | .add(this.article())
18 | .add(this.animBlobs(), '-=1');
19 | }
20 |
21 | addBlobs() {
22 | // size, speed, color, freq, density, strength, offset
23 | this.blob = new Blob(4.5, 0.15, 1.0, 2.0, 0.3, Math.PI * 2);
24 | this.blob.position.set(0, 0, 0);
25 | this.blob.rotation.set(0, 0, 0);
26 |
27 | Gl.scene.add(this.blob);
28 | }
29 |
30 | article() {
31 | // Main content
32 | const tl = gsap.timeline({
33 | defaults: {
34 | ease: 'power3.inOut',
35 | }
36 | });
37 |
38 | // Content clip
39 | const content = document.querySelector('.content span');
40 | const contentClip = { x: 0 };
41 |
42 | tl
43 | .from('.title div, .subtitle div', {
44 | duration: 2,
45 | xPercent: -100,
46 | // stagger: 0.1,
47 | })
48 | .from('.menu__inner-translate', {
49 | duration: 1.5,
50 | yPercent: -100,
51 | }, '-=1.5')
52 | .to(contentClip, {
53 | duration: 1.5,
54 | x: 100,
55 | onUpdate: () => {
56 | content.style.setProperty('--clip', `${contentClip.x}%`);
57 | },
58 | }, '-=1.25')
59 | .from('.play', {
60 | duration: 1,
61 | scale: 0,
62 | rotate: '-62deg',
63 | }, '-=1.5');
64 |
65 | return tl;
66 | }
67 |
68 | animBlobs() {
69 | // Move Threejs Blobs
70 | const tl = gsap.timeline({
71 | defaults: {
72 | duration: 2,
73 | ease: 'power3.inOut'
74 | },
75 | });
76 |
77 | tl
78 | .from(this.blob.position, { z: 5, })
79 | .from(this.blob.mesh.material.uniforms.uAlpha, {
80 | value: 0,
81 | stagger: 0.2,
82 | }, 0);
83 |
84 | return tl;
85 | }
86 | }
87 |
88 | new App();
--------------------------------------------------------------------------------
/scripts/index3.js:
--------------------------------------------------------------------------------
1 | import Gl from './gl';
2 | import Blob from './gl/Blob';
3 |
4 | import gsap from 'gsap';
5 |
6 | class App {
7 | constructor() {
8 | this.blobs = [];
9 | this.addBlobs();
10 |
11 | // Main animation tl
12 | this.tl = gsap.timeline({
13 | delay: 0.25,
14 | });
15 |
16 | this.tl
17 | .add(this.article())
18 | .add(this.animBlobs(), '-=1.5');
19 | }
20 |
21 | addBlobs() {
22 | // size, speed, color, freq, density, strength, offset
23 | const blob1 = new Blob(3, 0.3, 0.25, 2.0, 0.15, Math.PI * 1);
24 | const blob2 = new Blob(3, 0.25, 0.5, 1.5, 0.12, Math.PI * 0);
25 | blob1.position.set(-5, 0, 0);
26 | blob2.position.set(5, 0, 0);
27 |
28 | this.blobs = [blob1, blob2];
29 |
30 | Gl.scene.add(...this.blobs);
31 | }
32 |
33 | article() {
34 | // Main content
35 | const tl = gsap.timeline({
36 | defaults: {
37 | ease: 'power3.inOut',
38 | }
39 | });
40 |
41 | // Content clip
42 | const content = document.querySelector('.content span');
43 | const contentClip = { x: 0 };
44 |
45 | tl
46 | .from('.title div, .subtitle div', {
47 | duration: 2,
48 | xPercent: -100,
49 | // stagger: 0.1,
50 | })
51 | .from('.menu__inner-translate', {
52 | duration: 1.5,
53 | yPercent: -100,
54 | }, '-=1.5')
55 | .to(contentClip, {
56 | duration: 1.5,
57 | x: 100,
58 | onUpdate: () => {
59 | content.style.setProperty('--clip', `${contentClip.x}%`);
60 | },
61 | }, '-=1.25')
62 | .from('.play', {
63 | duration: 1,
64 | scale: 0,
65 | rotate: '-62deg',
66 | }, '-=1.5');
67 |
68 | return tl;
69 | }
70 |
71 | animBlobs() {
72 | // Move Threejs Blobs
73 | const tl = gsap.timeline();
74 |
75 | const scales = [
76 | this.blobs[0].scale,
77 | this.blobs[1].scale,
78 | ];
79 |
80 | tl
81 | .from(scales, {
82 | duration: 2,
83 | x: 0,
84 | y: 0,
85 | z: 0,
86 | ease: 'power3.inOut',
87 | stagger: 0.2,
88 | });
89 |
90 | return tl;
91 | }
92 | }
93 |
94 | new App();
--------------------------------------------------------------------------------