Last update: May 3, 2020

How to use Three.js with Oxygen

A COOL 3D EFFECT
MADE WITH THREE.JS

Three.js is the most popular javascript library to display 3D animations.
You don't need to be an expert in JS or in 3D to be able to make your own animation.

This effect is actually based on a demo I've coded few years ago: http://www.wab.com/?screen=447

Let's start by using the exact same code they provide in the official documentation:
threejs / Creating-a-scene

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var geometry = new THREE.BoxGeometry();
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

camera.position.z = 5;

var animate = function() {
    requestAnimationFrame(animate);

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;

    renderer.render(scene, camera);
};

animate();

If this code will work fine, it will show the animation in the full page.
We need to do some adjustments so we can show the animation in a Div instead.

Step 1

Go to this page to download the ZIP file: threejs.org  then upload and enqueue the JS file :

wp_enqueue_script( 'three-js', plugin_dir_url( __FILE__ ) . 'assets/js/three.min.js', '', '', true );

[ see HOWTO to learn how to add custom files ]

Step 2

Add a Div and change his width to 100% and his height to 300px.

Step 3

In a Code Block, add this Javascript (in the first line, replace the ID by the ID of your Div):

var container = document.getElementById('div_block-10-994');
var w = container.offsetWidth;
var h = container.offsetHeight;

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 10000);

var renderer = new THREE.WebGLRenderer();
renderer.setSize(w,h);
container.appendChild(renderer.domElement);

var geometry = new THREE.BoxGeometry();
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

camera.position.z = 5;

var animate = function() {
    requestAnimationFrame(animate);

    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;

    renderer.render(scene, camera);
};

animate();

Do you see the green cube moving?
It doesn't look particularly appealing but it's their example and it works now in our own Div.

LET THE FUN BEGIN!

1 - Replace the object

We don't need a cube but a simple plane:

threejs / PlaneGeometry

We can then remove the cube first:

var geometry = new THREE.BoxGeometry();
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

And we add a plane instead. We also change the camera position so it can be visible:

var geometry = new THREE.PlaneGeometry(w,h);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var plane = new THREE.Mesh(geometry, material);
scene.add(plane);

camera.position.z = 500;

Our plane is now a huge rectangle, with the same size as our Div (notice the 'w' and 'h').

We have to remove the animation, because the cube is not here anymore:

  //cube.rotation.x += 0.01;
  //cube.rotation.y += 0.01;

2 - Add a texture

threejs / Texture

First we have to upload the PNG file, then simply add the first line, and change the material, so it's not green anymore:

var texture = new THREE.TextureLoader().load( "/wp-content/uploads/2020/05/texture1.png" );

var material = new THREE.MeshBasicMaterial({  map:texture });

It works as expected but it's not the result we want.

We have to change the wrap mode and repeat the texture across the surface, to make a tiling effect.

var texture = new THREE.TextureLoader().load( "/wp-content/uploads/2020/05/texture1.png" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( w/100, h/100 );

Looks better now.
As we have to repeat a square texture, in a rectangle, we have to base our value with the width and the height of the object.

3 - Animate the texture

The trick is to animate the texture instead of the object.
And to animate a texture, we simply have to change his offset. In x or/and in y:

var animate = function () {
  requestAnimationFrame(animate);

  texture.offset.x+=0.01;
  texture.offset.y+=0.01;

  renderer.render(scene, camera);
};

That's it for the animation. The only thing left to do, is to change the view.
We rotate the object and we change the camera view, to be a bit closer.

Note that as the values have to be specified in radians, we can still write them in degrees (easier to deal with) and convert them with this simple formula:

plane.rotation.x = -15*Math.PI/180;
plane.rotation.y = 10*Math.PI/180;
plane.updateMatrix();
plane.matrixAutoUpdate  = false;

camera.position.z = 100;

The 2 lines about the matrix are simply here because our object is static (it doesn't move) so we can disable the autoupdate:
threejs / How-to-update-things

The full code :

var container = document.getElementById('div_block-18-994');
var w = container.offsetWidth;
var h = container.offsetHeight;

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 10000);

var renderer = new THREE.WebGLRenderer();
renderer.setSize(w,h);
container.appendChild(renderer.domElement);
  
var texture = new THREE.TextureLoader().load( "/wp-content/uploads/2020/05/texture1.png" );
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( w/100, h/100 );

var geometry = new THREE.PlaneGeometry(w,h);
var material = new THREE.MeshBasicMaterial({  map:texture });  
var plane = new THREE.Mesh(geometry, material);
scene.add(plane);

plane.rotation.x = -15*Math.PI/180;
plane.rotation.y = 10*Math.PI/180;
plane.updateMatrix();
plane.matrixAutoUpdate  = false;
  
camera.position.z = 100;

var animate = function () {
  requestAnimationFrame(animate);

  texture.offset.x+=0.01;
  texture.offset.y+=0.01;

  renderer.render(scene, camera);
};

animate();

Note

Working in the Code Block is fine, but it can be a bit annoying each time you update the code.
To fix this problem, here is a very nice tip from Alexander Buzmakov:
https://oxywp.com/en/how-do-i-prevent-certain-javascript-from-running-inside-the-oxygen-editor/

Next tutorial :

How to make our animation responsive (kind of).

closealign-justifychevron-downcaret-up