├── .babelrc
├── .gitignore
├── LICENSE
├── README.md
├── dist
├── core.js
├── index.js
└── primitives.js
├── package.json
└── src
├── core.js
├── index.js
└── primitives.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react", "stage-0"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .idea/
3 | npm-debug.log
4 | *.sw[ponm]
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 weloobe
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
react-aframe-ar
2 |
3 |
4 |
5 |
6 | Build virtual and augmented reality experiences with React and A-Frame.
7 |
8 |
9 |
10 |
11 | ## Get started
12 |
13 | - Install with [npm](https://www.npmjs.com/package/react-aframe-ar) or
14 | [yarn](https://github.com/yarnpkg/yarn).
15 |
16 | ```
17 | npm install --save aframe react-aframe-ar react react-dom
18 | yarn add aframe react-aframe-ar react react-dom
19 | ```
20 |
21 | - Basic example
22 |
23 | ```js
24 | import 'aframe';
25 | import React from 'react';
26 | import ReactDOM from 'react-dom';
27 | import {Box, Sphere, Cylinder, Plane, Sky, Text, Scene} from 'react-aframe-ar';
28 |
29 | class AppScene extends React.Component {
30 | render () {
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | );
41 | }
42 | }
43 |
44 | ReactDOM.render(, document.querySelector('#sceneContainer'));
45 | ```
46 |
47 | - Checkout [react-aframe-starter](https://github.com/tnga/react-aframe-starter) for easily boilerplate!
48 |
49 | ## More informations
50 |
51 | `react-aframe-ar` is a very thin layer on top of React to bridge with A-Frame.
52 | It passes React props to directly A-Frame using refs and `.setAttribute()`, bypassing the DOM.
53 | This works since A-Frame's `.setAttribute()`s are able to take non-string data such as objects,
54 | arrays, or elements and synchronously modify underlying 3D scene graph.
55 |
56 | ```js
57 | // react-aframe-ar's React Component
58 |
59 |
60 | // renders
61 |
62 |
63 | // and then applies the data directly to the underlying 3D scene graph, bypassing the DOM.
64 | .setAttribute('geometry', {primitive: 'box', width: 5});
65 | .setAttribute('position', '0 0 -5');
66 | ```
67 |
68 | `react-aframe-ar` provides the best of both worlds between A-Frame and React, the
69 | 3D and VR-oriented entity-component architecture of A-Frame, and the view and
70 | state management ergonomics of React, without penalties of attempting to use
71 | React for a VR application.
72 |
73 | [A-Frame](https://aframe.io) is a web framework for building virtual reality
74 | experiences. Since A-Frame is built on top of the DOM, web libraries such as
75 | React, Vue.js, Angular, Ember.js, d3.js are able to sit cleanly on top of
76 | A-Frame.
77 |
78 | A-Frame is an [entity-component-system (ECS) framework exposed through
79 | HTML](https://aframe.io/docs/). ECS is a pattern used in game development that
80 | favors composability over inheritance, which is more naturally suited to 3D
81 | scenes where objects are built of complex appearance, behavior, and
82 | functionality. In A-Frame, HTML attributes map to *components* which are
83 | composable modules that are plugged into ``s to attach appearance,
84 | behavior, and functionality.
85 |
--------------------------------------------------------------------------------
/dist/core.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Scene = exports.Entity = exports._options = undefined;
7 |
8 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9 |
10 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
11 |
12 | var _react = require('react');
13 |
14 | var _react2 = _interopRequireDefault(_react);
15 |
16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17 |
18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
19 |
20 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
21 |
22 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
23 |
24 | var _nonEntityProps = ['children', 'events', 'primitive'];
25 | var _nonStandardProp = ['className', 'id', 'mixin'];
26 | var _checkNonEntityProp = function _checkNonEntityProp(propName) {
27 | return _nonEntityProps.indexOf(propName) === -1;
28 | };
29 | var _checkStandardProp = function _checkStandardProp(propName) {
30 | return _nonStandardProp.indexOf(propName) !== -1 || propName.indexOf('data-') === 0;
31 | };
32 |
33 | var _options = {
34 | // React needs this because React serializes.
35 | // Preact does not because Preact runs `.setAttribute` on its own.
36 | runSetAttributeOnUpdates: true
37 | };
38 | exports._options = _options;
39 |
40 | /**
41 | * Render .
42 | * Tell React to use A-Frame's .setAttribute() on the DOM element
43 | * for all prop initializations and updates.
44 | */
45 |
46 | var Entity = exports.Entity = function (_React$Component) {
47 | _inherits(Entity, _React$Component);
48 |
49 | function Entity() {
50 | _classCallCheck(this, Entity);
51 |
52 | /**
53 | * In response to initial `ref` callback.
54 | */
55 | var _this = _possibleConstructorReturn(this, (Entity.__proto__ || Object.getPrototypeOf(Entity)).call(this));
56 |
57 | _this.initEntity = function (el) {
58 | if (!el) {
59 | return;
60 | }
61 |
62 | var props = _this.props;
63 | // Store.
64 | _this.el = el;
65 |
66 | // Attach events.
67 | if (props.events) {
68 | Object.keys(props.events).forEach(function (eventName) {
69 | addEventListeners(el, eventName, props.events[eventName]);
70 | });
71 | }
72 |
73 | // Update entity.
74 | updateAttributes(el, null, props);
75 |
76 | // Allow ref.
77 | if (props._ref) {
78 | props._ref(el);
79 | }
80 | };
81 | return _this;
82 | }
83 |
84 | /**
85 | * Handle updates after the initial render.
86 | */
87 |
88 |
89 | _createClass(Entity, [{
90 | key: 'componentDidUpdate',
91 | value: function componentDidUpdate(prevProps, prevState) {
92 | var el = this.el;
93 | var props = this.props;
94 |
95 | // Update events.
96 | updateEventListeners(el, prevProps.events, props.events);
97 |
98 | // Update entity.
99 | if (_options.runSetAttributeOnUpdates) {
100 | updateAttributes(el, prevProps, props);
101 | }
102 | }
103 | }, {
104 | key: 'componentWillUnmount',
105 | value: function componentWillUnmount() {
106 | var el = this.el;
107 | var props = this.props;
108 |
109 | if (props.events) {
110 | // Remove events.
111 | Object.keys(props.events).forEach(function (eventName) {
112 | removeEventListeners(el, eventName, props.events[eventName]);
113 | });
114 | }
115 | }
116 |
117 | /**
118 | * Render A-Frame DOM with ref: https://facebook.github.io/react/docs/refs-and-the-dom.html
119 | */
120 |
121 | }, {
122 | key: 'render',
123 | value: function render() {
124 | var props = this.props;
125 | var elementName = this.primitiveName || props.primitive || 'a-entity';
126 |
127 | // Let through props that are OK to render initially.
128 | var reactProps = {};
129 | Object.keys(props).forEach(function (propName) {
130 | if (_checkStandardProp(propName)) reactProps[propName] = props[propName];
131 | });
132 |
133 | return _react2.default.createElement(elementName, _extends({ ref: this.initEntity }, reactProps), props.children);
134 | }
135 | }]);
136 |
137 | return Entity;
138 | }(_react2.default.Component);
139 |
140 | /**
141 | * Render .
142 | * extends from in A-Frame so we reuse .
143 | */
144 |
145 |
146 | var Scene = exports.Scene = function (_Entity) {
147 | _inherits(Scene, _Entity);
148 |
149 | function Scene(props) {
150 | _classCallCheck(this, Scene);
151 |
152 | var _this2 = _possibleConstructorReturn(this, (Scene.__proto__ || Object.getPrototypeOf(Scene)).call(this, props));
153 |
154 | _this2.primitiveName = 'a-scene';
155 | return _this2;
156 | }
157 |
158 | return Scene;
159 | }(Entity);
160 |
161 | /**
162 | * Handle diffing of previous and current attributes.
163 | *
164 | * @param {Element} el
165 | * @param {Object|null} prevProps - Previous props map.
166 | * @param {Object} props - Current props map.
167 | */
168 |
169 |
170 | function updateAttributes(el, prevProps, props) {
171 | if (!props || prevProps === props) {
172 | return;
173 | }
174 |
175 | // Set attributes.
176 | Object.keys(props).filter(_checkNonEntityProp).forEach(function (propName) {
177 | callSetAttribute(el, props[propName], propName);
178 | });
179 |
180 | // See if attributes were removed.
181 | if (prevProps) {
182 | Object.keys(prevProps).filter(_checkNonEntityProp).forEach(function (propName) {
183 | if (props[propName] === undefined) el.removeAttribute(propName);
184 | });
185 | }
186 | }
187 |
188 | /**
189 | * Call `.setAttribute()` on the `ref`,
190 | * passing prop data directly to A-Frame.
191 | *
192 | * @param {Element} el
193 | * @param {Object|null} props - props map.
194 | * @param {Object} propName
195 | */
196 | function callSetAttribute(el, propValue, propName) {
197 | if (!propValue || !(propValue.constructor === Function)) {
198 | if (propName === 'className') propName = 'class';
199 |
200 | el.setAttribute(propName, propValue);
201 | }
202 | }
203 |
204 | /**
205 | * Handle diffing of previous and current event maps.
206 | *
207 | * @param {Element} el
208 | * @param {Object} prevEvents - Previous event map.
209 | * @param {Object} events - Current event map.
210 | */
211 | function updateEventListeners(el, prevEvents, events) {
212 | if (!prevEvents || !events || prevEvents === events) {
213 | return;
214 | }
215 |
216 | Object.keys(events).forEach(function (eventName) {
217 | // Didn't change.
218 | if (prevEvents[eventName] === events[eventName]) return;
219 |
220 | // If changed, remove old previous event listeners.
221 | if (prevEvents[eventName]) removeEventListeners(el, eventName, prevEvents[eventName]);
222 |
223 | // Add new event listeners.
224 | addEventListeners(el, eventName, events[eventName]);
225 | });
226 |
227 | // See if event handlers were removed.
228 | Object.keys(prevEvents).forEach(function (eventName) {
229 | if (!events[eventName]) removeEventListeners(el, eventName, prevEvents[eventName]);
230 | });
231 | }
232 |
233 | /**
234 | * Register event handlers for an event name to ref.
235 | *
236 | * @param {Element} el - DOM element.
237 | * @param {string} eventName
238 | * @param {array|function} eventHandlers - Handler function or array of handler functions.
239 | */
240 | function addEventListeners(el, eventName, handlers) {
241 | if (!handlers) return;
242 |
243 | // Convert to array.
244 | if (handlers.constructor === Function) handlers = [handlers];
245 |
246 | // Register.
247 | handlers.forEach(function (handler) {
248 | el.addEventListener(eventName, handler);
249 | });
250 | }
251 |
252 | /**
253 | * Unregister event handlers for an event name to ref.
254 | *
255 | * @param {Element} el - DOM element.
256 | * @param {string} eventName
257 | * @param {array|function} eventHandlers - Handler function or array of handler functions.
258 | */
259 | function removeEventListeners(el, eventName, handlers) {
260 | if (!handlers) return;
261 |
262 | // Convert to array.
263 | if (handlers.constructor === Function) handlers = [handlers];
264 |
265 | // Unregister.
266 | handlers.forEach(function (handler) {
267 | el.removeEventListener(eventName, handler);
268 | });
269 | }
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _core = require('./core');
8 |
9 | Object.keys(_core).forEach(function (key) {
10 | if (key === "default" || key === "__esModule") return;
11 | Object.defineProperty(exports, key, {
12 | enumerable: true,
13 | get: function get() {
14 | return _core[key];
15 | }
16 | });
17 | });
18 |
19 | var _primitives = require('./primitives');
20 |
21 | Object.keys(_primitives).forEach(function (key) {
22 | if (key === "default" || key === "__esModule") return;
23 | Object.defineProperty(exports, key, {
24 | enumerable: true,
25 | get: function get() {
26 | return _primitives[key];
27 | }
28 | });
29 | });
--------------------------------------------------------------------------------
/dist/primitives.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Videosphere = exports.Video = exports.Triangle = exports.Torus = exports.TorusKnot = exports.Text = exports.Tetrahedron = exports.Sphere = exports.Sound = exports.Sky = exports.Ring = exports.Plane = exports.Octahedron = exports.ObjModel = exports.Link = exports.Light = exports.Image = exports.Icosahedron = exports.gltfModel = exports.Dodecahedron = exports.Cylinder = exports.Curvedimage = exports.Cursor = exports.Cone = exports.ColladaModel = exports.Camera = exports.Box = exports.Animation = undefined;
7 |
8 | var _core = require('./core');
9 |
10 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
11 |
12 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
13 |
14 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
15 |
16 | /**
17 | * Render primitve tag as , eg: .
18 | * primitive tag extends from in A-Frame so we reuse .
19 | */
20 |
21 | var Animation = exports.Animation = function (_Entity) {
22 | _inherits(Animation, _Entity);
23 |
24 | function Animation(props) {
25 | _classCallCheck(this, Animation);
26 |
27 | var _this = _possibleConstructorReturn(this, (Animation.__proto__ || Object.getPrototypeOf(Animation)).call(this, props));
28 |
29 | _this.primitiveName = 'a-animation';
30 | return _this;
31 | }
32 |
33 | return Animation;
34 | }(_core.Entity);
35 |
36 | var Box = exports.Box = function (_Entity2) {
37 | _inherits(Box, _Entity2);
38 |
39 | function Box(props) {
40 | _classCallCheck(this, Box);
41 |
42 | var _this2 = _possibleConstructorReturn(this, (Box.__proto__ || Object.getPrototypeOf(Box)).call(this, props));
43 |
44 | _this2.primitiveName = 'a-box';
45 | return _this2;
46 | }
47 |
48 | return Box;
49 | }(_core.Entity);
50 |
51 | var Camera = exports.Camera = function (_Entity3) {
52 | _inherits(Camera, _Entity3);
53 |
54 | function Camera(props) {
55 | _classCallCheck(this, Camera);
56 |
57 | var _this3 = _possibleConstructorReturn(this, (Camera.__proto__ || Object.getPrototypeOf(Camera)).call(this, props));
58 |
59 | _this3.primitiveName = 'a-camera';
60 | return _this3;
61 | }
62 |
63 | return Camera;
64 | }(_core.Entity);
65 |
66 | var ColladaModel = exports.ColladaModel = function (_Entity4) {
67 | _inherits(ColladaModel, _Entity4);
68 |
69 | function ColladaModel(props) {
70 | _classCallCheck(this, ColladaModel);
71 |
72 | var _this4 = _possibleConstructorReturn(this, (ColladaModel.__proto__ || Object.getPrototypeOf(ColladaModel)).call(this, props));
73 |
74 | _this4.primitiveName = 'a-collada-model';
75 | return _this4;
76 | }
77 |
78 | return ColladaModel;
79 | }(_core.Entity);
80 |
81 | var Cone = exports.Cone = function (_Entity5) {
82 | _inherits(Cone, _Entity5);
83 |
84 | function Cone(props) {
85 | _classCallCheck(this, Cone);
86 |
87 | var _this5 = _possibleConstructorReturn(this, (Cone.__proto__ || Object.getPrototypeOf(Cone)).call(this, props));
88 |
89 | _this5.primitiveName = 'a-cone';
90 | return _this5;
91 | }
92 |
93 | return Cone;
94 | }(_core.Entity);
95 |
96 | var Cursor = exports.Cursor = function (_Entity6) {
97 | _inherits(Cursor, _Entity6);
98 |
99 | function Cursor(props) {
100 | _classCallCheck(this, Cursor);
101 |
102 | var _this6 = _possibleConstructorReturn(this, (Cursor.__proto__ || Object.getPrototypeOf(Cursor)).call(this, props));
103 |
104 | _this6.primitiveName = 'a-cursor';
105 | return _this6;
106 | }
107 |
108 | return Cursor;
109 | }(_core.Entity);
110 |
111 | var Curvedimage = exports.Curvedimage = function (_Entity7) {
112 | _inherits(Curvedimage, _Entity7);
113 |
114 | function Curvedimage(props) {
115 | _classCallCheck(this, Curvedimage);
116 |
117 | var _this7 = _possibleConstructorReturn(this, (Curvedimage.__proto__ || Object.getPrototypeOf(Curvedimage)).call(this, props));
118 |
119 | _this7.primitiveName = 'a-curvedimage';
120 | return _this7;
121 | }
122 |
123 | return Curvedimage;
124 | }(_core.Entity);
125 |
126 | var Cylinder = exports.Cylinder = function (_Entity8) {
127 | _inherits(Cylinder, _Entity8);
128 |
129 | function Cylinder(props) {
130 | _classCallCheck(this, Cylinder);
131 |
132 | var _this8 = _possibleConstructorReturn(this, (Cylinder.__proto__ || Object.getPrototypeOf(Cylinder)).call(this, props));
133 |
134 | _this8.primitiveName = 'a-cylinder';
135 | return _this8;
136 | }
137 |
138 | return Cylinder;
139 | }(_core.Entity);
140 |
141 | var Dodecahedron = exports.Dodecahedron = function (_Entity9) {
142 | _inherits(Dodecahedron, _Entity9);
143 |
144 | function Dodecahedron(props) {
145 | _classCallCheck(this, Dodecahedron);
146 |
147 | var _this9 = _possibleConstructorReturn(this, (Dodecahedron.__proto__ || Object.getPrototypeOf(Dodecahedron)).call(this, props));
148 |
149 | _this9.primitiveName = 'a-dodecahedron';
150 | return _this9;
151 | }
152 |
153 | return Dodecahedron;
154 | }(_core.Entity);
155 |
156 | var gltfModel = exports.gltfModel = function (_Entity10) {
157 | _inherits(gltfModel, _Entity10);
158 |
159 | function gltfModel(props) {
160 | _classCallCheck(this, gltfModel);
161 |
162 | var _this10 = _possibleConstructorReturn(this, (gltfModel.__proto__ || Object.getPrototypeOf(gltfModel)).call(this, props));
163 |
164 | _this10.primitiveName = 'a-gltf-model';
165 | return _this10;
166 | }
167 |
168 | return gltfModel;
169 | }(_core.Entity);
170 |
171 | var Icosahedron = exports.Icosahedron = function (_Entity11) {
172 | _inherits(Icosahedron, _Entity11);
173 |
174 | function Icosahedron(props) {
175 | _classCallCheck(this, Icosahedron);
176 |
177 | var _this11 = _possibleConstructorReturn(this, (Icosahedron.__proto__ || Object.getPrototypeOf(Icosahedron)).call(this, props));
178 |
179 | _this11.primitiveName = 'a-icosahedron';
180 | return _this11;
181 | }
182 |
183 | return Icosahedron;
184 | }(_core.Entity);
185 |
186 | var Image = exports.Image = function (_Entity12) {
187 | _inherits(Image, _Entity12);
188 |
189 | function Image(props) {
190 | _classCallCheck(this, Image);
191 |
192 | var _this12 = _possibleConstructorReturn(this, (Image.__proto__ || Object.getPrototypeOf(Image)).call(this, props));
193 |
194 | _this12.primitiveName = 'a-image';
195 | return _this12;
196 | }
197 |
198 | return Image;
199 | }(_core.Entity);
200 |
201 | var Light = exports.Light = function (_Entity13) {
202 | _inherits(Light, _Entity13);
203 |
204 | function Light(props) {
205 | _classCallCheck(this, Light);
206 |
207 | var _this13 = _possibleConstructorReturn(this, (Light.__proto__ || Object.getPrototypeOf(Light)).call(this, props));
208 |
209 | _this13.primitiveName = 'a-light';
210 | return _this13;
211 | }
212 |
213 | return Light;
214 | }(_core.Entity);
215 |
216 | var Link = exports.Link = function (_Entity14) {
217 | _inherits(Link, _Entity14);
218 |
219 | function Link(props) {
220 | _classCallCheck(this, Link);
221 |
222 | var _this14 = _possibleConstructorReturn(this, (Link.__proto__ || Object.getPrototypeOf(Link)).call(this, props));
223 |
224 | _this14.primitiveName = 'a-link';
225 | return _this14;
226 | }
227 |
228 | return Link;
229 | }(_core.Entity);
230 |
231 | var ObjModel = exports.ObjModel = function (_Entity15) {
232 | _inherits(ObjModel, _Entity15);
233 |
234 | function ObjModel(props) {
235 | _classCallCheck(this, ObjModel);
236 |
237 | var _this15 = _possibleConstructorReturn(this, (ObjModel.__proto__ || Object.getPrototypeOf(ObjModel)).call(this, props));
238 |
239 | _this15.primitiveName = 'a-obj-model';
240 | return _this15;
241 | }
242 |
243 | return ObjModel;
244 | }(_core.Entity);
245 |
246 | var Octahedron = exports.Octahedron = function (_Entity16) {
247 | _inherits(Octahedron, _Entity16);
248 |
249 | function Octahedron(props) {
250 | _classCallCheck(this, Octahedron);
251 |
252 | var _this16 = _possibleConstructorReturn(this, (Octahedron.__proto__ || Object.getPrototypeOf(Octahedron)).call(this, props));
253 |
254 | _this16.primitiveName = 'a-octahedron';
255 | return _this16;
256 | }
257 |
258 | return Octahedron;
259 | }(_core.Entity);
260 |
261 | var Plane = exports.Plane = function (_Entity17) {
262 | _inherits(Plane, _Entity17);
263 |
264 | function Plane(props) {
265 | _classCallCheck(this, Plane);
266 |
267 | var _this17 = _possibleConstructorReturn(this, (Plane.__proto__ || Object.getPrototypeOf(Plane)).call(this, props));
268 |
269 | _this17.primitiveName = 'a-plane';
270 | return _this17;
271 | }
272 |
273 | return Plane;
274 | }(_core.Entity);
275 |
276 | var Ring = exports.Ring = function (_Entity18) {
277 | _inherits(Ring, _Entity18);
278 |
279 | function Ring(props) {
280 | _classCallCheck(this, Ring);
281 |
282 | var _this18 = _possibleConstructorReturn(this, (Ring.__proto__ || Object.getPrototypeOf(Ring)).call(this, props));
283 |
284 | _this18.primitiveName = 'a-ring';
285 | return _this18;
286 | }
287 |
288 | return Ring;
289 | }(_core.Entity);
290 |
291 | var Sky = exports.Sky = function (_Entity19) {
292 | _inherits(Sky, _Entity19);
293 |
294 | function Sky(props) {
295 | _classCallCheck(this, Sky);
296 |
297 | var _this19 = _possibleConstructorReturn(this, (Sky.__proto__ || Object.getPrototypeOf(Sky)).call(this, props));
298 |
299 | _this19.primitiveName = 'a-sky';
300 | return _this19;
301 | }
302 |
303 | return Sky;
304 | }(_core.Entity);
305 |
306 | var Sound = exports.Sound = function (_Entity20) {
307 | _inherits(Sound, _Entity20);
308 |
309 | function Sound(props) {
310 | _classCallCheck(this, Sound);
311 |
312 | var _this20 = _possibleConstructorReturn(this, (Sound.__proto__ || Object.getPrototypeOf(Sound)).call(this, props));
313 |
314 | _this20.primitiveName = 'a-sound';
315 | return _this20;
316 | }
317 |
318 | return Sound;
319 | }(_core.Entity);
320 |
321 | var Sphere = exports.Sphere = function (_Entity21) {
322 | _inherits(Sphere, _Entity21);
323 |
324 | function Sphere(props) {
325 | _classCallCheck(this, Sphere);
326 |
327 | var _this21 = _possibleConstructorReturn(this, (Sphere.__proto__ || Object.getPrototypeOf(Sphere)).call(this, props));
328 |
329 | _this21.primitiveName = 'a-sphere';
330 | return _this21;
331 | }
332 |
333 | return Sphere;
334 | }(_core.Entity);
335 |
336 | var Tetrahedron = exports.Tetrahedron = function (_Entity22) {
337 | _inherits(Tetrahedron, _Entity22);
338 |
339 | function Tetrahedron(props) {
340 | _classCallCheck(this, Tetrahedron);
341 |
342 | var _this22 = _possibleConstructorReturn(this, (Tetrahedron.__proto__ || Object.getPrototypeOf(Tetrahedron)).call(this, props));
343 |
344 | _this22.primitiveName = 'a-tetrahedron';
345 | return _this22;
346 | }
347 |
348 | return Tetrahedron;
349 | }(_core.Entity);
350 |
351 | var Text = exports.Text = function (_Entity23) {
352 | _inherits(Text, _Entity23);
353 |
354 | function Text(props) {
355 | _classCallCheck(this, Text);
356 |
357 | var _this23 = _possibleConstructorReturn(this, (Text.__proto__ || Object.getPrototypeOf(Text)).call(this, props));
358 |
359 | _this23.primitiveName = 'a-text';
360 | return _this23;
361 | }
362 |
363 | return Text;
364 | }(_core.Entity);
365 |
366 | var TorusKnot = exports.TorusKnot = function (_Entity24) {
367 | _inherits(TorusKnot, _Entity24);
368 |
369 | function TorusKnot(props) {
370 | _classCallCheck(this, TorusKnot);
371 |
372 | var _this24 = _possibleConstructorReturn(this, (TorusKnot.__proto__ || Object.getPrototypeOf(TorusKnot)).call(this, props));
373 |
374 | _this24.primitiveName = 'a-torus-knot';
375 | return _this24;
376 | }
377 |
378 | return TorusKnot;
379 | }(_core.Entity);
380 |
381 | var Torus = exports.Torus = function (_Entity25) {
382 | _inherits(Torus, _Entity25);
383 |
384 | function Torus(props) {
385 | _classCallCheck(this, Torus);
386 |
387 | var _this25 = _possibleConstructorReturn(this, (Torus.__proto__ || Object.getPrototypeOf(Torus)).call(this, props));
388 |
389 | _this25.primitiveName = 'a-torus';
390 | return _this25;
391 | }
392 |
393 | return Torus;
394 | }(_core.Entity);
395 |
396 | var Triangle = exports.Triangle = function (_Entity26) {
397 | _inherits(Triangle, _Entity26);
398 |
399 | function Triangle(props) {
400 | _classCallCheck(this, Triangle);
401 |
402 | var _this26 = _possibleConstructorReturn(this, (Triangle.__proto__ || Object.getPrototypeOf(Triangle)).call(this, props));
403 |
404 | _this26.primitiveName = 'a-triangle';
405 | return _this26;
406 | }
407 |
408 | return Triangle;
409 | }(_core.Entity);
410 |
411 | var Video = exports.Video = function (_Entity27) {
412 | _inherits(Video, _Entity27);
413 |
414 | function Video(props) {
415 | _classCallCheck(this, Video);
416 |
417 | var _this27 = _possibleConstructorReturn(this, (Video.__proto__ || Object.getPrototypeOf(Video)).call(this, props));
418 |
419 | _this27.primitiveName = 'a-video';
420 | return _this27;
421 | }
422 |
423 | return Video;
424 | }(_core.Entity);
425 |
426 | var Videosphere = exports.Videosphere = function (_Entity28) {
427 | _inherits(Videosphere, _Entity28);
428 |
429 | function Videosphere(props) {
430 | _classCallCheck(this, Videosphere);
431 |
432 | var _this28 = _possibleConstructorReturn(this, (Videosphere.__proto__ || Object.getPrototypeOf(Videosphere)).call(this, props));
433 |
434 | _this28.primitiveName = 'a-videosphere';
435 | return _this28;
436 | }
437 |
438 | return Videosphere;
439 | }(_core.Entity);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-aframe-ar",
3 | "version": "1.18.0",
4 | "description": "Build virtual and augmented reality experiences with React and A-Frame.",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "build": "babel src -d dist",
8 | "prepublish": "npm run build"
9 | },
10 | "devDependencies": {
11 | "aframe": "^0.5.0",
12 | "babel": "^6.3.13",
13 | "babel-cli": "^6.3.15",
14 | "babel-loader": "^6.4.1",
15 | "babel-preset-es2015": "^6.3.13",
16 | "babel-preset-react": "^6.3.13",
17 | "babel-preset-stage-0": "^6.3.13",
18 | "babel-register": "^6.16.3",
19 | "react": "^15.5.4",
20 | "react-dom": "^15.5.4",
21 | "react-test-renderer": "^15.5.4",
22 | "webpack": "^2.3.2"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/tnga/react-aframe.git"
27 | },
28 | "keywords": [
29 | "react",
30 | "vr",
31 | "ar",
32 | "a-frame",
33 | "aframe",
34 | "moz-vr",
35 | "react-vr",
36 | "react-component",
37 | "virtual-reality",
38 | "augmented-reality",
39 | "webvr"
40 | ],
41 | "author": "tnga ",
42 | "license": "MIT",
43 | "bugs": {
44 | "url": "https://github.com/tnga/react-aframe/issues"
45 | },
46 | "homepage": "https://github.com/tnga/react-aframe#readme"
47 | }
48 |
--------------------------------------------------------------------------------
/src/core.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const _nonEntityProps = ['children', 'events', 'primitive']
4 | const _nonStandardProp = ['className', 'id', 'mixin']
5 | const _checkNonEntityProp = propName => _nonEntityProps.indexOf(propName) === -1
6 | const _checkStandardProp = propName => _nonStandardProp.indexOf(propName) !== -1 || propName.indexOf('data-') === 0
7 |
8 | const _options = {
9 | // React needs this because React serializes.
10 | // Preact does not because Preact runs `.setAttribute` on its own.
11 | runSetAttributeOnUpdates: true
12 | }
13 | export {_options}
14 |
15 | /**
16 | * Render .
17 | * Tell React to use A-Frame's .setAttribute() on the DOM element
18 | * for all prop initializations and updates.
19 | */
20 | export class Entity extends React.Component {
21 | constructor () {
22 | super()
23 |
24 | /**
25 | * In response to initial `ref` callback.
26 | */
27 | this.initEntity = (el) => {
28 | if (!el) { return }
29 |
30 | const props = this.props
31 | // Store.
32 | this.el = el
33 |
34 | // Attach events.
35 | if (props.events) {
36 | Object.keys(props.events).forEach(eventName => {
37 | addEventListeners(el, eventName, props.events[eventName])
38 | })
39 | }
40 |
41 | // Update entity.
42 | updateAttributes(el, null, props)
43 |
44 | // Allow ref.
45 | if (props._ref) { props._ref(el) }
46 | }
47 | }
48 |
49 | /**
50 | * Handle updates after the initial render.
51 | */
52 | componentDidUpdate (prevProps, prevState) {
53 | const el = this.el
54 | const props = this.props
55 |
56 | // Update events.
57 | updateEventListeners(el, prevProps.events, props.events)
58 |
59 | // Update entity.
60 | if (_options.runSetAttributeOnUpdates) {
61 | updateAttributes(el, prevProps, props)
62 | }
63 | }
64 |
65 | componentWillUnmount () {
66 | const el = this.el
67 | const props = this.props
68 |
69 | if (props.events) {
70 | // Remove events.
71 | Object.keys(props.events).forEach(eventName => {
72 | removeEventListeners(el, eventName, props.events[eventName])
73 | })
74 | }
75 | }
76 |
77 | /**
78 | * Render A-Frame DOM with ref: https://facebook.github.io/react/docs/refs-and-the-dom.html
79 | */
80 | render () {
81 | const props = this.props
82 | const elementName = this.primitiveName || props.primitive || 'a-entity'
83 |
84 | // Let through props that are OK to render initially.
85 | let reactProps = {}
86 | Object.keys(props).forEach(propName => {
87 | if (_checkStandardProp(propName)) reactProps[propName] = props[propName]
88 | })
89 |
90 | return React.createElement(
91 | elementName,
92 | { ref: this.initEntity, ...reactProps },
93 | props.children
94 | )
95 | }
96 | }
97 |
98 | /**
99 | * Render .
100 | * extends from in A-Frame so we reuse .
101 | */
102 | export class Scene extends Entity {
103 | constructor (props) {
104 | super(props)
105 | this.primitiveName = 'a-scene'
106 | }
107 | }
108 |
109 | /**
110 | * Handle diffing of previous and current attributes.
111 | *
112 | * @param {Element} el
113 | * @param {Object|null} prevProps - Previous props map.
114 | * @param {Object} props - Current props map.
115 | */
116 | function updateAttributes (el, prevProps, props) {
117 | if (!props || prevProps === props) { return }
118 |
119 | // Set attributes.
120 | Object.keys(props).filter(_checkNonEntityProp).forEach(propName => {
121 | callSetAttribute(el, props[propName], propName)
122 | })
123 |
124 | // See if attributes were removed.
125 | if (prevProps) {
126 | Object.keys(prevProps).filter(_checkNonEntityProp).forEach(propName => {
127 | if (props[propName] === undefined) el.removeAttribute(propName)
128 | })
129 | }
130 | }
131 |
132 | /**
133 | * Call `.setAttribute()` on the `ref`,
134 | * passing prop data directly to A-Frame.
135 | *
136 | * @param {Element} el
137 | * @param {Object|null} props - props map.
138 | * @param {Object} propName
139 | */
140 | function callSetAttribute (el, propValue, propName) {
141 | if (!propValue || !(propValue.constructor === Function)) {
142 | if (propName === 'className') propName = 'class'
143 |
144 | el.setAttribute(propName, propValue)
145 | }
146 | }
147 |
148 | /**
149 | * Handle diffing of previous and current event maps.
150 | *
151 | * @param {Element} el
152 | * @param {Object} prevEvents - Previous event map.
153 | * @param {Object} events - Current event map.
154 | */
155 | function updateEventListeners (el, prevEvents, events) {
156 | if (!prevEvents || !events || prevEvents === events) { return }
157 |
158 | Object.keys(events).forEach(eventName => {
159 | // Didn't change.
160 | if (prevEvents[eventName] === events[eventName]) return
161 |
162 | // If changed, remove old previous event listeners.
163 | if (prevEvents[eventName]) removeEventListeners(el, eventName, prevEvents[eventName])
164 |
165 | // Add new event listeners.
166 | addEventListeners(el, eventName, events[eventName])
167 | })
168 |
169 | // See if event handlers were removed.
170 | Object.keys(prevEvents).forEach(eventName => {
171 | if (!events[eventName]) removeEventListeners(el, eventName, prevEvents[eventName])
172 | })
173 | }
174 |
175 | /**
176 | * Register event handlers for an event name to ref.
177 | *
178 | * @param {Element} el - DOM element.
179 | * @param {string} eventName
180 | * @param {array|function} eventHandlers - Handler function or array of handler functions.
181 | */
182 | function addEventListeners (el, eventName, handlers) {
183 | if (!handlers) return
184 |
185 | // Convert to array.
186 | if (handlers.constructor === Function) handlers = [handlers]
187 |
188 | // Register.
189 | handlers.forEach(handler => { el.addEventListener(eventName, handler) })
190 | }
191 |
192 | /**
193 | * Unregister event handlers for an event name to ref.
194 | *
195 | * @param {Element} el - DOM element.
196 | * @param {string} eventName
197 | * @param {array|function} eventHandlers - Handler function or array of handler functions.
198 | */
199 | function removeEventListeners (el, eventName, handlers) {
200 | if (!handlers) return
201 |
202 | // Convert to array.
203 | if (handlers.constructor === Function) handlers = [handlers]
204 |
205 | // Unregister.
206 | handlers.forEach(handler => { el.removeEventListener(eventName, handler) })
207 | }
208 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export * from './core'
2 | export * from './primitives'
3 |
--------------------------------------------------------------------------------
/src/primitives.js:
--------------------------------------------------------------------------------
1 | import {Entity} from './core'
2 |
3 | /**
4 | * Render primitve tag as , eg: .
5 | * primitive tag extends from in A-Frame so we reuse .
6 | */
7 |
8 | export class Animation extends Entity {
9 | constructor (props) {
10 | super(props)
11 | this.primitiveName = 'a-animation'
12 | }
13 | }
14 |
15 | export class Box extends Entity {
16 | constructor (props) {
17 | super(props)
18 | this.primitiveName = 'a-box'
19 | }
20 | }
21 |
22 | export class Camera extends Entity {
23 | constructor (props) {
24 | super(props)
25 | this.primitiveName = 'a-camera'
26 | }
27 | }
28 |
29 | export class ColladaModel extends Entity {
30 | constructor (props) {
31 | super(props)
32 | this.primitiveName = 'a-collada-model'
33 | }
34 | }
35 |
36 | export class Cone extends Entity {
37 | constructor (props) {
38 | super(props)
39 | this.primitiveName = 'a-cone'
40 | }
41 | }
42 |
43 | export class Cursor extends Entity {
44 | constructor (props) {
45 | super(props)
46 | this.primitiveName = 'a-cursor'
47 | }
48 | }
49 |
50 | export class Curvedimage extends Entity {
51 | constructor (props) {
52 | super(props)
53 | this.primitiveName = 'a-curvedimage'
54 | }
55 | }
56 |
57 | export class Cylinder extends Entity {
58 | constructor (props) {
59 | super(props)
60 | this.primitiveName = 'a-cylinder'
61 | }
62 | }
63 |
64 | export class Dodecahedron extends Entity {
65 | constructor (props) {
66 | super(props)
67 | this.primitiveName = 'a-dodecahedron'
68 | }
69 | }
70 |
71 | export class gltfModel extends Entity {
72 | constructor (props) {
73 | super(props)
74 | this.primitiveName = 'a-gltf-model'
75 | }
76 | }
77 |
78 | export class Icosahedron extends Entity {
79 | constructor (props) {
80 | super(props)
81 | this.primitiveName = 'a-icosahedron'
82 | }
83 | }
84 |
85 | export class Image extends Entity {
86 | constructor (props) {
87 | super(props)
88 | this.primitiveName = 'a-image'
89 | }
90 | }
91 |
92 | export class Light extends Entity {
93 | constructor (props) {
94 | super(props)
95 | this.primitiveName = 'a-light'
96 | }
97 | }
98 |
99 | export class Link extends Entity {
100 | constructor (props) {
101 | super(props)
102 | this.primitiveName = 'a-link'
103 | }
104 | }
105 |
106 | export class ObjModel extends Entity {
107 | constructor (props) {
108 | super(props)
109 | this.primitiveName = 'a-obj-model'
110 | }
111 | }
112 |
113 | export class Octahedron extends Entity {
114 | constructor (props) {
115 | super(props)
116 | this.primitiveName = 'a-octahedron'
117 | }
118 | }
119 |
120 | export class Plane extends Entity {
121 | constructor (props) {
122 | super(props)
123 | this.primitiveName = 'a-plane'
124 | }
125 | }
126 |
127 | export class Ring extends Entity {
128 | constructor (props) {
129 | super(props)
130 | this.primitiveName = 'a-ring'
131 | }
132 | }
133 |
134 | export class Sky extends Entity {
135 | constructor (props) {
136 | super(props)
137 | this.primitiveName = 'a-sky'
138 | }
139 | }
140 |
141 | export class Sound extends Entity {
142 | constructor (props) {
143 | super(props)
144 | this.primitiveName = 'a-sound'
145 | }
146 | }
147 |
148 | export class Sphere extends Entity {
149 | constructor (props) {
150 | super(props)
151 | this.primitiveName = 'a-sphere'
152 | }
153 | }
154 |
155 | export class Tetrahedron extends Entity {
156 | constructor (props) {
157 | super(props)
158 | this.primitiveName = 'a-tetrahedron'
159 | }
160 | }
161 |
162 | export class Text extends Entity {
163 | constructor (props) {
164 | super(props)
165 | this.primitiveName = 'a-text'
166 | }
167 | }
168 |
169 | export class TorusKnot extends Entity {
170 | constructor (props) {
171 | super(props)
172 | this.primitiveName = 'a-torus-knot'
173 | }
174 | }
175 |
176 | export class Torus extends Entity {
177 | constructor (props) {
178 | super(props)
179 | this.primitiveName = 'a-torus'
180 | }
181 | }
182 |
183 | export class Triangle extends Entity {
184 | constructor (props) {
185 | super(props)
186 | this.primitiveName = 'a-triangle'
187 | }
188 | }
189 |
190 | export class Video extends Entity {
191 | constructor (props) {
192 | super(props)
193 | this.primitiveName = 'a-video'
194 | }
195 | }
196 |
197 | export class Videosphere extends Entity {
198 | constructor (props) {
199 | super(props)
200 | this.primitiveName = 'a-videosphere'
201 | }
202 | }
203 |
--------------------------------------------------------------------------------