All files / pagIndex PagIndexCmp.tsx

58.18% Statements 32/55
66.67% Branches 6/9
21.74% Functions 5/23
56.6% Lines 30/53
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217  1x 1x 1x 1x 1x 1x 1x 1x   1x                                                                                                                     1x       1x 1x                 1x 1x                 1x   1x       1x           1x 1x     1x       1x       1x         1x           1x       1x       1x       1x         1x 1x                                                                                                                                           1x
//todo: usar service worker
//todo: ampliar test segun code coverage report y enzyme
//todo: mostrar dialogo en pagindexcmp solo una vez (usar localstorage)
//todo: añadir click de mouse (raytracer component) a boton play de video 2d control
//todo: cambiar video de canguros
//todo: reducir tamaño de imagenes
//todo: Warning: React depends on requestAnimationFrame. Make sure that you load a polyfill in older browsers. http://fb.me/react-poly
//todo: comentar en stackoverflow aframe y abrir issue en aframe-extras. hay una dependencia defectuosa
//todo: crear componente button close para dialogo y side menu
//todo: crear un component de react que sea un link con imagen (parecido al componente portal)
//todo: arreglar errores al poner atributos no html5. Por ejemplo <img crossorigin="anonymous">
//todo: gestionar mejor el estado de la entidad camara
//todo: probar a eliminar completamente tslint del proyecto
//todo: creditos
//todo: custom event polyfill
//todo: mouse cursor pointer on <a-link>
 
import * as React from 'react';
import 'aframe-orbit-controls-component-2/dist/aframe-orbit-controls-component.min';
import Loader from "../components/loader/LoaderCmp";
import Dialog from "../components/dialog/DialogCmp";
import SideMenu from "../components/sideMenu/SideMenuCmp";
import TopMenu from "../components/topMenu/TopMenuCmp";
import {SIDE_MENU_ITEMS} from "../components/sideMenu/SideMenuItems";
import './PagIndexCmp.css';
const helpIcon = require('../components/sideMenu/icons/help.svg');
const mouseMove = require('./icons/mouse-move.svg');
const mouseGesture = require('./icons/move-gesture.svg');
const mouseWheel = require('./icons/mouse-wheel.svg');
const zoomGesture = require('./icons/zoom-gesture.svg');
const urlVRviewer = 'https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=vr+viewer';
 
interface IState {
  orbitControls: {
    autoRotate?: boolean;
    target?: string;
    enableDamping?: boolean;
    dampingFactor?: number;
    rotateSpeed?: number;
    autoRotateSpeed?: number;
    zoomSpeed?: number;
    minDistance?: number;
    maxDistance?: number;
    invertZoom?: boolean;
    rotateTo?: {x: number, y: number, z: number}
  }
}
 
interface IProps {history: any, isFirstTimeVisited: boolean}
 
export class PagIndexCmp extends React.Component<IProps, IState> {
 
  public state = {
    orbitControls: {
      autoRotate: true,
      target: '#entityGroup',
      enableDamping: true,
      dampingFactor: 0.1,
      rotateSpeed: 0.1,
      autoRotateSpeed: 0.15,
      zoomSpeed: 0.5,
      minDistance: 3,
      maxDistance: 100,
      invertZoom: true,
    }
  };
 
  public refs: {loader: Loader, scene: AFrame.Entity, dialog: Dialog, sideMenu: SideMenu};
  
  public componentDidMount() {
 
    // AFrame Events must be defined in componentDidMount().
 
    const aElements = document.querySelectorAll('.position-button') as HTMLCollection;
    Array.from(aElements).forEach( (aTag: HTMLAnchorElement) => {
      aTag.addEventListener('click', (event: any) => {
        const position = event.target.dataset.position;
        //todo: revisar esto, se pueden producir estados inconsistentes
        //El estado deberia contener las coordenadas de rotacion de la camara
        this.setState( {orbitControls: {rotateTo: position}} )
      });
    });
 
    const aFrameLinks = document.querySelectorAll('a-link') as HTMLCollection;
    Array.from(aFrameLinks).forEach( (aLink: AFrame.Entity) => {
      aLink.addEventListener('mouseenter', () => {
        aLink.setAttribute('scale', {x: 1.1, y: 1.1, z: 1.1});
      });
      aLink.addEventListener('mouseleave', () => {
        aLink.setAttribute('scale', {x: 1, y: 1, z: 1})
      })
    });
 
    this.refs.loader && this.refs.loader.hideWhen(this.refs.scene, 'loaded');
 
    this.refs.scene && this.refs.scene.addEventListener('click', () => {
      this.stopAnimation();
    });
 
    Iif (window.isFirstVisit) {
      this.openDialogDelayed(2000);
      window.isFirstVisit = false;
    }
  }
 
  private objToString(component: Object): string {
    return AFRAME.utils.styleParser.stringify(component);
  }
 
  private stopAnimation(): void {
    this.setState({orbitControls: {autoRotate: false}});
  }
 
  private startAnimation(): void {
    this.setState({orbitControls: {autoRotate: true}});
  }
 
  private openDialog = () => {
    this.refs.dialog.show();
    this.closeSideMenu();
  }
 
  private openDialogDelayed = (delayTime: number) => {
    setTimeout( () => {
      this.refs.dialog.show();
    }, delayTime)
  }
 
  private closeDialog = () => {
    this.refs.dialog.hide();
  }
 
  private openSideMenu = () => {
    this.refs.sideMenu.show();
  }
 
  private closeSideMenu = () => {
    this.refs.sideMenu.hide();
  }
 
  private onClickALink = (event: any, route: string) => {
    event.preventDefault();
    this.props.history.push(route);
  }
 
  public render() {
    return (
      <div>
 
        <Loader ref="loader">Loading</Loader>
 
        <Dialog ref="dialog">
          <div className="dialog-subtitle">This is a demostration of ReactJS, AFrame and TypeScript integration.
            No real functionality has been implemented</div>
          <div className="dialog-subtitle">For a full experience, use a
            <a href={ urlVRviewer } className="dialog-link" target="_blank"> VR HeadSet.</a>
            <div>Yellow circle is the pointer when VR HeadSet is on</div>
          </div>
          <fieldset>
            <legend>Pan</legend>
            <div><img className="icon-info" src={ mouseMove } /> Mouse Pointer</div>
            <div><img className="icon-info" src={ mouseGesture } /> Hand Gesture</div>
          </fieldset>
          <fieldset>
            <legend>Zoom</legend>
            <div><img className="icon-info" src={ mouseWheel } /> Mouse Wheel</div>
            <div><img className="icon-info" src={ zoomGesture } /> Hand Gesture</div>
          </fieldset>
          <div onClick={ this.closeDialog } className="dialog-close-bottom">Close</div>
        </Dialog>
 
        <SideMenu ref="sideMenu" title="React + AFrame" items={ SIDE_MENU_ITEMS } itemActive="0">
          <img src={ helpIcon } className="icon-item" /><a href="#" onClick={ this.openDialog }>Help</a>
        </SideMenu>
 
        <TopMenu onClickMenuBtn={ this.openSideMenu }>
          <a className="top-menu-item position-button" data-position="0.17 4.14 2.79">Position 1</a>
          <a className="top-menu-item position-button" data-position="3.48 0.57 0.15">Position 2</a>
          <a className="top-menu-item position-button" data-position="-2.89 -2.51 3.20">Position 3</a>
        </TopMenu>
 
        <a-scene id="scene" ref="scene" raycaster="far: 100; objects: [link], [url]; interval: 200"
          cursor="rayOrigin: mouse">
 
          <a-assets>
            <img id="sky" src="img/1.jpg"/>
            <img id="link1" src="img/sea.jpg"/>
            <img id="link2" src="img/4.jpg"/>
            <img id="link3" src="img/7.jpg"/>
            <img id="aframeArena" src="img/aframeArena.png"/>
          </a-assets>
 
          <a-sky src="#sky" rotation="0 -90 0"/>
 
          <a-entity id="camera" camera="fov: 80; zoom: 1" position="0 -0.2 5"
            orbit-controls={ this.objToString(this.state.orbitControls) }>
            <a-entity position="0 -0.5 -3"
                    geometry="primitive: ring; radiusInner: 0.03; radiusOuter: 0.05;"
                    material="color: yellow; shader: flat"
                    cursor="maxDistance: 30; fuse: false"/>
          </a-entity>
 
          <a-entity id="entityGroup">
            <a-plane position="0 -1 0" rotation="-90 0 0" width="6" height="6" src="#aframeArena"/>
            <a-link image="#link1" onClick={ () => this.onClickALink(event, '/2dvideo') }
              href="#" title="2D/3D Video" position="-3 1 0"/>
            <a-link image="#link2" onClick={ () => this.onClickALink(event, '/360video') }
              href="#" title="360 Video" position="0 1 0"/>
            <a-link image="#link3" onClick={ () => this.onClickALink(event, '/3dmodel') }
              href="#" title="3D Model Animation" position="3 1 0"/>
          </a-entity>
 
        </a-scene>
      </div>
    )
  }
}