Code a WEBGL header

Make your site stand out by cre­at­ing an in­ter­ac­tive an­i­mated header us­ing the Three.js li­brary.

Web Designer - - Contents -

Look­ing at the kind of sites that have been win­ning ‘site of the day’ on judg­ing pan­els such as Awwwards re­cently, you will have no­ticed a trend emerg­ing. Where once a large im­age or slider gallery would sit at the top of the page, there now re­sides an­i­mated con­tent that is in­ter­ac­tive with the user and shows off a unique as­pect of what the site has on of­fer.

This trend has been grow­ing over the past couple of years and it’s al­most al­ways the min­i­mum re­quire­ment to win a ‘site of the day’.

In this tu­to­rial, an an­i­mated header will be cre­ated that will be ren­dered onto the page us­ing WEBGL with the very pop­u­lar Three.js li­brary. A web­page of con­tent will also be dis­played so that you can see how WEBGL fits with a page de­sign. Over the top will be the usual header and menu, which are found at the top of most pages; fur­ther down you will find col­umn lay­outs. The idea here is that the an­i­mated con­tent at the top is used to grab the user’s at­ten­tion, mak­ing them want to ex­plore more. To be a real con­tender for a ‘site of the day’ award, the rest of the page should con­tinue to have an­i­ma­tions and in­ter­ac­tions for the user, but that’s be­yond the scope of what we can present here. In­stead you will cre­ate a vis­ually stun­ning 3D scene that the user can in­ter­act with and will an­i­mate on the screen.

1. On your marks

Open the ‘start’ folder as the project in your code edi­tor and open the ‘in­dex.html’ file. The page has been cre­ated al­ready (you can look at it in the browser) but it’s miss­ing the dy­namic el­e­ments. Scroll down to the ‘script’ tag and add the links to the li­braries and some ex­tra helpers just above that ‘script’ tag.

<script src=”js/three.min.js”></script>

<script src=”js/col­ladaloader.js”></script> <script src=”js/shape.js”></script>

2. In­side the script

There are a couple of event lis­ten­ers in the script al­ready, which will make the mouse re­ac­tive. Above that add in the vari­ables as shown be­low. They are the global vari­ables used through­out the project to make all of the el­e­ments work to­gether. var con­tainer; var cam­era, scene, ren­derer, group, cube­cam­era, tex­ture, tex­ture2, boxes, model; var load = 0, slid­ers = []; var mou­sex = 0, mousey = 0; var win­dowhalfx = win­dow.in­ner­width / 2; var win­dowhalfy = win­dow.in­ner­height / 2;

3. Load­ing el­e­ments

The first part to get­ting things work­ing is to load in rel­e­vant tex­tures and mod­els that will be used in the project. First up is the re­flec­tion map for the tri­an­gles in the scene. The sec­ond im­age will be used in the back­ground and placed onto a ‘plane’ so that it can move with the scene. var tex­tureloader = new Three.tex­tureloader(); tex­ture = tex­tureloader.load(“as­sets/space.

jpg”, func­tion() {

tex­ture.map­ping = THREE. Equirect­an­gu­lar­reflec tion­map­ping;

loaded(); }); tex­ture2 = tex­tureloader.load(“as­sets/space. png”, func­tion() {

loaded(); });

4. Load a model

The model that is be­ing loaded in is ac­tu­ally a 3D model of text so that a tex­ture can be ap­plied to the front of the text ge­om­e­try. If this was an im­age, then an­other tex­ture would not have been able to be ap­plied to it, as that al­pha mask is go­ing to be an­i­mated when fin­ished. var loader = new Three.col­ladaloader(); loader.op­tions.con­ver­tu­paxis = true; loader.load(‘as­sets/text.dae’, func­tion(col­lada)

{ model = col­lada.scene; model.scale.x = model.scale.y = model. scale.z = 0.5; model.up­datem­atrix(); loaded(); });

5. Check­ing the load

The last im­age tex­ture is loaded and you will no­tice that af­ter each load the ‘loaded’ func­tion is called. Here you can see that. All it is do­ing is in­creas­ing the num­ber of el­e­ments that have been loaded. If those el­e­ments are equal to four then ev­ery­thing has loaded so the ‘init’ and ‘an­i­mate’ func­tions are called. var al­phamap = tex­tureloader.load(‘as­sets/ clouds. jpg’, func­tion() {

loaded(); }); func­tion loaded() { load++; if (load >= 4) { init(); an­i­mate(); } }

6. Set­ting the scene

The ‘init’ func­tion is just short for ‘ini­tialise’. This adds all the el­e­ments into the scene so that it looks cor­rect when dis­play­ing on the screen. Here the scene is set up with the right back­ground colour and then a cam­era is added to the scene to view the con­tent that will be added. func­tion init() { scene = new THREE.SCENE(); scene.back­ground = new THREE. Color(0x687576);

cam­era = new Three.per­spec­tive­cam­era(50, win­dow.in­ner­width / win­dow.in­ner­height, 1, 3000); cam­era.po­si­tion.set(0, 0, 500); scene.add(cam­era);

7. Light­ing up

In or­der to see the scene a light needs to be added and this is made from a child of the cam­era so that there is al­ways a light point­ing at the con­tent from the po­si­tion of the cam­era. Ge­om­e­try is cre­ated with a yel­low ma­te­rial; these will be an­i­mated lines mov­ing di­ag­o­nally when fin­ished. var light = new Three.point­light(0xffffff,

0.8); cam­era.add(light); var ge­om­e­try = new Three.pla­ne­ge­om­e­try(300, 5,

1, 1); var ma­te­rial = new Three.mesh­ba­sic­ma­te­rial({

color: 0xffc400, });

8. Yel­low planes

From the ma­te­rial and ge­om­e­try in the pre­vi­ous step, two planes are cre­ated and placed on the screen at dif­fer­ent po­si­tions. These lines will not stay on the screen in these places, but will be an­i­mated mov­ing di­ag­o­nally in line with their an­gle. var plane = new Three.mesh(ge­om­e­try, ma­te­rial);

plane.po­si­tion.set(180, 200, 80); plane.ro­ta­tion.z = -2.2; slid­ers.push(plane); scene.add(plane); plane = new Three.mesh(ge­om­e­try, ma­te­rial); plane.po­si­tion.set(580, 600, 80); plane.ro­ta­tion.z = -2.2; slid­ers.push(plane); scene.add(plane);

9. Adding the back­ground

The back­ground is cre­ated now by mak­ing a new plane. This is quite large as it’s go­ing to be placed be­hind all other el­e­ments. The ma­te­rial for this is cre­ated here. It uses the im­age of ‘tex­ture2’, which is a trans­par­ent PNG of stars against a painted back­ground. ge­om­e­try = new Three.pla­ne­ge­om­e­try(1600, 1000,

1, 1); ma­te­rial = new Three.mesh­ba­sic­ma­te­rial({ map: tex­ture2, opac­ity: 0.8, trans­par­ent: true });

10. Build­ing the scene

The ge­om­e­try and ma­te­rial are placed into the mesh so that it cre­ates the plane. This is placed back­wards on the z axis so it sits be­hind all other el­e­ments. Then a new group is cre­ated. This will have all of the tri­an­gu­lar shapes added so that they can be po­si­tioned all to­gether within the scene more eas­ily. plane = new Three.mesh(ge­om­e­try, ma­te­rial); plane.po­si­tion.z = -500; scene.add(plane); group = new THREE.GROUP(); scene.add(group);

11. Cre­at­ing the tri­an­gle shape

There are sev­eral mod­els that are made us­ing the tri­an­gu­lar shape here. These are cre­ated by mak­ing the outer tri­an­gle and the in­ner tri­an­gle, which is the hole to make it a com­pound path. These co-or­di­nates for the points have just been lifted from the po­si­tions of the points in Adobe Il­lus­tra­tor. var Tr­ishape = new THREE.SHAPE(); Tr­ishape.moveto(387.6, 502.2); Tr­ishape.lineto(0.0, 0.0); Tr­ishape.lineto(775.2, 0.0); Tr­ishape.lineto(387.6, 502.2); var TRIPATH = new THREE.PATH(); Tripath.moveto(115.5, 56.8); Tripath.lineto(387.6, 409.3); Tripath.lineto(659.7, 56.8); Tripath.lineto(115.5, 56.8);

12. Ex­trud­ing the shape

The set­tings here are for the ex­truded tri­an­gle shape. It sets var­i­ous prop­er­ties that will give it the right look, such as how deep the ex­tru­sion will be, the size and depth of the bev­elled front. These will be ap­plied in the next step to cre­ate two tri­an­gles on the screen. Tr­ishape.holes.push(tripath); var ex­trude­set­tings = { depth: 8, beve­len­abled: true, bevelseg­ments: 2, steps: 2, bevel­size: 1, bevelth­ick­ness: 1 };

13. Interlocking tri­an­gles

Here, two tri­an­gles are cre­ated and po­si­tioned on the screen. They are cre­ated with a cut out hole in the cen­tre and slightly ro­tated so that they in­ter­lock with each other to make it look more in­ter­est­ing. One is placed slightly higher on the screen so that they are both clearly vis­i­ble. addshape(tr­ishape, ex­trude­set­tings, 0x3e2b43, 300, 240, -50, 0, 0.1, Math.pi, 0.8); for (var i = 0; i < Tr­ishape.holes.length; i += 1) {

ad­dli­ne­shape(tr­ishape.holes[i], 0x3e2b43, 300, 240, -50, 0, 0.1, Math.pi, 0.8);

} addshape(tr­ishape, ex­trude­set­tings, 0x624562, 300, 110, 0, 0, -0.1, Math.pi, 0.8); for (var i = 0; i < Tr­ishape.holes.length; i += 1) {

ad­dli­ne­shape(tr­ishape.holes[i], 0x624562, 300, 110, 0, 0, -0.1, Math.pi, 0.8);

}

14. Box par­ti­cles

Af­ter the tri­an­gles have been added some boxes will be cre­ated on the screen. These will act like par­ti­cles and spin out­wards while fad­ing out be­fore respawn­ing in the cen­tre of the screen. A group is cre­ated to put them in and then the ba­sic ge­om­e­try is cre­ated. A ‘for’ loop makes 300 boxes each with their own unique ma­te­rial, con­tain­ing a ran­dom opac­ity. boxes = new THREE.GROUP(); scene.add(boxes); ge­om­e­try = new Three.boxbuffer­ge­om­e­try(25, 25,

25); for (var i = 0; i < 300; i++) {

var ob­ject = new Three.mesh(ge­om­e­try, new Three.mesh­ba­sic­ma­te­rial({ color: 0x000000, trans­par­ent: true, opac­ity: Math.ran­dom()

}));

15. Adding ran­dom­ness

The boxes are to move around ran­domly so they are po­si­tioned at ran­dom lo­ca­tions, then given ran­dom ro­ta­tion and ve­loc­ity so that they ap­pear unique in their

move­ment. Be­cause of the dif­fer­ent axis to ran­domise, the code ap­pears quite long, but it is very sim­ple. ob­ject.po­si­tion.x = Math.ran­dom() * 300 - 150; ob­ject.po­si­tion.y = Math.ran­dom() * 300 - 150; ob­ject.po­si­tion.z = -250; ob­ject.di­rec­tionx = Math.ran­dom() * 2 - 1; ob­ject.di­rec­tiony = Math.ran­dom() * 2 - 1; ob­ject.ro­ta­tion.x = Math.ran­dom() * 2 * Math. PI; ob­ject.ro­ta­tion.y = Math.ran­dom() * 2 * Math. PI; ob­ject.ro­ta­tion.z = Math.ran­dom() * 2 * Math.

PI;

16. Ran­dom scal­ing

The fi­nal part of the ran­domis­ing is to give each box a slightly dif­fer­ent scale value so that they ap­pear to be dif­fer­ent. Once this is com­pleted each box is added to the box group. The text model is added to the screen and its ma­te­rial is stored in a vari­able to ma­nip­u­late its al­pha map­ping. ob­ject.scale.x = Math.ran­dom() * 2 + 0.5; ob­ject.scale.y = Math.ran­dom() * 2 + 0.5; ob­ject.scale.z = Math.ran­dom() * 2 + 0.5; boxes.add(ob­ject); } scene.add(model); model.po­si­tion.set(0, 0, 100); var ma­te­rial = model.chil­dren[0].chil­dren[0]. ma­te­rial; ma­te­rial.al­phamap = al­phamap; ma­te­rial.al­phamap.mag­fil­ter = THREE.

Near­est­fil­ter

17. Text al­pha map

Be­cause the text is ge­om­e­try the al­pha map is re­peated so that it can be an­i­mated later. The move­ment of the

al­pha map across the text ge­om­e­try will give it a wispy semi-trans­par­ent look in­stead of the hard edges of the ge­om­e­try. The ren­derer is set up and added into the doc­u­ment; this ap­pears as a HTML5 Can­vas el­e­ment on the page. ma­te­rial.al­phamap.wraps = THREE.

Re­peatwrap­ping; ma­te­rial.al­phamap.wrapt = THREE.

Re­peatwrap­ping; ma­te­rial.al­phamap.re­peat.x = 1; ma­te­rial.al­phamap.re­peat.y = 1; ma­te­rial.trans­par­ent = true; ma­te­rial.al­phat­est = 0.1; ren­derer = new Three.we­bglren­derer({ an­tialias: true }); ren­derer.set­pix­el­ra­tio(win­dow. de­vi­cepix­el­ra­tio); ren­derer.set­size(win­dow.in­ner­width, win­dow. in­ner­height); doc­u­ment.body.ap­pend­child(ren­derer.domele­ment);

18. Fin­ish­ing the ‘init’ func­tion

The last parts of the ‘init’ func­tion are added, which reg­is­ters all of the event lis­ten­ers to reg­is­ter touch or mouse move­ment on the screen so that the graph­ics on screen can re­act ac­cord­ing to the user in­put. A fi­nal lis­tener checks if the screen is re­sized. The ‘an­i­mate’ func­tion calls the screen to ren­der.

doc­u­ment.ad­de­ventlis­tener(‘mouse­move’, on­doc­u­ment­mouse­move, false);

doc­u­ment.ad­de­ventlis­tener(‘touch­start’, on­doc­u­ment­touch­start, false);

doc­u­ment.ad­de­ventlis­tener(‘touch­move’, on­doc­u­ment­touch­move, false);

win­dow.ad­de­ventlis­tener(‘re­size’, on­win­dowre­size, false); } func­tion an­i­mate() { re­ques­tani­ma­tion­frame(an­i­mate); ren­der(); }

19. Ren­der­ing each frame

The an­i­ma­tion oc­curs by up­dat­ing the screen each frame. Here the ren­der func­tion takes the po­si­tion of the mouse or touch and moves the cam­era around while look­ing at the cen­tre of the screen. Then a ‘for’ loop up­dates the 300 boxes on the screen by mov­ing their po­si­tion and re­duc­ing the opac­ity each frame. func­tion ren­der() {

cam­era.po­si­tion.x += ((mou­sex / 4) cam­era.po­si­tion.x) * 0.05;

cam­era.po­si­tion.y += ((mousey / 4) cam­era.po­si­tion.y) * 0.05; cam­era.lookat(scene.po­si­tion); for (var i = 0; i < 300; i++) {

var ob­ject = boxes.chil­dren[i]; ob­ject.ma­te­rial.opac­ity -= 0.001; ob­ject.po­si­tion.x += ob­ject. di­rec­tionx;

ob­ject.po­si­tion.y += ob­ject. di­rec­tiony;

ob­ject.ro­ta­tion.z += Math.ran­dom() * 0.1;

20. Resetting boxes

When the boxes fade out with their opac­ity at zero, they are re­set in the cen­tre of the screen. The al­pha map on the text is up­dated so that it moves di­ag­o­nally up and to the right each frame. This is up­dated as an off­set on the ma­te­rial. if (ob­ject.ma­te­rial.opac­ity <= 0) { ob­ject.ma­te­rial.opac­ity = 0.7; ob­ject.po­si­tion.x = 0; ob­ject.po­si­tion.y = 0; } } var ma­te­ri­albg = model.chil­dren[0]. chil­dren[0]. ma­te­rial.al­phamap; var time = Date.now() * 0.00025; var ox = (time * -0.1 * ma­te­ri­albg.re­peat.x) % 1; var oy = (time * -0.1 * ma­te­ri­albg.re­peat.y) %

1; ma­te­ri­albg.off­set.set(ox, oy);

21. End of the code

Now the yel­low slid­ers are moved di­ag­o­nally across the screen. These are re­set when they are far enough off screen. Save the page and test this on a server so that all of the ma­te­ri­als and mod­els load. It won’t work as just a lo­cally opened file on your com­puter. for (var i = 0; i < slid­ers.length; i++) { slid­ers[i].po­si­tion.x -= 0.9; slid­ers[i].po­si­tion.y -= 1.2; if (slid­ers[i].po­si­tion.y < -400) { slid­ers[i].po­si­tion.y = 400; slid­ers[i].po­si­tion.x = Math.ran­dom() *

600 - 50; } } ren­derer.ren­der(scene, cam­era); }

Step 01: The page con­tent will be set up us­ing the Three. js li­brary avail­able from threejs.org. The site con­tains all of the doc­u­men­ta­tion to get you started

Step 02: The ini­tial page de­sign has been put in place and the tu­to­rial will fo­cus on cre­at­ing the in­ter­ac­tive, an­i­mated header sec­tion

Step 10: The back­ground im­age is loaded and placed onto a flat plane. As this is a trans­par­ent PNG the im­age ap­pears with trans­par­ent edges on the plane

Step 13: The tri­an­gle shapes have been cre­ated. There are sev­eral shapes for each tri­an­gle, and they are in­ter­locked in their po­si­tion and placed in the scene

Step 21: Ev­ery­thing is in place – the par­ti­cles are an­i­mated, the yel­low lines are mov­ing and the al­pha map on the text is an­i­mated. The whole scene re­sponds to the user’s mouse move­ment

Step 17: The text is placed in the scene and the al­pha map is added to give a soft edge to the text and al­low con­tent be­hind to be seen through.

Newspapers in English

Newspapers from UK

© PressReader. All rights reserved.