Include three.js

Hi! :slightly_smiling:

I’m trying to include three.js into my Ionic 2 project.

I’ve ran npm install three and it was successfully installed in my node_modules folder, but when I try to import it in my .ts file with import THREE from 'three/three'; it gives me the following error message:

Cannot find module ‘three/three’.

I’ve also changed my package.json file and added "three.js": "0.74.0" to the dependencies.

So what do I have to do? Is my import statement wrong or did I’ve make any other mistake?

Hi, i’ve made a starter project using ionic2 and threejs:


Feel free to ask if you have any questions :slight_smile:

I am also trying to include threejs with ionic2 and typescript without success.

I did:

npm install three@0.81 --save
npm install @types/three --save-dev --save-exact

Then in my page I have:

import { Component } from '@angular/core';
import * as THREE from 'three';
@Component({
  selector: 'page-viewer',
  templateUrl: 'viewer.html'
})
export class PageViewer {
    constructor() {
        let scene = new THREE.Scene();
    }
}

But I get the following error:

rollup: Export 'Scene' is not defined by '/MY_APP_PATH/app/src/pages/viewer/viewer.ts'

And:

EXCEPTION: Error in ./PageViewer class PageViewer_Host - inline template:0:0 caused by: THREE.Scene is not a constructor

Have you been able to use treejs with typescript?
Anyone else?

Jerome.

Rollup hates wildcard imports. I’m not familiar with this library, but if it has a default export, try importing that (see this for more information). If that doesn’t work, try switching bundlers to webpack, which is more forgiving.

1 Like

Thank you rapropos.

At the bottom of the file node_modules/@types/three/index.d.ts I have:

declare module "three" {
    export = THREE;
}

But when I use: import { THREE } from 'three';
I get: Module '"three"' has no exported member 'THREE'

When I use: import THREE from 'three';
I get: ORIGINAL EXCEPTION: THREE is undefined

When I use: import { Scene } from 'three';
I get: bundle failed: 'Scene' is not exported by node_modules/three/build/three.js

Could it be that the type definitions are not loaded ? Do I have to do something ? Can I make sure they are ?

I will investigate webpack.

webpack + import * as THREE from 'three'; did the trick. Thanks.

Quick question, were you able to include files from the Examples folder from three?

I am using ionic2 and typescript 2. I tried a lot of things, and the thing that is close to work is this:

Installed imports-loader, created a three-loader.js:

const THREE = require('three');

window.THREE = THREE;

require('imports-loader?THREE=three!three/examples/js/renderers/CanvasRenderer');


module.export = THREE;

And imported it in my page.ts, with:

require('../../three-loader');

But, even if it bundles correctly the CanvasRenderer, Three.js is using it’s own function which is deprecated, as the CanvasRenderer has been externalized.
(But if I take a look in the generated main.js, with all the libraries… I can see that both functions are present: The one that comes from the build version of Three.js and the one injected by loading CanvasRenderer.

Only, THREE.CanvasRenderer does not reference the loaded CanvasRendererer module.

Any ideas?

PS: This is my TS Code (it’s just a three.js example ported to TypeScript variables, and, except for the CanvasRenderer Issue, it should work…)

import {Component} from '@angular/core';

import {MenuController, NavController} from 'ionic-angular';

import {WelcomePage} from '../welcome/welcome';

import {TranslateService} from 'ng2-translate/ng2-translate';


require('../../three-loader');

export interface Slide {
    title: string;
    description: string;
    image: string;
}

@Component({
    selector: 'page-tutorial',
    templateUrl: 'tutorial.html'
})
export class TutorialPage {
    slides: Slide[];
    showSkip = true;

    SEPARATION: number = 100;
    AMOUNTX: number = 50;
    AMOUNTY: number = 50;

    container: any;
    stats: any;
    camera: any;
    scene: any;
    renderer: any;
    particles: any;
    particle: any;
    count: number = 0;

    mouseX: number = 0;
    mouseY: number = 0;

    windowHalfX: number = window.innerWidth / 2;
    windowHalfY: number = window.innerHeight / 2;


    constructor(public navCtrl: NavController, public menu: MenuController, translate: TranslateService) {

        translate.get(["TUTORIAL_SLIDE1_TITLE",
            "TUTORIAL_SLIDE1_DESCRIPTION",
            "TUTORIAL_SLIDE2_TITLE",
            "TUTORIAL_SLIDE2_DESCRIPTION",
            "TUTORIAL_SLIDE3_TITLE",
            "TUTORIAL_SLIDE3_DESCRIPTION",
        ])
            .subscribe((values) => {
                console.log('Loaded values', values);
                this.slides = [
                    {
                        title: values.TUTORIAL_SLIDE1_TITLE,
                        description: values.TUTORIAL_SLIDE1_DESCRIPTION,
                        image: 'assets/img/ica-slidebox-img-1.png',
                    },
                    {
                        title: values.TUTORIAL_SLIDE2_TITLE,
                        description: values.TUTORIAL_SLIDE2_DESCRIPTION,
                        image: 'assets/img/ica-slidebox-img-2.png',
                    },
                    {
                        title: values.TUTORIAL_SLIDE3_TITLE,
                        description: values.TUTORIAL_SLIDE3_DESCRIPTION,
                        image: 'assets/img/ica-slidebox-img-3.png',
                    }
                ];
            });

        this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
        this.camera.position.z = 1000;

        this.scene = new THREE.Scene();

        this.particles = new Array();

        var PI2 = Math.PI * 2;
        var material = new THREE.SpriteCanvasMaterial({

            color: 0xffffff,
            program: function (context) {

                context.beginPath();
                context.arc(0, 0, 0.5, 0, PI2, true);
                context.fill();

            }

        });

        var i = 0;

        for (var ix = 0; ix < this.AMOUNTX; ix++) {

            for (var iy = 0; iy < this.AMOUNTY; iy++) {

                this.particle = this.particles[i++] = new THREE.Sprite(material);
                this.particle.position.x = ix * this.SEPARATION - ( ( this.AMOUNTX * this.SEPARATION ) / 2 );
                this.particle.position.z = iy * this.SEPARATION - ( ( this.AMOUNTY * this.SEPARATION ) / 2 );
                this.scene.add(this.particle);

            }

        }

        this.renderer = new THREE.CanvasRenderer();



        document.addEventListener('mousemove', this.onDocumentMouseMove, false);
        document.addEventListener('touchstart', this.onDocumentTouchStart, false);
        document.addEventListener('touchmove', this.onDocumentTouchMove, false);

        window.addEventListener( 'resize', this.onWindowResize, false );



        setTimeout(() => {


            this.renderer.setSize(window.innerWidth, window.innerHeight);
            //this.renderer.setPixelRatio = window.devicePixelRatio ;

            this.container = document.getElementById('threeContent');
            this.container.appendChild(this.renderer.domElement);

            this.render();

        }, 1);

    }


    onWindowResize() {

        this.windowHalfX = window.innerWidth / 2;
        this.windowHalfY = window.innerHeight / 2;

        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();

        this.renderer.setSize(window.innerWidth, window.innerHeight);

    }


    onDocumentMouseMove(event) {

        this.mouseX = event.clientX - this.windowHalfX;
        this.mouseY = event.clientY - this.windowHalfY;

    }

    onDocumentTouchStart(event) {

        if (event.touches.length === 1) {

            event.preventDefault();

            this.mouseX = event.touches[0].pageX - this.windowHalfX;
            this.mouseY = event.touches[0].pageY - this.windowHalfY;

        }

    }

    onDocumentTouchMove(event) {

        if (event.touches.length === 1) {

            event.preventDefault();

            this.mouseX = event.touches[0].pageX - this.windowHalfX;
            this.mouseY = event.touches[0].pageY - this.windowHalfY;

        }

    }

    //

    animate() {

        requestAnimationFrame(this.animate);

        this.render();
        this.stats.update();

    }

    render() {

        this.camera.position.x += ( this.mouseX - this.camera.position.x ) * .05;
        this.camera.position.y += ( -this.mouseY - this.camera.position.y ) * .05;
        this.camera.lookAt(this.scene.position);

        var i = 0;

        for (var ix = 0; ix < this.AMOUNTX; ix++) {

            for (var iy = 0; iy < this.AMOUNTY; iy++) {


                this.particle = this.particles[i++];
                this.particle.position.y = ( Math.sin(( ix + this.count ) * 0.3) * 50 ) +
                    ( Math.sin(( iy + this.count ) * 0.5) * 50 );
                this.particle.scale.x = this.particle.scale.y = ( Math.sin(( ix + this.count ) * 0.3) + 1 ) * 4 +
                    ( Math.sin(( iy + this.count ) * 0.5) + 1 ) * 4;

            }

        }

        this.renderer.render(this.scene, this.camera);

        this.count += 0.1;

    }

    startApp() {
        this.navCtrl.setRoot(WelcomePage, {}, {
            animate: true,
            direction: 'forward'
        });
    }

    onSlideChangeStart(slider) {
        this.showSkip = !slider.isEnd;
    }

    ionViewDidEnter() {
        // the root left menu should be disabled on the tutorial page
        this.menu.enable(false);
    }

    ionViewWillLeave() {
        // enable the root left menu when leaving the tutorial page
        this.menu.enable(true);
    }

}

It is a pain that this other components are not modularized. And If you have any workaround to successfully load them… that would be great.

Thanks!

I created a small demo project which includes three.js and where I display a .stl model. The inclusion of three.js was a bit of a challenge, especially since I then had to extend the THREE module/namespace using STLImporter functionality.

Check it out here: https://github.com/quickies/Ionic3D.poc