├── .watchmanconfig
├── src
├── data
│ ├── us-10m.v1.js
│ ├── stackData.js
│ ├── clusterData.js
│ └── contourData.js
├── screens
│ ├── AnchorHome.js
│ ├── PopcornList.js
│ ├── ARExample
│ │ ├── actions.js
│ │ ├── actions
│ │ │ ├── progress.js
│ │ │ └── hudSelection.js
│ │ ├── reducers
│ │ │ ├── region.js
│ │ │ ├── map.js
│ │ │ ├── geometry.js
│ │ │ ├── index.js
│ │ │ ├── heading.js
│ │ │ ├── location.js
│ │ │ ├── hudSelection.js
│ │ │ ├── progress.js
│ │ │ ├── hud.js
│ │ │ ├── objects.js
│ │ │ ├── polyObject3Ds.js
│ │ │ ├── poly.js
│ │ │ └── three.js
│ │ ├── App.js
│ │ ├── GeometryView.js
│ │ ├── PolyAssetView.js
│ │ ├── Progress.js
│ │ ├── store.js
│ │ ├── constants.js
│ │ ├── LongpressControl.js
│ │ ├── PolyAssetListView.js
│ │ ├── TouchVisualizer.js
│ │ └── GeometryListView.js
│ ├── Main.js
│ ├── TypingAnimation.js
│ ├── SvgInterpolatePointAlongPathAnimation.js
│ ├── SvgD3SankeyBasic.js
│ ├── SvgD3InterpolatePathAnimation.js
│ ├── SvgD3ContourDensityBasic.js
│ ├── SvgD3PackCircles.js
│ ├── SvgD3TreeBasic.js
│ ├── SvgD3TreemapBasic.js
│ ├── SvgRectAnimation.js
│ ├── SvgPathDrawing.js
│ ├── SvgD3Axis.js
│ ├── SvgD3ContourBasic.js
│ ├── SvgFlubberAnimation.js
│ ├── SvgCircleAnimation.js
│ ├── SvgD3PieAnimation.js
│ ├── SvgLineAnimation.js
│ ├── SvgEllipseAnimation.js
│ ├── SvgTextAnimation.js
│ ├── PixelBlurAnimation.js
│ ├── SvgD3Hexbin.js
│ ├── SvgD3PartitionBasic.js
│ ├── SvgD3ChordAnimation.js
│ ├── SvgPolylineAnimation.js
│ ├── SvgPolygonAnimation.js
│ ├── SvgD3StackBars.js
│ ├── SvgD3LineAnimation.js
│ ├── FacebookMessengerSearch.js
│ ├── SvgD3AreaAnimation.js
│ ├── LolaTravelChat.js
│ ├── SvgD3LinkAnimation.js
│ ├── SvgD3VoronoiAnimation.js
│ ├── StaggeredMapAnimation.js
│ ├── SvgTSpanAnimation.js
│ ├── SvgTransformAnimation.js
│ ├── SvgD3ClusterBasic.js
│ └── RealTimeChartExample.js
├── components
│ ├── package.json
│ ├── D3ShapeData.js
│ ├── D3PathCommand.js
│ ├── AnimatedSvgDelta.js
│ ├── AnimatedSvgStrokeDasharray.js
│ ├── AnimatedBlurView.js
│ ├── OperatorTextMessage.js
│ ├── AnimatedSvgStop.js
│ ├── VivusHi.js
│ ├── TwitterIconSvgPath.js
│ ├── AnimatedSvgG.js
│ ├── AnimatedSvgUse.js
│ ├── AnimatedSvg.js
│ ├── AnimatedSvgTSpan.js
│ ├── AnimatedSvgText.js
│ ├── AnimatedSvgPath.js
│ ├── GithubIconSvgPath.js
│ ├── OperatorMessageMeta.js
│ ├── AnimatedSvgLine.js
│ ├── AnimatedSvgCircle.js
│ ├── AnimatedSvgRect.js
│ ├── AnimatedSvgEllipse.js
│ ├── AnimatedSvgFix.js
│ ├── PopcornMovie.js
│ ├── AnimatedSvgTextPath.js
│ ├── OperatorMessageText.js
│ ├── LolaTravelBorder.js
│ ├── OperatorQuoteMessage.js
│ ├── AnimatedSvgLinearGradient.js
│ ├── LolaTravelTextMessage.js
│ ├── AnimatedSvgRadialGradient.js
│ ├── LolaTravelProfileMessage.js
│ ├── OperatorFormMessage.js
│ ├── OperatorMessageButton.js
│ ├── AnimatedSvgPropStringFix.js
│ ├── OperatorMessageFormInput.js
│ ├── AnimatedSvgAnimatedPropFix.js
│ ├── AnimatedSvgStateFix.js
│ ├── AnchorRadialChild.js
│ ├── SnapchatQuickChatItem.js
│ ├── OperatorSelectMessage.js
│ ├── AnimatedSvgD3InterpolatePath.js
│ ├── OperatorInputMessage.js
│ ├── LolaTravelSliderItem.js
│ ├── SnapchatSearchItem.js
│ ├── OperatorMessage.js
│ ├── AnimatedSvgInterpolatePointAtLength.js
│ ├── OperatorProductMessage.js
│ ├── AnimatedSvgD3ShapeArc.js
│ ├── OperatorMessageFormSelect.js
│ ├── AnimatedSvgPolyline.js
│ ├── AnimatedSvgPolygon.js
│ ├── SnapchatIconSvgPath.js
│ ├── PanResponderTouchable.js
│ ├── SvgTextWrap.js
│ ├── AnimatedLinearGradient.js
│ ├── AnimatedSvgD3ShapeLine.js
│ ├── AnimatedSvgD3ShapeLineRadial.js
│ ├── AnimatedSvgD3ShapeArea.js
│ ├── AnimatedSvgD3ShapeAreaRadial.js
│ └── AnimatedSvgD3GeoPath.js
├── randomBoolean.js
├── assets
│ └── icons
│ │ ├── poly.png
│ │ └── predicthq.png
├── randomNumber.js
├── randomArrayElement.js
├── randomImage.js
├── isAnimated.js
├── randomColor.js
├── PredictHQ.js
├── Poly.js
├── randomUser.js
├── randomPolygons.js
├── Instagram.js
├── GoogleMaps.js
├── noteAlignment.js
└── AnnotationCollection.js
├── assets
├── icon.png
└── splash.png
├── .vscode
└── settings.json
├── .gitignore
├── babel.config.js
├── jsconfig.json
├── lint-staged.config.js
├── App.js
├── .huskyrc.js
├── ReactotronConfig.js
├── .eslintrc
├── app.json
├── package.json
└── README.md
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/src/data/us-10m.v1.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/screens/AnchorHome.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/screens/PopcornList.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/screens/ARExample/actions.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/package.json:
--------------------------------------------------------------------------------
1 | { "name": "app" }
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ethantran/react-native-examples/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/src/randomBoolean.js:
--------------------------------------------------------------------------------
1 | export default function () {
2 | return Math.random() >= 0.5;
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.enable": true,
3 | "javascript.validate.enable": false
4 | }
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ethantran/react-native-examples/HEAD/assets/splash.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p12
6 | *.key
7 | *.mobileprovision
8 |
--------------------------------------------------------------------------------
/src/assets/icons/poly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ethantran/react-native-examples/HEAD/src/assets/icons/poly.png
--------------------------------------------------------------------------------
/src/randomNumber.js:
--------------------------------------------------------------------------------
1 | export default function (min, max) {
2 | return Math.random() * (max - min) + min;
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/icons/predicthq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ethantran/react-native-examples/HEAD/src/assets/icons/predicthq.png
--------------------------------------------------------------------------------
/src/randomArrayElement.js:
--------------------------------------------------------------------------------
1 | export default function (array) {
2 | return array[Math.floor(Math.random() * array.length)];
3 | }
4 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ["babel-preset-expo"]
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/src/randomImage.js:
--------------------------------------------------------------------------------
1 | export default function (width = 800, height = 600) {
2 | return `https://source.unsplash.com/random/${width}x${height}`;
3 | }
4 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "allowSyntheticDefaultImports": true
5 | },
6 | "exclude": [
7 | "node_modules"
8 | ]
9 | }
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "*.js": [
3 | // "eslint . --fix --quiet",
4 | "prettier --write",
5 | "git add"
6 | // "jest --bail --findRelatedTests"
7 | ]
8 | };
9 |
--------------------------------------------------------------------------------
/src/components/D3ShapeData.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | class D3ShapeData extends Component {
3 | render() {
4 | return null;
5 | }
6 | }
7 | export default D3ShapeData;
8 |
--------------------------------------------------------------------------------
/src/components/D3PathCommand.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | class D3PathCommand extends Component {
3 | render() {
4 | return null;
5 | }
6 | }
7 | export default D3PathCommand;
8 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgDelta.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | class AnimatedSvgDelta extends Component {
3 | render() {
4 | return null;
5 | }
6 | }
7 | export default AnimatedSvgDelta;
--------------------------------------------------------------------------------
/src/isAnimated.js:
--------------------------------------------------------------------------------
1 | import { Animated } from 'react-native';
2 | export default function (val) {
3 | return val instanceof Animated.Value || val instanceof Animated.ValueXY || val instanceof Animated.AnimatedInterpolation;
4 | }
5 |
--------------------------------------------------------------------------------
/src/randomColor.js:
--------------------------------------------------------------------------------
1 | export default function () {
2 | var letters = '0123456789ABCDEF';
3 | var color = '#';
4 | for (var i = 0; i < 6; i++) {
5 | color += letters[Math.floor(Math.random() * 16)];
6 | }
7 | return color;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgStrokeDasharray.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | class AnimatedSvgStrokeDasharray extends Component {
3 | render() {
4 | return null;
5 | }
6 | }
7 | export default AnimatedSvgStrokeDasharray;
8 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import { createAppContainer, createStackNavigator } from "react-navigation";
2 | // import Reactotron from './ReactotronConfig';
3 | import routeConfig from "./src/routeConfig";
4 |
5 | export default createAppContainer(createStackNavigator(routeConfig));
6 |
--------------------------------------------------------------------------------
/src/components/AnimatedBlurView.js:
--------------------------------------------------------------------------------
1 | import { Animated } from 'react-native';
2 | import { Components } from 'expo';
3 | const { BlurView } = Components;
4 | const AnimatedBlurView = Animated.createAnimatedComponent(BlurView);
5 | export default AnimatedBlurView;
6 |
--------------------------------------------------------------------------------
/src/screens/ARExample/actions/progress.js:
--------------------------------------------------------------------------------
1 | export const SET_PROGRESS = 'progress/SET';
2 | export const CLOSE_PROGRESS = 'progress/CLOSE';
3 |
4 | export const setProgress = progress => ({ type: SET_PROGRESS, progress });
5 | export const closeProgress = () => ({ type: CLOSE_PROGRESS });
--------------------------------------------------------------------------------
/.huskyrc.js:
--------------------------------------------------------------------------------
1 | const runYarnLock = "yarn install --frozen-lockfile";
2 |
3 | module.exports = {
4 | hooks: {
5 | "pre-commit": "lint-staged",
6 | "post-checkout": `if [[ $HUSKY_GIT_PARAMS =~ 1$ ]]; then ${runYarnLock}; fi`,
7 | "post-merge": runYarnLock,
8 | "post-rebase": "yarn install"
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/src/components/OperatorTextMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Message from './OperatorMessage';
4 | import MessageText from './OperatorMessageText';
5 |
6 | const TextMessage = (props) => (
7 |
8 |
9 |
10 | );
11 | export default TextMessage;
12 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgStop.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | /**
3 | * Problem: Stop throws a warning when using animated as a prop
4 | * Solution: Use this since we are going to replace it in parent
5 | */
6 | class SvgRect extends Component {
7 | render() {
8 | return null;
9 | }
10 | }
11 | export default SvgRect;
12 |
--------------------------------------------------------------------------------
/src/PredictHQ.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs';
2 |
3 | const PredictHQ = {
4 | events: {
5 | search: async (params, options) =>
6 | (await fetch(
7 | `https://api.predicthq.com/v1/events/?${qs.stringify(params)}`,
8 | options
9 | )).json()
10 | }
11 | };
12 |
13 | export default PredictHQ;
14 |
--------------------------------------------------------------------------------
/src/Poly.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs';
2 |
3 | const Poly = {
4 | getAsset: async (name, params) =>
5 | (await fetch(
6 | `https://poly.googleapis.com/v1/${name}/?${qs.stringify(params)}`
7 | )).json(),
8 | listAssets: async params =>
9 | (await fetch(
10 | `https://poly.googleapis.com/v1/assets/?${qs.stringify(params)}`
11 | )).json()
12 | };
13 |
14 | export default Poly;
15 |
--------------------------------------------------------------------------------
/src/components/VivusHi.js:
--------------------------------------------------------------------------------
1 | export default 'M366.2,204.2c-9.8,0-15-5.6-15-15.1V77.2h-85v28h19.5c9.8,0,8.5,2.1,8.5,11.6v72.4c0,9.5,0.5,15.1-9.3,15.1H277h-20.7c-8.5,0-14.2-4.1-14.2-12.9V52.4c0-8.5,5.7-12.3,14.2-12.3h18.8v-28h-127v28h18.1c8.5,0,9.9,2.1,9.9,8.9v56.1h-75V53.4c0-11.5,8.6-13.3,17-13.3h11v-28H2.2v28h26c8.5,0,12,2.1,12,7.9v142.2c0,8.5-3.6,13.9-12,13.9h-21v33h122v-33h-11c-8.5,0-17-4.1-17-12.2v-57.8h75v58.4c0,9.1-1.4,11.6-9.9,11.6h-18.1v33h122.9h5.9h102.2v-33H366.2z';
2 |
--------------------------------------------------------------------------------
/src/randomUser.js:
--------------------------------------------------------------------------------
1 | import randomBoolean from './randomBoolean'
2 | import randomNumber from './randomNumber'
3 |
4 | export default function (size) {
5 | const gender = randomBoolean() ? 'men': 'women';
6 | const number = Math.floor(randomNumber(0, 100));
7 | if (!size) {
8 | return `https://randomuser.me/api/portraits/${gender}/${number}.jpg`;
9 | }
10 | return `https://randomuser.me/api/portraits/${size}/${gender}/${number}.jpg`;
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/TwitterIconSvgPath.js:
--------------------------------------------------------------------------------
1 | export const d = 'M419.6 168.6c-11.7 5.2-24.2 8.7-37.4 10.2 13.4-8.1 23.8-20.8 28.6-36 -12.6 7.5-26.5 12.9-41.3 15.8 -11.9-12.6-28.8-20.6-47.5-20.6 -42 0-72.9 39.2-63.4 79.9 -54.1-2.7-102.1-28.6-134.2-68 -17 29.2-8.8 67.5 20.1 86.9 -10.7-0.3-20.7-3.3-29.5-8.1 -0.7 30.2 20.9 58.4 52.2 64.6 -9.2 2.5-19.2 3.1-29.4 1.1 8.3 25.9 32.3 44.7 60.8 45.2 -27.4 21.4-61.8 31-96.4 27 28.8 18.5 63 29.2 99.8 29.2 120.8 0 189.1-102.1 185-193.6C399.9 193.1 410.9 181.7 419.6 168.6z';
--------------------------------------------------------------------------------
/src/data/stackData.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | index: 0,
4 | apples: 3840,
5 | bananas: 1920,
6 | cherries: 960,
7 | dates: 400
8 | },
9 | {
10 | index: 1,
11 | apples: 1600,
12 | bananas: 1440,
13 | cherries: 960,
14 | dates: 400
15 | },
16 | { index: 2, apples: 640, bananas: 960, cherries: 640, dates: 400 },
17 | { index: 3, apples: 320, bananas: 480, cherries: 640, dates: 400 }
18 | ];
19 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/region.js:
--------------------------------------------------------------------------------
1 | import { SET_REGION, RESET } from '../actions/ar';
2 |
3 | const initialState = {};
4 |
5 | export default function(state = initialState, action) {
6 | switch (action.type) {
7 | case SET_REGION:
8 | return {
9 | ...state,
10 | ...action.region
11 | };
12 | case RESET:
13 | return initialState;
14 | default:
15 | return state;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/map.js:
--------------------------------------------------------------------------------
1 | import { TOGGLE_MAP } from '../actions/hud';
2 | import { RESET } from '../actions/ar';
3 |
4 | const initialState = {
5 | visible: false
6 | };
7 |
8 | export default function(state = initialState, action) {
9 | switch (action.type) {
10 | case TOGGLE_MAP:
11 | return {
12 | ...state,
13 | visible: !state.visible
14 | };
15 | case RESET:
16 | return initialState;
17 | default:
18 | return state;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/screens/ARExample/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import { PersistGate } from 'redux-persist/es/integration/react';
4 |
5 | import ARExample from './ARExample';
6 | import { store, persistor } from './store';
7 |
8 | export default class ARApp extends React.Component {
9 | render() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ReactotronConfig.js:
--------------------------------------------------------------------------------
1 | import { NativeModules } from 'react-native';
2 | import url from 'url';
3 |
4 | const { hostname } = url.parse(NativeModules.SourceCode.scriptURL);
5 |
6 | let Reactotron;
7 | if (__DEV__) {
8 | require('reactotron-react-native');
9 | Reactotron = require('reactotron-react-native').default;
10 | const reactotronRedux = require('reactotron-redux').reactotronRedux;
11 | Reactotron.configure({ host: hostname })
12 | .useReactNative()
13 | .use(reactotronRedux())
14 | .connect();
15 | console.tron = Reactotron;
16 | }
17 | export default Reactotron;
18 |
--------------------------------------------------------------------------------
/src/data/clusterData.js:
--------------------------------------------------------------------------------
1 | import * as d3Array from 'd3-array';
2 | export default function(numNodes = 200, numClusters = 10, maxRadius = 12) {
3 | var clusters = new Array(numClusters);
4 | var nodes = d3Array.range(numNodes).map(function() {
5 | var i = Math.floor(Math.random() * numClusters),
6 | r = Math.sqrt((i + 1) / numClusters * -Math.log(Math.random())) * maxRadius,
7 | d = { cluster: i, radius: r };
8 | if (!clusters[i] || r > clusters[i].radius) {clusters[i] = d;}
9 | return d;
10 | });
11 | return {
12 | clusters,
13 | nodes
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgG.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgFix from './AnimatedSvgFix';
4 |
5 | const NativeSvgG = Svg.G;
6 |
7 | class SvgG extends Component {
8 | setNativeProps = (props) => {
9 | this._component && this._component.setNativeProps(props);
10 | }
11 | render() {
12 | return (
13 | (this._component = component)}
15 | {...this.props}
16 | />
17 | );
18 | }
19 | }
20 | SvgG = AnimatedSvgFix(SvgG);
21 | export default SvgG;
22 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgUse.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgFix from './AnimatedSvgFix';
4 |
5 | const NativeSvgUse = Svg.Use;
6 |
7 | class SvgUse extends Component {
8 | setNativeProps = (props) => {
9 | this._component && this._component.setNativeProps(props);
10 | }
11 | render() {
12 | return (
13 | (this._component = component)}
15 | {...this.props}
16 | />
17 | );
18 | }
19 | }
20 | SvgUse = AnimatedSvgFix(SvgUse);
21 | export default SvgUse;
22 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvg.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg as NativeSvg } from 'expo';
3 | import AnimatedSvgFix from './AnimatedSvgFix';
4 |
5 | class Svg extends Component {
6 | setNativeProps = (props) => {
7 | this._component && this._component.setNativeProps(props);
8 | }
9 | render() {
10 | return (
11 | (this._component = component)}
13 | {...this.props}
14 | />
15 | );
16 | }
17 | }
18 | Svg = AnimatedSvgFix(Svg, {
19 | propString: ['width', 'height']
20 | });
21 | export default Svg;
22 |
--------------------------------------------------------------------------------
/src/randomPolygons.js:
--------------------------------------------------------------------------------
1 | import * as d3 from 'd3-array';
2 | export default function randomPolygons(numShapes, width, height) {
3 | return d3.range(numShapes).map(randomPolygon, width, height);
4 | }
5 | export function randomPolygon(width, height) {
6 | var sides = 3 + Math.floor(Math.random() * 10),
7 | r = 50 + Math.random() * 100,
8 | x = r + Math.random() * (width - r * 2),
9 | y = r + Math.random() * (height - r * 2);
10 | return d3.range(sides).map(function(i) {
11 | return [
12 | Math.cos(Math.PI / 2 + 2 * Math.PI * i / sides) * r + x,
13 | Math.sin(Math.PI / 2 + 2 * Math.PI * i / sides) * r + y
14 | ];
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgTSpan.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgTextFix from './AnimatedSvgTextFix';
4 |
5 | const NativeSvgTSpan = Svg.TSpan;
6 |
7 | class SvgTSpan extends Component {
8 | setNativeProps = (props) => {
9 | this._component && this._component.setNativeProps(props);
10 | }
11 | render() {
12 | return (
13 | (this._component = component)}
15 | {...this.props}
16 | />
17 | );
18 | }
19 | }
20 | SvgTSpan = AnimatedSvgTextFix(SvgTSpan);
21 | export default SvgTSpan;
22 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgText.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgTextFix from './AnimatedSvgTextFix';
4 |
5 | const NativeSvgText = Svg.Text;
6 |
7 | class SvgText extends Component {
8 | setNativeProps = (props) => {
9 | this._component && this._component.setNativeProps(props);
10 | }
11 | render() {
12 | return (
13 | (this._component = component)}
15 | {...this.props}
16 | />
17 | );
18 | }
19 | }
20 | SvgText = AnimatedSvgTextFix(SvgText, { container: true });
21 | export default SvgText;
22 |
--------------------------------------------------------------------------------
/src/data/contourData.js:
--------------------------------------------------------------------------------
1 | function goldsteinPrice(x, y) {
2 | return (
3 | (1 +
4 | Math.pow(x + y + 1, 2) *
5 | (19 - 14 * x + 3 * x * x - 14 * y + 6 * x * x + 3 * y * y)) *
6 | (30 +
7 | Math.pow(2 * x - 3 * y, 2) *
8 | (18 - 32 * x + 12 * x * x + 48 * y - 36 * x * y + 27 * y * y))
9 | );
10 | }
11 | export default function(n = 240, m = 125) {
12 | let values = new Array(n * m);
13 | for (var j = 0.5, k = 0; j < m; ++j) {
14 | for (var i = 0.5; i < n; ++i, ++k) {
15 | values[k] = goldsteinPrice(i / n * 4 - 2, 1 - j / m * 3);
16 | }
17 | }
18 | return values;
19 | }
20 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true,
5 | "es6": true,
6 | "react-native-globals/all": true
7 | },
8 | "plugins": [
9 | "react",
10 | "react-native-globals",
11 | "react-hooks"
12 | ],
13 | "extends": [
14 | "eslint:recommended",
15 | "plugin:react/recommended",
16 | "plugin:import/errors",
17 | "plugin:import/warnings",
18 | "prettier",
19 | "prettier/react",
20 | "plugin:prettier/recommended"
21 | ],
22 | "rules": {
23 | "react/prop-types": "off",
24 | "react-hooks/rules-of-hooks": "error",
25 | "react-hooks/exhaustive-deps": "warn"
26 | }
27 | }
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/geometry.js:
--------------------------------------------------------------------------------
1 | import { OPEN, CLOSE } from '../actions/geometry';
2 | import { RESET } from '../actions/ar';
3 |
4 | const initialState = {
5 | visible: false
6 | };
7 |
8 | export default function(state = initialState, action) {
9 | switch (action.type) {
10 | case OPEN:
11 | return {
12 | ...state,
13 | visible: true
14 | };
15 | case CLOSE:
16 | return {
17 | ...state,
18 | visible: false
19 | };
20 | case RESET:
21 | return initialState;
22 | default:
23 | return state;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/screens/ARExample/GeometryView.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TouchableOpacity, Text } from 'react-native';
3 |
4 | export default ({ geometry, onPress, index }) => (
5 | onPress(geometry)}
14 | >
15 |
21 | {geometry.name}
22 |
23 |
24 | );
25 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgPath.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import { Animated } from 'react-native';
4 |
5 | import AnimatedSvgFix from './AnimatedSvgFix';
6 |
7 | const NativeSvgPath = Svg.Path;
8 |
9 | class SvgPath extends Component {
10 | setNativeProps = (props = {}) => {
11 | this._component && this._component.setNativeProps(props);
12 | }
13 | render() {
14 | return (
15 | (this._component = component)}
17 | {...this.props}
18 | />
19 | );
20 | }
21 | }
22 | SvgPath = AnimatedSvgFix(SvgPath);
23 | export default SvgPath;
24 |
--------------------------------------------------------------------------------
/src/components/GithubIconSvgPath.js:
--------------------------------------------------------------------------------
1 | export const d = 'M256 70.7c-102.6 0-185.9 83.2-185.9 185.9 0 82.1 53.3 151.8 127.1 176.4 9.3 1.7 12.3-4 12.3-8.9V389.4c-51.7 11.3-62.5-21.9-62.5-21.9 -8.4-21.5-20.6-27.2-20.6-27.2 -16.9-11.5 1.3-11.3 1.3-11.3 18.7 1.3 28.5 19.2 28.5 19.2 16.6 28.4 43.5 20.2 54.1 15.4 1.7-12 6.5-20.2 11.8-24.9 -41.3-4.7-84.7-20.6-84.7-91.9 0-20.3 7.3-36.9 19.2-49.9 -1.9-4.7-8.3-23.6 1.8-49.2 0 0 15.6-5 51.1 19.1 14.8-4.1 30.7-6.2 46.5-6.3 15.8 0.1 31.7 2.1 46.6 6.3 35.5-24 51.1-19.1 51.1-19.1 10.1 25.6 3.8 44.5 1.8 49.2 11.9 13 19.1 29.6 19.1 49.9 0 71.4-43.5 87.1-84.9 91.7 6.7 5.8 12.8 17.1 12.8 34.4 0 24.9 0 44.9 0 51 0 4.9 3 10.7 12.4 8.9 73.8-24.6 127-94.3 127-176.4C441.9 153.9 358.6 70.7 256 70.7z';
2 |
--------------------------------------------------------------------------------
/src/components/OperatorMessageMeta.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 |
7 | },
8 | containerMe: {
9 | justifyContent: 'flex-end',
10 | alignItems: 'flex-end'
11 | },
12 | text: {
13 | fontSize: 10,
14 | color: '#9B9B9B'
15 | }
16 | });
17 |
18 | const MessageMeta = ({ me, children }) => (
19 |
23 | {children}
24 | {me && Lorem Ipsum}
25 |
26 | );
27 | export default MessageMeta;
28 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/index.js:
--------------------------------------------------------------------------------
1 | import geometry from './geometry';
2 | import heading from './heading';
3 | import hud from './hud';
4 | import hudSelection from './hudSelection';
5 | import location from './location';
6 | import map from './map';
7 | import objects from './objects';
8 | import poly from './poly';
9 | import polyObject3Ds from './polyObject3Ds';
10 | import progress from './progress';
11 | import region from './region';
12 | import three from './three';
13 |
14 | export default {
15 | geometry,
16 | heading,
17 | hud,
18 | hudSelection,
19 | location,
20 | map,
21 | objects,
22 | poly,
23 | polyObject3Ds,
24 | progress,
25 | region,
26 | three
27 | };
28 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgLine.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgFix from './AnimatedSvgFix';
4 |
5 | const NativeSvgLine = Svg.Line;
6 |
7 | export const args = ['x1', 'y1', 'x2', 'y2'];
8 |
9 | class SvgLine extends Component {
10 | setNativeProps = (props) => {
11 | this._component && this._component.setNativeProps(props);
12 | }
13 | render() {
14 | return (
15 | (this._component = component)}
17 | {...this.props}
18 | />
19 | );
20 | }
21 | }
22 | SvgLine = AnimatedSvgFix(SvgLine, { propString: args });
23 | export default SvgLine;
24 |
--------------------------------------------------------------------------------
/src/screens/Main.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { ScrollView, Button } from 'react-native';
3 | import routeConfig from '../routeConfig';
4 |
5 | export default class MainScreen extends Component {
6 | render() {
7 | const { Main, ...routes } = routeConfig;
8 | return (
9 |
10 | {Object.keys(routes).map(route => (
11 |
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/screens/TypingAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View } from 'react-native';
3 | import loremipsum from 'lorem-ipsum-react-native';
4 |
5 | import randomNumber from '../randomNumber';
6 | import TypingText from '../components/TypingText';
7 |
8 | const min = 1;
9 | const max = 5;
10 | const texts = Array(2)
11 | .fill()
12 | .map(_ => loremipsum({ count: randomNumber(min, max), units: 'words' }));
13 |
14 | export default class TypingAnimation extends Component {
15 | state = { texts };
16 | render() {
17 | return (
18 |
19 |
20 |
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgCircle.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgFix from './AnimatedSvgFix';
4 |
5 | const NativeSvgCircle = Svg.Circle;
6 |
7 | export const args = ['r', 'cx', 'cy'];
8 |
9 | class SvgCircle extends Component {
10 | setNativeProps = (props) => {
11 | this._component && this._component.setNativeProps(props);
12 | }
13 | render() {
14 | return (
15 | (this._component = component)}
17 | {...this.props}
18 | />
19 | );
20 | }
21 | }
22 | SvgCircle = AnimatedSvgFix(SvgCircle, { propString: args });
23 | export default SvgCircle;
24 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgRect.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgFix from './AnimatedSvgFix';
4 |
5 | const NativeSvgRect = Svg.Rect;
6 |
7 | export const args = ['width', 'height'];
8 |
9 | class SvgRect extends Component {
10 | setNativeProps = (props) => {
11 | this._component && this._component.setNativeProps(props);
12 | }
13 | render() {
14 | return (
15 | (this._component = component)}
17 | {...this.props}
18 | />
19 | );
20 | }
21 | }
22 | SvgRect = AnimatedSvgFix(SvgRect, { propString: args, keepXY: true });
23 | export default SvgRect;
24 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/heading.js:
--------------------------------------------------------------------------------
1 | import { SET_INITIAL_HEADING, SET_HEADING, RESET } from '../actions/ar';
2 |
3 | const initialState = {};
4 |
5 | export default function(state = initialState, action) {
6 | switch (action.type) {
7 | case SET_INITIAL_HEADING:
8 | return {
9 | ...state,
10 | initialHeading: action.heading,
11 | currentHeading: action.heading
12 | };
13 | case SET_HEADING:
14 | return {
15 | ...state,
16 | currentHeading: action.heading
17 | };
18 | case RESET:
19 | return initialState;
20 | default:
21 | return state;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgEllipse.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgFix from './AnimatedSvgFix';
4 |
5 | const NativeSvgEllipse = Svg.Ellipse;
6 |
7 | export const args = ['rx', 'ry', 'cx', 'cy'];
8 |
9 | class SvgEllipse extends Component {
10 | setNativeProps = (props) => {
11 | this._component && this._component.setNativeProps(props);
12 | }
13 | render() {
14 | return (
15 | (this._component = component)}
17 | {...this.props}
18 | />
19 | );
20 | }
21 | }
22 | SvgEllipse = AnimatedSvgFix(SvgEllipse, { propString: args });
23 | export default SvgEllipse;
24 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgFix.js:
--------------------------------------------------------------------------------
1 | import { Animated } from 'react-native';
2 | import AnimatedSvgBrushFix from './AnimatedSvgBrushFix';
3 | import AnimatedSvgPropStringFix from './AnimatedSvgPropStringFix';
4 | import AnimatedSvgStateFix from './AnimatedSvgStateFix';
5 | import AnimatedSvgTransformFix from './AnimatedSvgTransformFix';
6 |
7 | export default function (Component, { state, propString, keepXY } = {}) {
8 | Component = AnimatedSvgStateFix(Component, state);
9 | Component = AnimatedSvgBrushFix(Component);
10 | Component = AnimatedSvgPropStringFix(Component, propString);
11 | Component = AnimatedSvgTransformFix(Component, { keepXY });
12 | Component = Animated.createAnimatedComponent(Component);
13 | return Component;
14 | }
15 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/location.js:
--------------------------------------------------------------------------------
1 | import { SET_INITIAL_LOCATION, SET_LOCATION, RESET } from '../actions/ar';
2 |
3 | const initialState = {};
4 |
5 | export default function(state = initialState, action) {
6 | switch (action.type) {
7 | case SET_INITIAL_LOCATION:
8 | return {
9 | ...state,
10 | initialLocation: action.location,
11 | currentLocation: action.location
12 | };
13 | case SET_LOCATION:
14 | return {
15 | ...state,
16 | currentLocation: action.location
17 | };
18 | case RESET:
19 | return initialState;
20 | default:
21 | return state;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/hudSelection.js:
--------------------------------------------------------------------------------
1 | import { HUD_CLOSE } from '../actions/hudSelection';
2 | import { SELECT_OBJECT, SELECT_OBJECT3D, RESET } from '../actions/ar';
3 |
4 | const initialState = {
5 | visible: false
6 | };
7 |
8 | export default function(state = initialState, action) {
9 | switch (action.type) {
10 | case SELECT_OBJECT:
11 | case SELECT_OBJECT3D:
12 | return {
13 | ...state,
14 | visible: true
15 | };
16 | case HUD_CLOSE:
17 | return {
18 | ...state,
19 | visible: false
20 | };
21 | case RESET:
22 | return initialState;
23 | default:
24 | return state;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/progress.js:
--------------------------------------------------------------------------------
1 | import { SET_PROGRESS, CLOSE_PROGRESS } from '../actions/progress';
2 | import { RESET } from '../actions/ar';
3 |
4 | const initialState = {
5 | visible: false,
6 | progress: 0
7 | };
8 |
9 | export default function(state = initialState, action) {
10 | switch (action.type) {
11 | case SET_PROGRESS:
12 | return {
13 | ...state,
14 | visible: true,
15 | progress: action.progress
16 | };
17 | case CLOSE_PROGRESS:
18 | return {
19 | ...state,
20 | visible: false
21 | };
22 | case RESET:
23 | return initialState;
24 | default:
25 | return state;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/PopcornMovie.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, Image } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 |
7 | },
8 | image: {
9 |
10 | },
11 | overlay: {
12 |
13 | },
14 | meta: {
15 |
16 | },
17 | name: {
18 | backgroundColor: 'transparent'
19 | },
20 | stars: {
21 |
22 | }
23 | });
24 |
25 | const Movie = ({ name, stars, source }) => (
26 |
27 |
28 |
29 |
30 | {name}
31 | {stars}
32 |
33 |
34 | );
35 | export default Movie;
36 |
--------------------------------------------------------------------------------
/src/screens/ARExample/PolyAssetView.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { TouchableOpacity, Image, Dimensions } from 'react-native';
3 |
4 | const { width } = Dimensions.get('window');
5 |
6 | const ROW_COUNT = 3;
7 | const SIZE = width / ROW_COUNT - 12;
8 |
9 | export default ({ asset, onPress, index }) => (
10 | onPress(asset)}
17 | >
18 |
28 |
29 | );
30 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgTextPath.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Svg } from 'expo';
3 | import AnimatedSvgTextFix from './AnimatedSvgTextFix';
4 |
5 | /**
6 | * BUG: TextPath does not have setNativeProps
7 | * BUG: startOffset does not animate even with state
8 | */
9 |
10 | const NativeSvgTextPath = Svg.TextPath;
11 |
12 | class SvgTextPath extends Component {
13 | setNativeProps = (props) => {
14 | // this._component && this._component.setNativeProps(props);
15 | }
16 | render() {
17 | return (
18 | (this._component = component)}
20 | {...this.props}
21 | />
22 | );
23 | }
24 | }
25 | SvgTextPath = AnimatedSvgTextFix(SvgTextPath, { container: true });
26 | export default SvgTextPath;
27 |
--------------------------------------------------------------------------------
/src/components/OperatorMessageText.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text } from 'react-native';
3 |
4 | const colorLight = '#E8E8E8';
5 |
6 | const styles = StyleSheet.create({
7 | textContainer: {
8 | borderBottomWidth: StyleSheet.hairlineWidth,
9 | borderColor: colorLight,
10 | padding: 10,
11 | overflow: 'hidden'
12 | },
13 | text: {
14 | fontSize: 18,
15 | fontWeight: '200',
16 | padding: 5,
17 | color: '#717171'
18 | },
19 | textMe: {
20 | color: 'white',
21 | textAlign: 'right'
22 | }
23 | });
24 |
25 | const MessageText = ({ text, me }) => (
26 |
27 | {text}
28 |
29 | );
30 | export default MessageText;
31 |
--------------------------------------------------------------------------------
/src/components/LolaTravelBorder.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View } from 'react-native';
3 |
4 | const colorSandra = '#1363FB';
5 | const colorRobin = '#AFAFA5';
6 |
7 | const styles = StyleSheet.create({
8 | container: {
9 | flexDirection: 'row'
10 | },
11 | border: {
12 | width: 5,
13 | borderRadius: 2,
14 | alignItems: 'stretch',
15 | },
16 | children: {
17 | paddingHorizontal: 20,
18 | paddingVertical: 3
19 | }
20 | });
21 | const Border = ({ me, children, style }) => (
22 |
23 |
24 |
25 | {children}
26 |
27 |
28 | );
29 | export default Border;
30 |
--------------------------------------------------------------------------------
/src/components/OperatorQuoteMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text } from 'react-native';
3 |
4 | import Message from './OperatorMessage';
5 |
6 | const styles = StyleSheet.create({
7 | container: {
8 | paddingHorizontal: 15,
9 | paddingTop: 15,
10 | paddingBottom: 10
11 | },
12 | text: {
13 | fontSize: 18,
14 | fontWeight: '100',
15 | marginBottom: 10,
16 | },
17 | time: {
18 | fontWeight: '100',
19 | }
20 | });
21 |
22 | const QuoteMessage = ({ text, time, ...props }) => (
23 |
24 |
25 | {text}
26 | {time}
27 |
28 |
29 | );
30 | export default QuoteMessage;
31 |
--------------------------------------------------------------------------------
/src/screens/ARExample/Progress.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
3 | import { connect } from 'react-redux';
4 |
5 | const Progress = ({ visible, progress }) =>
6 | visible ? (
7 |
17 |
18 | {`Loading... ${progress}`}
19 |
20 | ) : null;
21 |
22 | const mapStateToProps = state => state.progress;
23 |
24 | export default connect(mapStateToProps)(Progress);
25 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/hud.js:
--------------------------------------------------------------------------------
1 | import { OPEN as OPEN_POLY, CLOSE as CLOSE_POLY } from '../actions/poly';
2 | import {
3 | OPEN as OPEN_GEOMETRY,
4 | CLOSE as CLOSE_GEOMETRY
5 | } from '../actions/geometry';
6 | import { RESET } from '../actions/ar';
7 |
8 | const initialState = {
9 | visible: true
10 | };
11 |
12 | export default function(state = initialState, action) {
13 | switch (action.type) {
14 | case OPEN_POLY:
15 | case OPEN_GEOMETRY:
16 | return {
17 | ...state,
18 | visible: false
19 | };
20 | case CLOSE_POLY:
21 | case CLOSE_GEOMETRY:
22 | return {
23 | ...state,
24 | visible: true
25 | };
26 | case RESET:
27 | return initialState;
28 | default:
29 | return state;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/objects.js:
--------------------------------------------------------------------------------
1 | // import pick from 'lodash/pick';
2 | import omit from 'lodash/omit';
3 |
4 | import { ADD_OBJECT, ADD_OBJECTS, REMOVE_OBJECT, RESET } from '../actions/ar';
5 |
6 | // need to track objects in the scene for loading from storage,
7 | const initialState = [];
8 |
9 | export default function(state = initialState, action) {
10 | switch (action.type) {
11 | case ADD_OBJECT:
12 | // remove object3D beacuse we don't want to store that
13 | return [...state, omit(action.object, 'object3D')];
14 | case ADD_OBJECTS:
15 | return [...state, ...action.objects.map(o => omit(o, 'object3D'))];
16 | case REMOVE_OBJECT:
17 | return state.filter(e => e !== action.object);
18 | case RESET:
19 | return initialState;
20 | default:
21 | return state;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgLinearGradient.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Animated } from 'react-native';
3 | import { Svg } from 'expo';
4 | import AnimatedSvgStateFix from './AnimatedSvgStateFix';
5 | import AnimatedSvgGradientFix from './AnimatedSvgGradientFix';
6 |
7 | const NativeSvgLinearGradient = Svg.LinearGradient;
8 |
9 | export const args = ['x1', 'y1', 'x2', 'y2'];
10 |
11 | class SvgLinearGradient extends Component {
12 | render() {
13 | return (
14 |
17 | );
18 | }
19 | }
20 | SvgLinearGradient = AnimatedSvgGradientFix(SvgLinearGradient);
21 | SvgLinearGradient = AnimatedSvgStateFix(SvgLinearGradient, args, { cancelSetNativeProps: true });
22 | SvgLinearGradient = Animated.createAnimatedComponent(SvgLinearGradient);
23 | export default SvgLinearGradient;
24 |
--------------------------------------------------------------------------------
/src/screens/ARExample/store.js:
--------------------------------------------------------------------------------
1 | import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import { persistStore, persistCombineReducers } from 'redux-persist';
4 | import storage from 'redux-persist/es/storage';
5 |
6 | import reducers from './reducers';
7 |
8 | const config = {
9 | key: 'root',
10 | storage,
11 | blacklist: ['three', 'hudSelection', 'polyObject3Ds']
12 | };
13 |
14 | const reducer = persistCombineReducers(config, reducers);
15 |
16 | let createStore = reduxCreateStore;
17 |
18 | // if (__DEV__) {
19 | // require('reactotron-react-native');
20 | // //$FlowFixMe
21 | // const Reactotron = require('reactotron-react-native').default;
22 | // createStore = Reactotron.createStore;
23 | // }
24 |
25 | export const store = createStore(reducer, applyMiddleware(thunk));
26 | export const persistor = persistStore(store);
27 |
--------------------------------------------------------------------------------
/src/components/LolaTravelTextMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text } from 'react-native';
3 |
4 | import Border from './LolaTravelBorder';
5 |
6 | const colorSandra = '#1363FB';
7 | const colorRobin = '#AFAFA5';
8 |
9 | const styles = StyleSheet.create({
10 | container: {
11 | marginBottom: 20
12 | },
13 | name: {
14 | fontWeight: 'bold'
15 | },
16 | text: {
17 | lineHeight: 23
18 | },
19 | });
20 |
21 | const TextMessage = ({user, text, me}) => (
22 |
23 |
24 |
25 | {user.name.toUpperCase()}
26 | {text}
27 |
28 |
29 |
30 | );
31 |
32 | export default TextMessage;
33 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgRadialGradient.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Animated } from 'react-native';
3 | import { Svg } from 'expo';
4 | import AnimatedSvgStateFix from './AnimatedSvgStateFix';
5 | import AnimatedSvgGradientFix from './AnimatedSvgGradientFix';
6 |
7 | const NativeSvgRadialGradient = Svg.RadialGradient;
8 |
9 | export const args = ['fx', 'fy', 'rx', 'ry', 'cx', 'cy', 'r'];
10 |
11 | class SvgRadialGradient extends Component {
12 | render() {
13 | return (
14 |
17 | );
18 | }
19 | }
20 | SvgRadialGradient = AnimatedSvgGradientFix(SvgRadialGradient);
21 | SvgRadialGradient = AnimatedSvgStateFix(SvgRadialGradient, args, { cancelSetNativeProps: true });
22 | SvgRadialGradient = Animated.createAnimatedComponent(SvgRadialGradient);
23 | export default SvgRadialGradient;
24 |
--------------------------------------------------------------------------------
/src/Instagram.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs';
2 |
3 | const Instagram = {
4 | location: {
5 | search: async params =>
6 | (await fetch(
7 | `https://api.instagram.com/v1/locations/search?${qs.stringify(
8 | params
9 | )}`
10 | )).json(),
11 | media: {
12 | recent: async (id, params) =>
13 | (await fetch(
14 | `https://api.instagram.com/v1/locations/${
15 | id
16 | }/media/recent?${qs.stringify(params)}`
17 | )).json()
18 | }
19 | },
20 | media: {
21 | search: async (id, params) =>
22 | (await fetch(
23 | `https://api.instagram.com/v1/media/search?${qs.stringify(
24 | params
25 | )}`
26 | )).json()
27 | }
28 | };
29 | export default Instagram;
30 |
--------------------------------------------------------------------------------
/src/components/LolaTravelProfileMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, Image } from 'react-native';
3 |
4 | const colorRobin = '#AFAFA5';
5 |
6 | const styles = StyleSheet.create({
7 | container: {
8 | padding: 10,
9 | alignSelf: 'stretch',
10 | alignItems: 'center',
11 | justifyContent: 'center'
12 | },
13 | image: {
14 | width: 50,
15 | height: 50,
16 | borderRadius: 25,
17 | marginBottom: 10
18 | },
19 | text: {
20 | textAlign: 'center',
21 | width: 200,
22 | color: colorRobin,
23 | fontWeight: 'bold'
24 | }
25 | });
26 |
27 | const ProfileMessage = ({source, text}) => (
28 |
29 |
30 | {text}
31 |
32 | );
33 | export default ProfileMessage;
34 |
--------------------------------------------------------------------------------
/src/components/OperatorFormMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Message from './OperatorMessage';
4 | import MessageText from './OperatorMessageText';
5 | import MessageButton from './OperatorMessageButton';
6 | import MessageFormInput from './OperatorMessageFormInput';
7 | import MessageFormSelect from './OperatorMessageFormSelect';
8 |
9 | const FormMessage = ({ text, items, onPress, buttonTitle, selected, valid, ...props }) => (
10 |
11 | {!!text && }
12 | {items.map((item, i) => {
13 | if (item.type === 'input') {
14 | return ;
15 | } else if (item.type === 'select') {
16 | return ;
17 | }
18 | })}
19 |
20 |
21 | );
22 | export default FormMessage;
23 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "react-native-examples",
4 | "slug": "react-native-examples",
5 | "privacy": "public",
6 | "sdkVersion": "33.0.0",
7 | "platforms": [
8 | "ios",
9 | "android",
10 | "web"
11 | ],
12 | "version": "1.0.0",
13 | "orientation": "portrait",
14 | "icon": "./assets/icon.png",
15 | "splash": {
16 | "image": "./assets/splash.png",
17 | "resizeMode": "contain",
18 | "backgroundColor": "#ffffff"
19 | },
20 | "updates": {
21 | "fallbackToCacheTimeout": 0
22 | },
23 | "assetBundlePatterns": [
24 | "**/*"
25 | ],
26 | "ios": {
27 | "supportsTablet": true
28 | },
29 | "description": "UI Patterns, Animated, SVG, D3, Flubber, and more. Source at https://github.com/ethantran/react-native-examples"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/OperatorMessageButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
3 |
4 | const colorPrimary = '#4EAAF0';
5 |
6 | const styles = StyleSheet.create({
7 | button: {
8 | alignItems: 'center',
9 | padding: 20
10 | },
11 | buttonTitle: {
12 | fontWeight: 'bold',
13 | color: '#CCCCCC'
14 | },
15 | valid: {
16 | color: '#61B56B'
17 | },
18 | primary: {
19 | color: colorPrimary
20 | }
21 | });
22 |
23 | const MessageButton = ({onPress, title, style, valid, primary}) => (
24 |
25 |
26 | {title}
31 |
32 |
33 | );
34 | export default MessageButton;
35 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/polyObject3Ds.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Once a poly asset is turned into an object3d using a loader you can clone it instead of using a loader again
3 | */
4 | import { SELECT_ASSET, LOAD_ASSETS, LOAD_ASSET } from '../actions/poly';
5 | import { RESET } from '../actions/ar';
6 |
7 | const initialState = {};
8 |
9 | export default function(state = initialState, action) {
10 | switch (action.type) {
11 | case SELECT_ASSET:
12 | return {
13 | ...state,
14 | [action.asset.name]: action.object3D
15 | };
16 | case LOAD_ASSETS:
17 | return {
18 | ...state,
19 | ...action.object3Ds
20 | };
21 | case LOAD_ASSET:
22 | return {
23 | ...state,
24 | [action.assetName]: action.object3D
25 | };
26 | case RESET:
27 | return initialState;
28 | default:
29 | return state;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgPropStringFix.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | * Problem: Props cannot be animated, too many times you have to do val.toString() in setNativeProps
5 | * Solution: Use a higher order component to do that for you
6 | */
7 |
8 | const KEYS = ['strokeWidth', 'strokeOpacity', 'fillOpacity'];
9 |
10 | export default function SvgPropStringFix(WrappedComponent, propKeys = []) {
11 | propKeys = [...KEYS, ...propKeys];
12 | return class extends Component {
13 | setNativeProps = props => {
14 | propKeys.reduce((acc, key) => {
15 | const val = props[key];
16 | if (val != null) {
17 | acc[key] = val.toString();
18 | }
19 | return acc;
20 | }, props);
21 | this._component && this._component.setNativeProps(props);
22 | };
23 | render() {
24 | return (
25 | (this._component = component)}
27 | {...this.props}
28 | />
29 | );
30 | }
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/OperatorMessageFormInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, TextInput } from 'react-native';
3 |
4 | const colorLight = '#E8E8E8';
5 |
6 | const styles = StyleSheet.create({
7 | inputContainer: {
8 | borderBottomWidth: StyleSheet.hairlineWidth,
9 | borderColor: colorLight,
10 | paddingHorizontal: 10,
11 | paddingVertical: 15
12 | },
13 | label: {
14 | fontWeight: 'bold',
15 | color: '#666666',
16 | marginHorizontal: 5,
17 | marginBottom: 15
18 | },
19 | optional: {
20 | fontSize: 12,
21 | fontWeight: '100'
22 | },
23 | input: {
24 | fontSize: 25,
25 | color: '#666666',
26 | height: 30,
27 | marginLeft: -5,
28 | marginBottom: 15
29 | }
30 | });
31 |
32 | const MessageFormInput = ({ labelText, optional, placeholder }) => (
33 |
34 | {labelText.toUpperCase()}{optional && (optional)}
35 |
36 |
37 | );
38 | export default MessageFormInput;
39 |
--------------------------------------------------------------------------------
/src/screens/ARExample/constants.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 |
3 | export const POLY_API_KEY = 'AIzaSyDVowQMZQfFz7XsURJciLIQXZpgBDLfqIc';
4 | export const GOOGLE_MAPS_GEOCODING_API_KEY =
5 | 'AIzaSyBzaFQvsdY9Cx5aB6tXMxrYm3jNchcvFhk';
6 | export const GOOGLE_PLACES_API_KEY = 'AIzaSyBUu7bIlrA-3YvwwLZQpKMKmuMcTZtBByw';
7 | export const GOOGLE_ELEVATION_API_KEY = 'AIzaSyBla1qvxgQfV4DOK8lllUkFhvsgki1kX-Q';
8 | export const PREDICTHQ_ACCESS_TOKEN = '2tOvVru3zpLQ5wa5qsuolUDzYqc5gC';
9 | export const GEOMETRIES = [new THREE.BoxGeometry(1, 2, 1)];
10 | export const MATERIALS = [
11 | //red
12 | new THREE.MeshBasicMaterial({
13 | color: 0xff0000
14 | }),
15 | //blue
16 | new THREE.MeshBasicMaterial({
17 | color: 0x00ff00
18 | }),
19 | //lime
20 | new THREE.MeshBasicMaterial({
21 | color: 0x0000ff
22 | }),
23 | //yellow
24 | new THREE.MeshBasicMaterial({
25 | color: 0xffff00
26 | }),
27 | //aqua
28 | new THREE.MeshBasicMaterial({
29 | color: 0x00ffff
30 | }),
31 | //fuchsia
32 | new THREE.MeshBasicMaterial({
33 | color: 0xff00ff
34 | }),
35 | //white
36 | new THREE.MeshBasicMaterial({
37 | color: 0xffffff
38 | })
39 | ];
40 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgAnimatedPropFix.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Animated } from 'react-native';
3 | import omitBy from 'lodash/omitBy';
4 |
5 | /**
6 | * Problem: Setting an animated prop on the native components throws errors
7 | * Solution: Use a higher order component to omit them down the chain this should be wrapped first so you don't accidentally send it to the native component through calling setNativeProps manually such as in componentWillReceiveProps.
8 | */
9 |
10 | function isAnimated(val, key) {
11 | return val instanceof Animated.Value
12 | || val instanceof Animated.ValueXY
13 | || val instanceof Animated.Interpolation;
14 | }
15 |
16 | export default function SvgAnimatedPropFix(WrappedComponent) {
17 | return class extends Component {
18 | setNativeProps = (props) => {
19 | this._component && this._component.setNativeProps(props);
20 | }
21 | render() {
22 | const props = omitBy(this.props, isAnimated);
23 | return (
24 | (this._component = component)}
26 | {...props}
27 | />
28 | );
29 | }
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/src/screens/SvgInterpolatePointAlongPathAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import SvgInterpolatePointAtLength from '../components/AnimatedSvgInterpolatePointAtLength';
6 | import { d as TwitterIconSvgPath } from '../components/TwitterIconSvgPath';
7 |
8 | const d = TwitterIconSvgPath;
9 |
10 | export default class SvgInterpolatePointAlongPathAnimation extends Component {
11 | constructor(props) {
12 | super(props);
13 | this.t = new Animated.Value(0);
14 | }
15 |
16 | componentDidMount() {
17 | this.animate();
18 | }
19 |
20 | animate() {
21 | Animated.parallel([
22 | Animated.timing(this.t, {
23 | toValue: this.t.__getValue() === 0 ? 1 : 0,
24 | duration: 5000
25 | })
26 | ]).start(() => this.animate());
27 | }
28 |
29 | render() {
30 | const { width, height } = Dimensions.get('window');
31 | return (
32 |
33 |
37 |
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgStateFix.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import omit from 'lodash/omit';
3 |
4 | /**
5 | * Problem: Some props cannot be animated through setNativeProps
6 | * Solution: Use state for those and use setNativeProps for the rest
7 | */
8 |
9 | function createState(props, keys) {
10 | return keys.reduce((acc, key) => {
11 | const value = props[key];
12 | if (value != null) {
13 | acc[key] = value;
14 | }
15 | return acc;
16 | }, {});
17 | }
18 |
19 | export default function SvgStateFix(WrappedComponent, propToStateKeys = [], { cancelSetNativeProps } = {}) {
20 | return class extends Component {
21 | state = createState(this.props, propToStateKeys);
22 | setNativeProps = (props) => {
23 | if (!cancelSetNativeProps) {
24 | const nativeProps = omit(props, propToStateKeys);
25 | this._component && this._component.setNativeProps(nativeProps);
26 | }
27 | const newState = createState(props, propToStateKeys);
28 | this.setState(newState);
29 | }
30 | render() {
31 | return (
32 | (this._component = component)}
34 | {...this.props}
35 | {...this.state}
36 | />
37 | );
38 | }
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/src/screens/ARExample/LongpressControl.js:
--------------------------------------------------------------------------------
1 | import { placeObject3DFromCamera } from './utils';
2 |
3 | export default class LongpressControl {
4 | constructor(camera, distance = 3, duration = 1000) {
5 | this.camera = camera;
6 | this.distance = distance;
7 | this.duration = duration;
8 | }
9 |
10 | handlePanResponderGrant(event, gestureState) {
11 | this.timeoutId = setTimeout(() => {
12 | console.log('longpress');
13 | this.triggerUpdate = true;
14 | }, this.duration);
15 | }
16 |
17 | handlePanResponderMove(event, gestureState) {
18 | if (event.nativeEvent.touches.length > 1) {
19 | clearTimeout(this.timeoutId);
20 | this.triggerUpdate = false;
21 | }
22 | }
23 |
24 | handlePanResponderRelease() {
25 | clearTimeout(this.timeoutId);
26 | this.triggerUpdate = false;
27 | this.detach();
28 | }
29 |
30 | handlePanResponderTerminate() {
31 | clearTimeout(this.timeoutId);
32 | this.triggerUpdate = false;
33 | this.detach();
34 | }
35 |
36 | attach(object) {
37 | this.object = object;
38 | }
39 |
40 | detach() {
41 | this.object = undefined;
42 | }
43 |
44 | update() {
45 | if (this.triggerUpdate && this.object) {
46 | placeObject3DFromCamera(this.camera, this.object, this.distance);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/AnchorRadialChild.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, Animated } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | position: 'absolute',
7 | alignItems: 'center',
8 | justifyContent: 'center',
9 | overflow: 'hidden',
10 | borderColor: '#7BF9DA'
11 | }
12 | });
13 |
14 | export default class AnchorRadialChild extends Component {
15 | static defaultProps = {
16 | borderWidth: 3,
17 | playing: false
18 | }
19 | borderWidth = new Animated.Value(0)
20 | componentDidMount() {
21 | this.onPlayingUpdate(this.props.playing);
22 | }
23 | componentWillReceiveProps(nextProps) {
24 | if (nextProps.playing !== this.props.playing) {
25 | this.onPlayingUpdate(nextProps.playing);
26 | }
27 | }
28 | onPlayingUpdate(playing) {
29 | Animated.spring(this.borderWidth, {
30 | toValue: playing ? this.props.borderWidth : 0,
31 | friction: 5
32 | }).start();
33 | }
34 | render() {
35 | return (
36 |
42 | {this.props.children}
43 |
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/screens/SvgD3SankeyBasic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Scale from 'd3-scale';
5 |
6 | import Sankey from '../components/AnimatedSvgD3Sankey';
7 | import data from '../data/sankeyData';
8 |
9 | export default class SvgSankeyBasic extends Component {
10 | constructor(props) {
11 | super(props);
12 | }
13 |
14 | render() {
15 | const { width, height } = Dimensions.get('window');
16 | const extent = [[1, 1], [width - 1, height - 6]];
17 | const color = d3Scale.scaleOrdinal(d3Scale.schemeCategory10);
18 | return (
19 |
20 |
36 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/screens/SvgD3InterpolatePathAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import InterpolatePath from '../components/AnimatedSvgD3InterpolatePath';
7 |
8 | const d1 = 'm72.5,167.5c1,0 67,-36 151,2c84,38 166,-15 165.5,-15.5';
9 | const d2 = 'm82.5,187.5c1,0 87,-86 181,2c88,88 186,-15 185.5,-85.5';
10 |
11 | export default class SvgD3InterpolatePathAnimation extends Component {
12 | constructor(props) {
13 | super(props);
14 | this.t = new Animated.Value(Math.random());
15 | }
16 |
17 | componentDidMount() {
18 | this.animate();
19 | }
20 |
21 | animate = () => {
22 | Animated.parallel([
23 | Animated.spring(this.t, {
24 | toValue: Math.random()
25 | })
26 | ]).start(() => this.animate());
27 | };
28 |
29 | render() {
30 | const { width, height } = Dimensions.get('window');
31 | return (
32 |
33 |
43 |
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/SnapchatQuickChatItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, Image } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import IconSvgPath from '../components/SnapchatIconSvgPath';
6 |
7 | const styles = StyleSheet.create({
8 | container: {
9 | alignItems: 'center',
10 | backgroundColor: 'white',
11 | borderRadius: 10,
12 | paddingVertical: 10,
13 | width: 80,
14 | marginRight: 10
15 | },
16 | imageContainer: {
17 | marginBottom: 10
18 | },
19 | image: {
20 | ...StyleSheet.absoluteFillObject
21 | },
22 | name: {
23 | backgroundColor: 'transparent',
24 | color: 'black',
25 | fontWeight: 'bold',
26 | fontSize: 11
27 | }
28 | });
29 |
30 | const QuickChatItem = ({ name, source }) => (
31 |
32 |
33 |
34 |
46 |
47 | {name}
48 |
49 | );
50 | export default QuickChatItem;
51 |
--------------------------------------------------------------------------------
/src/components/OperatorSelectMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text } from 'react-native';
3 |
4 | import Message from './OperatorMessage';
5 | import MessageText from './OperatorMessageText';
6 | import MessageButton from './OperatorMessageButton';
7 |
8 | const colorLight = '#E8E8E8';
9 | const colorPrimary = '#4EAAF0';
10 |
11 | const styles = StyleSheet.create({
12 | container: {
13 | padding: 20,
14 | borderBottomWidth: 1,
15 | borderColor: colorLight
16 | },
17 | containerSelected: {
18 | backgroundColor: colorPrimary,
19 | borderColor: colorPrimary
20 | },
21 | text: {
22 | fontWeight: 'bold',
23 | color: colorPrimary
24 | },
25 | textSelected: {
26 | color: 'white'
27 | }
28 | });
29 |
30 | const SelectMessage = ({ text, items, selected, onPress, buttonTitle, valid, ...props }) => (
31 |
32 | {!!text && }
33 | {items.map((item, i) => {
34 | return (
35 |
39 | {item}
43 |
44 | );
45 | })}
46 |
47 |
48 | );
49 | export default SelectMessage;
50 |
--------------------------------------------------------------------------------
/src/screens/SvgD3ContourDensityBasic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Scale from 'd3-scale';
5 | import * as d3Array from 'd3-array';
6 |
7 | import ContourDensity from '../components/AnimatedSvgD3ContourDensity';
8 | import data from '../data/contourDensityData';
9 |
10 | export default class SvgContourDensityBasic extends Component {
11 | constructor(props) {
12 | super(props);
13 | }
14 |
15 | render() {
16 | const { width, height } = Dimensions.get('window');
17 | const x = d3Scale.scaleLinear().rangeRound([10, width - 10]);
18 | const y = d3Scale.scaleLinear().rangeRound([height - 10, 10]);
19 | x.domain(d3Array.extent(data, d => d.waiting)).nice();
20 | y.domain(d3Array.extent(data, d => d.eruptions)).nice();
21 | const size = [width, height];
22 | const bandwidth = 10;
23 | return (
24 |
25 |
38 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/screens/SvgD3PackCircles.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Hierarchy from 'd3-hierarchy';
5 |
6 | import Pack from '../components/AnimatedSvgD3HierarchyPack';
7 | import Circle from '../components/AnimatedSvgCircle';
8 | import data from '../data/packData';
9 |
10 | export default class SvgPackCircles extends Component {
11 | constructor(props) {
12 | super(props);
13 | }
14 |
15 | render() {
16 | const { width, height } = Dimensions.get('window');
17 | const root = d3Hierarchy
18 | .hierarchy(data)
19 | .sum(d => d.size)
20 | .sort((a, b) => b.value - a.value);
21 | const size = [width, width];
22 | return (
23 |
24 |
39 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgD3InterpolatePath.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Problem: What is the best way to animate a path with animated.value?
3 | * Solution: This demonstrates how you can do it with d3 interpolate
4 | */
5 | import React, { Component } from 'react';
6 | import { Animated } from 'react-native';
7 | import * as d3 from 'd3-interpolate-path';
8 |
9 | import Path from './AnimatedSvgPath';
10 |
11 | function createInterpolator(props) {
12 | return d3.interpolatePath(props.d1, props.d2);
13 | }
14 |
15 | class SvgD3InterpolatePath extends Component {
16 | static defaultProps = {
17 | t: 0
18 | };
19 | constructor(props) {
20 | super(props);
21 | this.interpolator = createInterpolator(props);
22 | }
23 | setNativeProps = props => {
24 | if (props.t) {
25 | props.d = this.interpolator(props.t);
26 | }
27 | this._component && this._component.setNativeProps(props);
28 | };
29 | shouldComponentUpdate(nextProps) {
30 | if (nextProps.d1 !== this.props.d1 || nextProps.d2 !== this.props.d2) {
31 | this.interpolator = createInterpolator(nextProps);
32 | return true;
33 | }
34 | return false;
35 | }
36 | render() {
37 | const { t, ...props } = this.props;
38 | const d = this.interpolator(t);
39 | return (
40 | (this._component = component)}
42 | {...props}
43 | d={d}
44 | />
45 | );
46 | }
47 | }
48 | SvgD3InterpolatePath = Animated.createAnimatedComponent(SvgD3InterpolatePath);
49 | export default SvgD3InterpolatePath;
50 |
--------------------------------------------------------------------------------
/src/screens/ARExample/PolyAssetListView.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ScrollView,
4 | TouchableOpacity,
5 | Text,
6 | Dimensions,
7 | ActivityIndicator
8 | } from 'react-native';
9 | import PolyAssetView from './PolyAssetView';
10 |
11 | const { width } = Dimensions.get('window');
12 |
13 | export default ({ assets, canLoadMore, onLoadMore, loadingMore, onPress }) => (
14 |
24 | {assets.map((asset, i) => (
25 |
26 | ))}
27 | {canLoadMore && (
28 |
39 | {loadingMore ? (
40 |
41 | ) : (
42 |
43 | Load more
44 |
45 | )}
46 |
47 | )}
48 |
49 | );
50 |
--------------------------------------------------------------------------------
/src/components/OperatorInputMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, TextInput } from 'react-native';
3 |
4 | import Message from './OperatorMessage';
5 | import MessageText from './OperatorMessageText';
6 | import MessageButton from './OperatorMessageButton';
7 |
8 | const colorLight = '#E8E8E8';
9 |
10 | const styles = StyleSheet.create({
11 | inputContainer: {
12 | borderBottomWidth: StyleSheet.hairlineWidth,
13 | borderColor: colorLight,
14 | paddingHorizontal: 10,
15 | paddingVertical: 15
16 | },
17 | label: {
18 | fontWeight: 'bold',
19 | color: '#666666',
20 | marginHorizontal: 5,
21 | marginBottom: 15
22 | },
23 | input: {
24 | fontSize: 30,
25 | color: '#666666',
26 | height: 30,
27 | marginLeft: -5,
28 | marginBottom: 15
29 | },
30 | help: {
31 | color: '#C7C7C7',
32 | fontWeight: '100',
33 | marginHorizontal: 5
34 | }
35 | });
36 |
37 | const InputMessage = ({ text, labelText, placeholder, onPress, buttonTitle, helpText, valid, ...props }) => (
38 |
39 |
40 |
41 | {labelText.toUpperCase()}
42 |
43 | {!!helpText && {helpText}}
44 |
45 |
46 |
47 | );
48 | export default InputMessage;
49 |
--------------------------------------------------------------------------------
/src/screens/SvgD3TreeBasic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Hierarchy from 'd3-hierarchy';
5 |
6 | import Tree from '../components/AnimatedSvgD3HierarchyTree';
7 | import LinkHorizontal from '../components/AnimatedSvgD3ShapeLinkHorizontal';
8 | import data from '../data/hierarchyData';
9 |
10 | export default class SvgTreeBasic extends Component {
11 | constructor(props) {
12 | super(props);
13 | }
14 |
15 | render() {
16 | const { width, height } = Dimensions.get('window');
17 | const root = d3Hierarchy
18 | .stratify()
19 | .parentId(d => d.id.substring(0, d.id.lastIndexOf('.')))(data).sort(
20 | (a, b) => a.height - b.height || a.id.localeCompare(b.id)
21 | );
22 | const size = [width, width];
23 | return (
24 |
25 |
40 |
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/screens/ARExample/TouchVisualizer.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import { Dimensions } from 'react-native';
3 |
4 | export default class TouchVisualizer {
5 | constructor(scene, camera) {
6 | this.scene = scene;
7 | this.camera = camera;
8 | this.arrows = [];
9 | }
10 |
11 | handlePanResponderGrant(event, gestureState) {
12 | const { width, height } = Dimensions.get('window');
13 | const touches = event.nativeEvent.touches;
14 | this.clean();
15 | touches.forEach(({ locationX, locationY }) => {
16 | let pointerVector = new THREE.Vector2();
17 | pointerVector.set(
18 | locationX / width * 2 - 1,
19 | -(locationY / height * 2) + 1
20 | );
21 | let raycaster = new THREE.Raycaster();
22 | raycaster.setFromCamera(pointerVector, this.camera);
23 | const arrow = new THREE.ArrowHelper(
24 | // pointerVector.normalize(),
25 | // this.camera.position,
26 | raycaster.ray.direction,
27 | raycaster.ray.origin,
28 | 100,
29 | Math.random() * 0xffffff
30 | );
31 | this.scene.add(arrow);
32 | this.arrows.push(arrow);
33 | });
34 | }
35 |
36 | handlePanResponderRelease(event, gestureState) {
37 |
38 | }
39 |
40 | handlePanResponderTerminate(event, gestureState) {
41 | this.clean();
42 | }
43 |
44 | clean() {
45 | this.arrows.forEach(arrow => {
46 | this.scene.remove(arrow);
47 | });
48 | this.arrows = [];
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/screens/SvgD3TreemapBasic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Hierarchy from 'd3-hierarchy';
5 |
6 | import Treemap from '../components/AnimatedSvgD3HierarchyTreemap';
7 | import LinkHorizontal from '../components/AnimatedSvgD3ShapeLinkHorizontal';
8 | import data from '../data/hierarchyData';
9 |
10 | export default class SvgTreemapBasic extends Component {
11 | constructor(props) {
12 | super(props);
13 | }
14 |
15 | render() {
16 | const { width, height } = Dimensions.get('window');
17 | const root = d3Hierarchy
18 | .stratify()
19 | .parentId(d => d.id.substring(0, d.id.lastIndexOf('.')))(data).sort(
20 | (a, b) => a.height - b.height || a.id.localeCompare(b.id)
21 | );
22 | const size = [width, width];
23 | return (
24 |
25 |
40 |
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/screens/SvgRectAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Rect from '../components/AnimatedSvgRect';
8 |
9 | export default class SvgRectAnimation extends Component {
10 | constructor(props) {
11 | super(props);
12 | const { width, height } = Dimensions.get('window');
13 | this.width = new Animated.Value(randomNumber(50, width));
14 | this.height = new Animated.Value(randomNumber(50, height));
15 | }
16 |
17 | componentDidMount() {
18 | this.animate();
19 | }
20 |
21 | animate = () => {
22 | const { width, height } = Dimensions.get('window');
23 | Animated.parallel([
24 | Animated.spring(this.width, {
25 | toValue: randomNumber(50, width)
26 | }),
27 | Animated.spring(this.height, {
28 | toValue: randomNumber(50, height)
29 | })
30 | ]).start(() => this.animate());
31 | };
32 |
33 | render() {
34 | const { width, height } = Dimensions.get('window');
35 | return (
36 |
37 |
45 |
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/LolaTravelSliderItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, Image } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | shadowColor: 'rgba(0, 0, 0, 0.12)',
7 | shadowOpacity: 0.8,
8 | shadowRadius: 6,
9 | shadowOffset: {
10 | height: 1,
11 | width: 2,
12 | },
13 | borderRadius: 15
14 | },
15 | imageContainer: {
16 | borderTopLeftRadius: 15,
17 | borderTopRightRadius: 15,
18 | overflow: 'hidden'
19 | },
20 | image: {
21 | height: 100,
22 | borderTopLeftRadius: 15,
23 | borderTopRightRadius: 15,
24 | overflow: 'hidden'
25 | },
26 | title: {
27 | fontSize: 20,
28 | fontWeight: 'bold',
29 | color: 'white',
30 | position: 'absolute',
31 | bottom: 10,
32 | left: 10,
33 | backgroundColor: 'transparent'
34 | },
35 | textBorderRadiusFix: {
36 | // https://github.com/facebook/react-native/issues/29
37 | borderBottomLeftRadius: 15,
38 | borderBottomRightRadius: 15,
39 | overflow: 'hidden'
40 | },
41 | text: {
42 | padding: 10,
43 | }
44 | });
45 |
46 | const SliderItem = ({source, title, text}) => (
47 |
48 |
49 |
50 | {title}
51 |
52 |
53 | {text}
54 |
55 |
56 | );
57 | export default SliderItem;
58 |
--------------------------------------------------------------------------------
/src/screens/SvgPathDrawing.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import { svgPathProperties } from 'svg-path-properties';
5 |
6 | import randomColor from '../randomColor';
7 | import Path from '../components/AnimatedSvgPath';
8 | import VivusHi from '../components/VivusHi';
9 |
10 | export default class SvgCircleAnimation extends Component {
11 | constructor(props) {
12 | super(props);
13 | const properties = svgPathProperties(VivusHi);
14 | this.length = properties.getTotalLength();
15 | this.strokeDashoffset = new Animated.Value(this.length);
16 | }
17 |
18 | componentDidMount() {
19 | this.animate();
20 | }
21 |
22 | animate = () => {
23 | this.strokeDashoffset.setValue(this.length);
24 | Animated.sequence([
25 | Animated.delay(1000),
26 | Animated.timing(this.strokeDashoffset, {
27 | toValue: 0,
28 | duration: 2000
29 | })
30 | ]).start(() => this.animate());
31 | };
32 |
33 | render() {
34 | const { width, height } = Dimensions.get('window');
35 | return (
36 |
37 |
46 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/SnapchatSearchItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, Image } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import IconSvgPath from '../components/SnapchatIconSvgPath';
6 |
7 | const styles = StyleSheet.create({
8 | container: {
9 | flexDirection: 'row',
10 | backgroundColor: 'white',
11 | marginBottom: 10
12 | },
13 | imageContainer: {
14 | marginRight: 20,
15 | },
16 | image: {
17 | ...StyleSheet.absoluteFillObject
18 | },
19 | meta: {
20 | justifyContent: 'center'
21 | },
22 | name: {
23 | backgroundColor: 'transparent',
24 | fontSize: 20
25 | },
26 | username: {
27 | color: '#D3D5D9',
28 | fontSize: 10
29 | },
30 | count: {
31 | color: '#D3D5D9',
32 | fontSize: 10
33 | }
34 | });
35 |
36 | const QuickChatItem = ({ source, name, username, count }) => (
37 |
38 |
39 |
40 |
52 |
53 |
54 | {name}
55 | {username} {count}
56 |
57 |
58 | );
59 | export default QuickChatItem;
60 |
--------------------------------------------------------------------------------
/src/screens/SvgD3Axis.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Scale from 'd3-scale';
5 |
6 | import SvgD3Axis from '../components/SvgD3Axis';
7 |
8 | export default class SvgD3AxisExample extends Component {
9 | render() {
10 | const { width, height } = Dimensions.get('window');
11 | const minTranslateX = 50;
12 | const minTranslateY = 50;
13 | const max = Math.min(width, height) / 1.5;
14 | const scale = d3Scale.scaleLinear().range([0, max]);
15 | return (
16 |
17 |
43 |
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/GoogleMaps.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs';
2 |
3 | const GoogleMaps = {
4 | geocode: async params =>
5 | (await fetch(
6 | `https://maps.googleapis.com/maps/api/geocode/json?${qs.stringify(
7 | params
8 | )}`
9 | )).json(),
10 | place: {
11 | nearbysearch: async params =>
12 | (await fetch(
13 | `https://maps.googleapis.com/maps/api/place/nearbysearch/json?${qs.stringify(
14 | params
15 | )}`
16 | )).json(),
17 | textsearch: async params =>
18 | (await fetch(
19 | `https://maps.googleapis.com/maps/api/place/textsearch/json?${qs.stringify(
20 | params
21 | )}`
22 | )).json(),
23 | details: async params =>
24 | (await fetch(
25 | `https://maps.googleapis.com/maps/api/place/details/json?${qs.stringify(
26 | params
27 | )}`
28 | )).json(),
29 | autocomplete: async params =>
30 | (await fetch(
31 | `https://maps.googleapis.com/maps/api/place/autocomplete/json?${qs.stringify(
32 | params
33 | )}`
34 | )).json(),
35 | queryautocomplete: async params =>
36 | (await fetch(
37 | `https://maps.googleapis.com/maps/api/place/queryautocomplete/json?${qs.stringify(
38 | params
39 | )}`
40 | )).json()
41 | },
42 | elevation: async params =>
43 | (await fetch(
44 | `https://maps.googleapis.com/maps/api/elevation/json?${qs.stringify(
45 | params
46 | )}`
47 | )).json()
48 | };
49 |
50 | export default GoogleMaps;
51 |
--------------------------------------------------------------------------------
/src/screens/SvgD3ContourBasic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Scale from 'd3-scale';
5 | import * as d3Array from 'd3-array';
6 | import * as d3ScaleChromatic from 'd3-scale-chromatic';
7 |
8 | import Contour from '../components/AnimatedSvgD3Contour';
9 | import contourData from '../data/contourData';
10 |
11 | const contourWidth = 10;
12 | const contourHeight = 10;
13 | const thresholdMin = 1;
14 | const thresholdMax = 21;
15 |
16 | export default class SvgContourBasic extends Component {
17 | constructor(props) {
18 | super(props);
19 | }
20 |
21 | render() {
22 | const { width, height } = Dimensions.get('window');
23 | const data = contourData(contourWidth, contourHeight);
24 | const thresholds = d3Array
25 | .range(thresholdMin, thresholdMax)
26 | .map(p => Math.pow(2, p));
27 | const color = d3Scale
28 | .scaleLog()
29 | .domain(d3Array.extent(thresholds))
30 | .interpolate(() => d3ScaleChromatic.interpolateYlGnBu);
31 | const size = [contourWidth, contourHeight];
32 | const scale = width / 10;
33 | return (
34 |
35 |
46 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/OperatorMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Image } from 'react-native';
3 |
4 | import MessageMeta from './OperatorMessageMeta';
5 |
6 | const colorLight = '#E8E8E8';
7 | const colorPrimary = '#4EAAF0';
8 |
9 | const styles = StyleSheet.create({
10 | container: {
11 | borderRadius: 10,
12 | marginTop: 20,
13 | marginBottom: 10
14 | },
15 | containerMe: {
16 | backgroundColor: colorPrimary,
17 | marginLeft: 50,
18 | justifyContent: 'flex-end',
19 | borderRadius: 10,
20 | },
21 | containerOther: {
22 | backgroundColor: 'white',
23 | marginRight: 50
24 | },
25 | fullWidth: {
26 | marginLeft: 0,
27 | marginRight: 0
28 | },
29 | avatarContainer: {
30 | position: 'absolute',
31 | top: -18,
32 | borderTopRightRadius: 7,
33 | borderTopLeftRadius: 7,
34 | borderBottomRightRadius: 7,
35 | borderBottomWidth: 2,
36 | borderRightWidth: 2,
37 | borderColor: colorLight,
38 | overflow: 'hidden'
39 | },
40 | avatar: {
41 | width: 30,
42 | height: 30,
43 | borderTopRightRadius: 7,
44 | borderTopLeftRadius: 7,
45 | borderBottomRightRadius: 7,
46 | overflow: 'hidden'
47 | }
48 | });
49 |
50 | const Message = ({ me, fullWidth, user, children }) => (
51 |
52 |
57 | {!me && }
58 | {children}
59 |
60 |
61 | );
62 | export default Message;
63 |
--------------------------------------------------------------------------------
/src/screens/SvgFlubberAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import FlubberPath from '../components/AnimatedSvgFlubberPath';
7 | import { d as GithubIconSvgPath } from '../components/GithubIconSvgPath';
8 | import { d as TwitterIconSvgPath } from '../components/TwitterIconSvgPath';
9 |
10 | const fromShape = GithubIconSvgPath;
11 | const toShape = TwitterIconSvgPath;
12 | const interpolatorType = 'interpolate';
13 | const options = { maxSegmentLength: 5 };
14 |
15 | export default class SvgFlubberAnimation extends Component {
16 | constructor(props) {
17 | super(props);
18 | this.t = new Animated.Value(Math.random());
19 | }
20 |
21 | componentDidMount() {
22 | this.animate();
23 | }
24 |
25 | animate = () => {
26 | Animated.parallel([
27 | Animated.spring(this.t, {
28 | toValue: this.t.__getValue() === 0 ? 1 : 0
29 | })
30 | ]).start(() => this.animate());
31 | };
32 |
33 | render() {
34 | const { width, height } = Dimensions.get('window');
35 | return (
36 |
37 |
49 |
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/screens/SvgCircleAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Circle from '../components/AnimatedSvgCircle';
8 |
9 | export default class SvgCircleAnimation extends Component {
10 | constructor(props) {
11 | super(props);
12 | const { width, height } = Dimensions.get('window');
13 | this.r = new Animated.Value(randomNumber(50, width / 2));
14 | this.cx = new Animated.Value(randomNumber(50, width));
15 | this.cy = new Animated.Value(randomNumber(50, height));
16 | }
17 |
18 | componentDidMount() {
19 | this.animate();
20 | }
21 |
22 | animate = () => {
23 | const { width, height } = Dimensions.get('window');
24 | Animated.parallel([
25 | Animated.spring(this.r, {
26 | toValue: randomNumber(50, width / 2)
27 | }),
28 | Animated.spring(this.cx, {
29 | toValue: randomNumber(50, width)
30 | }),
31 | Animated.spring(this.cy, {
32 | toValue: randomNumber(50, height)
33 | })
34 | ]).start(() => this.animate());
35 | };
36 |
37 | render() {
38 | const { width, height } = Dimensions.get('window');
39 | return (
40 |
41 |
50 |
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/screens/SvgD3PieAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomNumber from '../randomNumber';
6 | import randomColor from '../randomColor';
7 | import Pie from '../components/AnimatedSvgD3ShapePie';
8 |
9 | const dataLength = 10;
10 | const min = 0;
11 | const max = 100;
12 |
13 | export default class SvgPieAnimation extends Component {
14 | constructor(props) {
15 | super(props);
16 | this.data = Array(dataLength).fill().map((_, index) => ({
17 | index,
18 | value: new Animated.Value(randomNumber(min, max))
19 | }));
20 | }
21 |
22 | componentDidMount() {
23 | this.animate();
24 | }
25 |
26 | animate() {
27 | Animated.sequence(
28 | this.data.map(item =>
29 | Animated.spring(item.value, {
30 | toValue: randomNumber(min, max)
31 | })
32 | )
33 | ).start(() => this.animate());
34 | }
35 |
36 | render() {
37 | const { width, height } = Dimensions.get('window');
38 | const maxSize = Math.min(width, height);
39 | const outerRadius = maxSize / 2;
40 | const innerRadius = randomNumber(0, outerRadius);
41 | const cornerRadius = randomNumber(0, 10);
42 | return (
43 |
44 |
57 |
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/screens/SvgLineAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Line from '../components/AnimatedSvgLine';
8 |
9 | export default class SvgLineAnimation extends Component {
10 | constructor(props) {
11 | super(props);
12 | const { width, height } = Dimensions.get('window');
13 | this.x1 = new Animated.Value(randomNumber(0, width));
14 | this.y1 = new Animated.Value(randomNumber(0, height));
15 | this.x2 = new Animated.Value(randomNumber(0, width));
16 | this.y2 = new Animated.Value(randomNumber(0, height));
17 | }
18 |
19 | componentDidMount() {
20 | this.animate();
21 | }
22 |
23 | animate = () => {
24 | const { width, height } = Dimensions.get('window');
25 | Animated.parallel([
26 | Animated.spring(this.x1, {
27 | toValue: randomNumber(0, width)
28 | }),
29 | Animated.spring(this.y1, {
30 | toValue: randomNumber(0, height)
31 | }),
32 | Animated.spring(this.x2, {
33 | toValue: randomNumber(0, width)
34 | }),
35 | Animated.spring(this.y2, {
36 | toValue: randomNumber(0, height)
37 | })
38 | ]).start(() => this.animate());
39 | };
40 |
41 | render() {
42 | const { width, height } = Dimensions.get('window');
43 | return (
44 |
45 |
54 |
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/noteAlignment.js:
--------------------------------------------------------------------------------
1 | export const leftRightDynamic = (align, y) => {
2 | if (align === 'dynamic' || align === 'left' || align === 'right') {
3 | if (y < 0) {
4 | align = 'top';
5 | } else {
6 | align = 'bottom';
7 | }
8 | }
9 | return align;
10 | };
11 |
12 | export const topBottomDynamic = (align, x) => {
13 | if (align === 'dynamic' || align === 'top' || align === 'bottom') {
14 | if (x < 0) {
15 | align = 'right';
16 | } else {
17 | align = 'left';
18 | }
19 | }
20 | return align;
21 | };
22 |
23 | const orientationTopBottom = ['topBottom', 'top', 'bottom'];
24 | const orientationLeftRight = ['leftRight', 'left', 'right'];
25 |
26 | export default ({
27 | padding = 0,
28 | bbox = { x: 0, y: 0, width: 0, height: 0 },
29 | align,
30 | orientation,
31 | offset = { x: 0, y: 0 }
32 | }) => {
33 | let x = -bbox.x;
34 | let y = 0; //-bbox.y
35 | if (orientationTopBottom.indexOf(orientation) !== -1) {
36 | align = topBottomDynamic(align, offset.x);
37 | if (
38 | (offset.y < 0 && orientation === 'topBottom') ||
39 | orientation === 'top'
40 | ) {
41 | y -= bbox.height + padding;
42 | } else {
43 | y += padding;
44 | }
45 |
46 | if (align === 'middle') {
47 | x -= bbox.width / 2;
48 | } else if (align === 'right') {
49 | x -= bbox.width;
50 | }
51 | } else if (orientationLeftRight.indexOf(orientation) !== -1) {
52 | align = leftRightDynamic(align, offset.y);
53 | if (
54 | (offset.x < 0 && orientation === 'leftRight') ||
55 | orientation === 'left'
56 | ) {
57 | x -= bbox.width + padding;
58 | } else {
59 | x += padding;
60 | }
61 |
62 | if (align === 'middle') {
63 | y -= bbox.height / 2;
64 | } else if (align === 'top') {
65 | y -= bbox.height;
66 | }
67 | }
68 | return { x, y };
69 | };
70 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgInterpolatePointAtLength.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Animated } from 'react-native';
4 | import { svgPathProperties } from 'svg-path-properties';
5 |
6 | import Circle from './AnimatedSvgCircle';
7 |
8 | type Props = {
9 | d: string,
10 | t: number
11 | };
12 |
13 | const defaultProps = {
14 | t: 0
15 | };
16 |
17 | class SvgInterpolatePointAtLength extends Component {
18 | props: Props;
19 | properties: typeof svgPathProperties;
20 | length: number;
21 | static defaultProps = typeof defaultProps;
22 | constructor(props) {
23 | super(props);
24 | this.properties = svgPathProperties(props.d);
25 | this.length = this.properties.getTotalLength();
26 | }
27 | setNativeProps(props) {
28 | if (props.t) {
29 | const point = this.properties.getPointAtLength(
30 | this.length * props.t
31 | );
32 | props.translateX = point.x;
33 | props.translateY = point.y;
34 | }
35 | this._component && this._component.setNativeProps(props);
36 | }
37 | componentWillReceiveProps(nextProps) {
38 | if (nextProps.d !== this.props.d) {
39 | this.properties = svgPathProperties(nextProps.d);
40 | this.length = this.properties.getTotalLength();
41 | }
42 | }
43 | render() {
44 | const { d, t, ...props } = this.props;
45 | const point = this.properties.getPointAtLength(this.length * t);
46 | const translateX = point.x;
47 | const translateY = point.y;
48 | return (
49 | (this._component = component)}
51 | {...props}
52 | translateX={translateX}
53 | translateY={translateY}
54 | />
55 | );
56 | }
57 | }
58 | SvgInterpolatePointAtLength.defaultProps = defaultProps;
59 | SvgInterpolatePointAtLength = Animated.createAnimatedComponent(
60 | SvgInterpolatePointAtLength
61 | );
62 | export default SvgInterpolatePointAtLength;
63 |
--------------------------------------------------------------------------------
/src/screens/SvgEllipseAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Ellipse from '../components/AnimatedSvgEllipse';
8 |
9 | export default class SvgEllipseAnimation extends Component {
10 | constructor(props) {
11 | super(props);
12 | const { width, height } = Dimensions.get('window');
13 | this.rx = new Animated.Value(randomNumber(50, width / 2));
14 | this.ry = new Animated.Value(randomNumber(50, height / 2));
15 | this.cx = new Animated.Value(randomNumber(50, width));
16 | this.cy = new Animated.Value(randomNumber(50, height));
17 | }
18 |
19 | componentDidMount() {
20 | this.animate();
21 | }
22 |
23 | animate = () => {
24 | const { width, height } = Dimensions.get('window');
25 | Animated.parallel([
26 | Animated.spring(this.rx, {
27 | toValue: randomNumber(50, width / 2)
28 | }),
29 | Animated.spring(this.ry, {
30 | toValue: randomNumber(50, height / 2)
31 | }),
32 | Animated.spring(this.cx, {
33 | toValue: randomNumber(50, width)
34 | }),
35 | Animated.spring(this.cy, {
36 | toValue: randomNumber(50, height)
37 | })
38 | ]).start(() => this.animate());
39 | };
40 |
41 | render() {
42 | const { width, height } = Dimensions.get('window');
43 | return (
44 |
45 |
55 |
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/screens/SvgTextAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Text from '../components/AnimatedSvgText';
8 |
9 | const min = 1;
10 | const max = 50;
11 | const text = 'abc';
12 |
13 | export default class SvgTextAnimation extends Component {
14 | constructor(props) {
15 | super(props);
16 | this.dx = Array(text.length)
17 | .fill()
18 | .map(_ => new Animated.Value(randomNumber(min, max)));
19 | this.dy = Array(text.length)
20 | .fill()
21 | .map(_ => new Animated.Value(randomNumber(min, max)));
22 | this.fontSize = new Animated.Value(randomNumber(12, 50));
23 | }
24 |
25 | componentDidMount() {
26 | this.animate();
27 | }
28 |
29 | animate = () => {
30 | Animated.parallel([
31 | ...this.dx.map(animValue =>
32 | Animated.spring(animValue, {
33 | toValue: randomNumber(min, max)
34 | })
35 | ),
36 | ...this.dy.map(animValue =>
37 | Animated.spring(animValue, {
38 | toValue: randomNumber(min, max)
39 | })
40 | ),
41 | Animated.spring(this.fontSize, {
42 | toValue: randomNumber(12, 50)
43 | })
44 | ]).start(() => this.animate());
45 | };
46 |
47 | render() {
48 | const { width, height } = Dimensions.get('window');
49 | return (
50 |
51 |
62 |
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/components/OperatorProductMessage.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, Image } from 'react-native';
3 |
4 | import Message from './OperatorMessage';
5 | import MessageButton from './OperatorMessageButton';
6 |
7 | const colorLight = '#E8E8E8';
8 |
9 | const styles = StyleSheet.create({
10 | profileContainer: {
11 | flexDirection: 'row',
12 | padding: 15,
13 | borderBottomWidth: StyleSheet.hairlineWidth,
14 | borderColor: colorLight
15 | },
16 | avatar: {
17 | width: 50,
18 | height: 50,
19 | borderRadius: 10,
20 | marginRight: 15
21 | },
22 | text: {
23 | alignSelf: 'center',
24 | fontSize: 16,
25 | fontWeight: '100'
26 | },
27 | productContainer: {
28 | flexDirection: 'row',
29 | borderBottomWidth: StyleSheet.hairlineWidth,
30 | borderColor: colorLight
31 | },
32 | image: {
33 | width: 100,
34 | height: 100,
35 | margin: 15,
36 | },
37 | metaContainer: {
38 | justifyContent: 'center'
39 | },
40 | label: {
41 | fontWeight: 'bold',
42 | color: '#666666'
43 | },
44 | meta: {
45 | fontWeight: '100',
46 | fontSize: 18,
47 | color: '#666666'
48 | }
49 | });
50 |
51 | const ProductMessage = ({ avatar, text, source, creator, name, price, buttonTitle, onPress, ...props }) => (
52 |
53 |
54 |
55 | {text}
56 |
57 |
58 |
59 |
60 | {creator.toUpperCase()}
61 | {name}
62 | {price}
63 |
64 |
65 |
66 |
67 | );
68 | export default ProductMessage;
69 |
--------------------------------------------------------------------------------
/src/screens/PixelBlurAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, Image, Dimensions, Animated } from 'react-native';
3 |
4 | import randomImage from '../randomImage';
5 | import randomArrayElement from '../randomArrayElement';
6 | import AnimatedBlurView from '../components/AnimatedBlurView';
7 |
8 | const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
9 | const source = { uri: randomImage() };
10 |
11 | const styles = StyleSheet.create({
12 | container: {
13 | backgroundColor: 'gray',
14 | flex: 1,
15 | flexWrap: 'wrap',
16 | flexDirection: 'row'
17 | }
18 | });
19 |
20 | const tints = ['light', 'default', 'dark'];
21 |
22 | export default class PixelBlurAnimation extends Component {
23 | static defaultProps = {
24 | numColumns: 2
25 | }
26 | size = SCREEN_WIDTH / this.props.numColumns
27 | numRows = Math.round(SCREEN_HEIGHT / this.size) + 1
28 | numBoxes = this.props.numColumns * this.numRows
29 | state = {
30 | values: Array(this.numBoxes).fill().map(_ => new Animated.Value(0))
31 | }
32 | componentDidMount() {
33 | this.animate();
34 | }
35 | animate = () => {
36 | Animated.parallel(this.state.values.map(value => {
37 | return Animated.timing(value, {
38 | toValue: Math.round(100 * Math.random()),
39 | duration: 500
40 | });
41 | })).start(this.animate);
42 | }
43 | render() {
44 | return (
45 |
46 |
47 | {this.state.values.map((value, i) => {
48 | return (
49 |
57 | );
58 | })}
59 |
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgD3ShapeArc.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Svg } from 'expo';
4 | import * as d3 from 'd3-shape';
5 | import pick from 'lodash/pick';
6 | import omit from 'lodash/omit';
7 |
8 | import AnimatedSvgFix from './AnimatedSvgFix';
9 |
10 | type Arc = d3.Arc;
11 |
12 | const NativeSvgPath = Svg.Path;
13 |
14 | export const args = ['innerRadius', 'outerRadius', 'startAngle', 'endAngle', 'centroid', 'cornerRadius', 'padAngle', 'padRadius'];
15 |
16 | function createGenerator(props, generator?: Arc): Arc {
17 | generator = generator || d3.arc();
18 | return args.reduce((acc: Arc, arg) => {
19 | const prop = props[arg];
20 | if (prop) {
21 | return acc[arg](prop);
22 | }
23 | return acc;
24 | }, generator);
25 | }
26 |
27 | function createPath(generator: Arc, props): string {
28 | return generator(pick(props, args));
29 | }
30 |
31 | class SvgD3ShapeArc extends Component {
32 | generator: Arc;
33 | constructor(props) {
34 | super(props);
35 | this.generator = createGenerator(props);
36 | }
37 | setNativeProps = (props = {}) => {
38 | const argChanged = args.some((key, index) => props[key] != null);
39 | if (argChanged) {
40 | this.generator = createGenerator(props, this.generator);
41 | props.d = createPath(this.generator, pick(props, args));
42 | }
43 | this._component && this._component.setNativeProps(props);
44 | }
45 | shouldComponentUpdate(nextProps) {
46 | const argChanged = args.some((key, index) => nextProps[key] !== this.props[key]);
47 | if (argChanged) {
48 | this.generator = createGenerator(nextProps, this.generator);
49 | }
50 | return argChanged;
51 | }
52 | render() {
53 | const filteredProps = omit(this.props, args);
54 | const d = createPath(this.generator, pick(this.props, args));
55 | return (
56 | (this._component = component)}
58 | {...filteredProps}
59 | d={d}
60 | />
61 | );
62 | }
63 | }
64 | SvgD3ShapeArc = AnimatedSvgFix(SvgD3ShapeArc);
65 | export default SvgD3ShapeArc;
66 |
--------------------------------------------------------------------------------
/src/components/OperatorMessageFormSelect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
3 |
4 | const colorLight = '#E8E8E8';
5 | const colorPrimary = '#4EAAF0';
6 |
7 | const styles = StyleSheet.create({
8 | inputContainer: {
9 | borderBottomWidth: StyleSheet.hairlineWidth,
10 | borderColor: colorLight,
11 | paddingHorizontal: 10,
12 | paddingVertical: 15
13 | },
14 | label: {
15 | fontWeight: 'bold',
16 | color: '#666666',
17 | marginHorizontal: 5,
18 | marginBottom: 15
19 | },
20 | optional: {
21 | fontSize: 12,
22 | fontWeight: '100'
23 | },
24 | selectContainer: {
25 | flexDirection: 'row'
26 | },
27 | container: {
28 | padding: 10,
29 | borderWidth: StyleSheet.hairlineWidth,
30 | borderColor: '#DCDCDC',
31 | borderRadius: 7,
32 | marginRight: 10
33 | },
34 | containerSelected: {
35 | borderColor: colorPrimary
36 | },
37 | text: {
38 | fontSize: 16,
39 | color: '#DCDCDC'
40 | },
41 | textSelected: {
42 | color: colorPrimary
43 | }
44 | });
45 |
46 | const MessageFormSelect = ({ labelText, optional, items, selected, onPress }) => (
47 |
48 | {labelText.toUpperCase()}{optional && (optional)}
49 |
50 |
51 | {items.map((item, i) => {
52 | return (
53 |
57 | {item}
61 |
62 | );
63 | })}
64 |
65 |
66 |
67 | );
68 | export default MessageFormSelect;
69 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgPolyline.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Svg } from 'expo';
4 | import AnimatedSvgFix from './AnimatedSvgFix';
5 | import { listen, removeListeners } from '../animatedListener';
6 | import type { AnimatedListener } from '../animatedListener';
7 |
8 | const NativeSvgPolyline = Svg.Polyline;
9 |
10 | // https://github.com/react-native-community/react-native-svg/blob/master/lib/extract/extractPolyPoints.js
11 | function extractPolyPoints(polyPoints) {
12 | return polyPoints.replace(/[^e]-/, ' -').split(/(?:\s+|\s*,\s*)/g).join(' ');
13 | }
14 |
15 | function pointsToString(points: number[][]): string {
16 | return points.reduce((acc, point, i) => acc + point[0] + ',' + point[1] + ' ', '');
17 | }
18 |
19 | // https://github.com/react-native-community/react-native-svg/blob/master/elements/Polyline.js
20 | function getPath(points: number[][]): string {
21 | return `M${extractPolyPoints(pointsToString(points))}`;
22 | }
23 |
24 | class SvgPolyline extends Component {
25 | points: AnimatedListener;
26 | constructor(props) {
27 | super(props);
28 | this.points = listen(props.points, _ => this.setNativeProps({ _listener: true }));
29 | }
30 | setNativeProps = (props) => {
31 | if (props._listener) {
32 | props.d = getPath(this.points.values);
33 | }
34 | // BUG: getNativeElement() is not a function https://github.com/react-native-community/react-native-svg/issues/180
35 | this._component && this._component.root && this._component.root.setNativeProps(props);
36 | }
37 | componentWillReceiveProps(nextProps) {
38 | if (nextProps.points !== this.props.points) {
39 | removeListeners(this.points);
40 | this.points = listen(nextProps.points, _ => this.setNativeProps({ _listener: true }));
41 | }
42 | }
43 | componentWillUnmount() {
44 | removeListeners(this.points);
45 | }
46 | render() {
47 | const d = getPath(this.points.values);
48 | return (
49 | (this._component = component)}
51 | {...this.props}
52 | />
53 | );
54 | }
55 | }
56 | SvgPolyline = AnimatedSvgFix(SvgPolyline);
57 | export default SvgPolyline;
58 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgPolygon.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Svg } from 'expo';
4 | import AnimatedSvgFix from './AnimatedSvgFix';
5 | import { listen, removeListeners } from '../animatedListener';
6 | import type { AnimatedListener } from '../animatedListener';
7 |
8 | const NativeSvgPolygon = Svg.Polygon;
9 |
10 | // https://github.com/react-native-community/react-native-svg/blob/master/lib/extract/extractPolyPoints.js
11 | function extractPolyPoints(polyPoints) {
12 | return polyPoints.replace(/[^e]-/, ' -').split(/(?:\s+|\s*,\s*)/g).join(' ');
13 | }
14 |
15 | function pointsToString(points: number[][]): string {
16 | return points.reduce((acc, point, i) => acc + point[0] + ',' + point[1] + ' ', '');
17 | }
18 |
19 | // https://github.com/react-native-community/react-native-svg/blob/master/elements/Polygon.js
20 | function getPath(points: number[][]): string {
21 | return `M${extractPolyPoints(pointsToString(points))}z`;
22 | }
23 |
24 | class SvgPolygon extends Component {
25 | points: AnimatedListener;
26 | constructor(props) {
27 | super(props);
28 | this.points = listen(props.points, _ => this.setNativeProps({ _listener: true }));
29 | }
30 | setNativeProps = (props) => {
31 | if (props._listener) {
32 | props.d = getPath(this.points.values);
33 | }
34 | // BUG: getNativeElement() is not a function https://github.com/react-native-community/react-native-svg/issues/180
35 | this._component && this._component.root && this._component.root.setNativeProps(props);
36 | }
37 | componentWillReceiveProps(nextProps) {
38 | if (nextProps.points !== this.props.points) {
39 | removeListeners(this.points);
40 | this.points = listen(nextProps.points, _ => this.setNativeProps({ _listener: true }));
41 | }
42 | }
43 | componentWillUnmount() {
44 | removeListeners(this.points);
45 | }
46 | render() {
47 | const d = getPath(this.points.values);
48 | return (
49 | (this._component = component)}
51 | {...this.props}
52 | d={d}
53 | />
54 | );
55 | }
56 | }
57 | SvgPolygon = AnimatedSvgFix(SvgPolygon);
58 | export default SvgPolygon;
59 |
--------------------------------------------------------------------------------
/src/screens/SvgD3Hexbin.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Random from 'd3-random';
5 | import * as d3Array from 'd3-array';
6 |
7 | import Hexbin from '../components/AnimatedSvgD3Hexbin';
8 |
9 | const numPoints = 5;
10 | const radius = 20;
11 | const stdev = 80;
12 |
13 | export default class SvgContourDensityBasic extends Component {
14 | constructor(props) {
15 | super(props);
16 | const { width, height } = Dimensions.get('window');
17 | const rx = d3Random.randomNormal(width / 2, stdev);
18 | const ry = d3Random.randomNormal(height / 2, stdev);
19 | this.points = d3Array
20 | .range(numPoints)
21 | .map(_ => [rx(), ry()])
22 | .map(value => [
23 | new Animated.Value(value[0]),
24 | new Animated.Value(value[1])
25 | ]);
26 | }
27 |
28 | componentDidMount() {
29 | this.animate();
30 | }
31 |
32 | animate() {
33 | const { width, height } = Dimensions.get('window');
34 | const rx = d3Random.randomNormal(width / 2, stdev);
35 | const ry = d3Random.randomNormal(height / 2, stdev);
36 | Animated.sequence(
37 | this.points.map(value =>
38 | Animated.sequence([
39 | Animated.timing(value[0], {
40 | toValue: rx(),
41 | duration: 1000
42 | }),
43 | Animated.timing(value[1], {
44 | toValue: ry(),
45 | duration: 1000
46 | })
47 | ])
48 | )
49 | ).start(() => this.animate());
50 | }
51 |
52 | render() {
53 | const { width, height } = Dimensions.get('window');
54 | const extent = [[0, 0], [width, height]];
55 | return (
56 |
57 |
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/screens/SvgD3PartitionBasic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Hierarchy from 'd3-hierarchy';
5 | import * as d3Scale from 'd3-scale';
6 |
7 | import Partition from '../components/AnimatedSvgD3HierarchyPartition';
8 | import Rect from '../components/AnimatedSvgRect';
9 | import data from '../data/hierarchyData';
10 |
11 | export default class SvgPartitionBasic extends Component {
12 | constructor(props) {
13 | super(props);
14 | }
15 |
16 | render() {
17 | const { width, height } = Dimensions.get('window');
18 | const root = d3Hierarchy
19 | .stratify()
20 | .parentId(d => d.id.substring(0, d.id.lastIndexOf('.')))(data)
21 | .sum(d => d.value)
22 | .sort((a, b) => b.height - a.height || b.value - a.value);
23 | const color = d3Scale.scaleOrdinal(d3Scale.schemeCategory10);
24 | const size = [height, width];
25 | return (
26 |
27 |
53 |
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/screens/ARExample/actions/hudSelection.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import { addObjectAtHeading, removeObject, deselect } from './ar';
3 |
4 | export const HUD_RESIZE = 'hudSelection/RESIZE';
5 | export const HUD_CLOSE = 'hudSelection/CLOSE';
6 |
7 | export const resize = () => ({ type: HUD_RESIZE });
8 | export const translate = addObjectAtHeading;
9 | export const scaleUp = (object, value = 1) => dispatch => {
10 | object.object3D.scale.set(
11 | object.object3D.scale.x + object.object3D.scale.x * 0.1,
12 | object.object3D.scale.y + object.object3D.scale.y * 0.1,
13 | object.object3D.scale.z + object.object3D.scale.z * 0.1
14 | );
15 | };
16 | export const scaleDown = (object, value = 1) => dispatch => {
17 | object.object3D.scale.set(
18 | object.object3D.scale.x - object.object3D.scale.x * 0.1,
19 | object.object3D.scale.y - object.object3D.scale.y * 0.1,
20 | object.object3D.scale.z - object.object3D.scale.z * 0.1
21 | );
22 | };
23 | export const rotateLeft = object => (dispatch, getState) => {
24 | const { three: { camera } } = getState();
25 | const plane = new THREE.Plane();
26 | plane.setFromNormalAndCoplanarPoint(
27 | camera.getWorldDirection(plane.normal),
28 | object.object3D.position
29 | );
30 | // const coplanarPoint = plane.coplanarPoint();
31 | // const focalPoint = new THREE.Vector3().copy(coplanarPoint).add(plane.normal);
32 | object.object3D.rotateOnAxis(plane.normal, -0.1);
33 | };
34 | export const rotateRight = object => (dispatch, getState) => {
35 | const { three: { camera } } = getState();
36 | const plane = new THREE.Plane();
37 | plane.setFromNormalAndCoplanarPoint(
38 | camera.getWorldDirection(plane.normal),
39 | object.object3D.position
40 | );
41 | object.object3D.rotateOnAxis(plane.normal, 0.1);
42 | };
43 | export const copy = object => dispatch => {
44 | const newObject = {
45 | ...object,
46 | object3D: object.object3D.clone()
47 | };
48 | dispatch(addObjectAtHeading(newObject));
49 | };
50 | /**
51 | * Remove object3D from scene and object from store
52 | */
53 | export const remove = object => (dispatch, getState) => {
54 | dispatch(removeObject(object));
55 | dispatch(close());
56 | };
57 | export const close = () => dispatch => {
58 | dispatch(deselect());
59 | dispatch({ type: HUD_CLOSE });
60 | };
61 |
--------------------------------------------------------------------------------
/src/screens/ARExample/GeometryListView.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | View,
4 | TouchableOpacity,
5 | TextInput,
6 | StyleSheet,
7 | ScrollView
8 | } from 'react-native';
9 | import { MaterialIcons } from '@expo/vector-icons';
10 | import { connect } from 'react-redux';
11 |
12 | import GeometryView from './GeometryView';
13 | import { close, select, creators } from './actions/geometry';
14 |
15 | const geometries = Object.keys(creators).map(name => ({ name }));
16 |
17 | class GeometryListView extends React.Component {
18 | render() {
19 | return this.props.visible ? (
20 |
21 |
22 |
33 |
34 |
35 |
36 |
45 | {geometries.map((geometry, i) => (
46 |
51 | ))}
52 |
53 |
54 | ) : null;
55 | }
56 | }
57 |
58 | const mapStateToProps = state => state.geometry;
59 |
60 | const mapDispatchToProps = {
61 | onClose: close,
62 | onSelect: select
63 | };
64 |
65 | export default connect(mapStateToProps, mapDispatchToProps)(GeometryListView);
66 |
--------------------------------------------------------------------------------
/src/components/SnapchatIconSvgPath.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Svg } from 'expo';
3 |
4 | export const d = 'M28.6601,51.6829c-0.1281,0-0.2539-0.0037-0.3798-0.0096h-0.0003c-0.0802,0.0062-0.1641,0.0096-0.248,0.0096 c-2.9442,0-4.834-1.3358-6.6613-2.6278c-1.2618-0.8916-2.4524-1.7332-3.8554-1.9669c-0.6846-0.1133-1.366-0.1709-2.0245-0.1709 c-1.1863,0-2.1222,0.1831-2.8054,0.3167c-0.4152,0.0813-0.7734,0.1511-1.0456,0.1511c-0.2844,0-0.5926-0.0615-0.7265-0.5191 c-0.1164-0.3965-0.2006-0.7804-0.2816-1.1519c-0.2087-0.956-0.3569-1.5439-0.7575-1.6054 c-4.6697-0.7213-6.0062-1.7048-6.3042-2.4027c-0.0422-0.0998-0.0662-0.1998-0.0719-0.2988 c-0.0151-0.2681,0.1748-0.5048,0.4397-0.5481c7.1784-1.1821,10.3974-8.5188,10.531-8.8304 c0.0036-0.0086,0.0076-0.0169,0.0115-0.0255c0.4394-0.8903,0.5254-1.6632,0.2566-2.2967c-0.4926-1.161-2.0992-1.6712-3.1628-2.0086 c-0.2602-0.0823-0.5069-0.1602-0.7015-0.237c-2.1222-0.839-2.2988-1.7002-2.2154-2.1391c0.1422-0.7479,1.1417-1.2691,1.95-1.2691 c0.2217,0,0.4168,0.0393,0.5809,0.1159c0.9547,0.4472,1.815,0.6739,2.5574,0.6739c1.0258,0,1.4735-0.4311,1.5283-0.4876 c-0.0261-0.4858-0.0584-0.9933-0.0914-1.5168c-0.2138-3.3938-0.4787-7.6108,0.595-10.0179 c3.218-7.2154,10.0421-7.7759,12.0569-7.7759c0.0516,0,0.8833-0.0088,0.8833-0.0088l0.1193-0.0005 c2.0195,0,8.8585,0.5616,12.0783,7.7809c1.074,2.4084,0.8083,6.629,0.5941,10.0202l-0.0089,0.1474 c-0.0297,0.4723-0.0583,0.9312-0.0823,1.3706c0.0513,0.0524,0.4631,0.4488,1.3931,0.4845h0.001 c0.7067-0.0271,1.5189-0.2524,2.4102-0.6699c0.2613-0.1219,0.5514-0.1477,0.7489-0.1477c0.3016,0,0.6075,0.0584,0.8614,0.1644 l0.0154,0.0063c0.721,0.2555,1.1933,0.7609,1.2034,1.2889c0.0094,0.4972-0.3702,1.2448-2.2323,1.9804 c-0.193,0.0758-0.4397,0.1542-0.7009,0.237c-1.0646,0.3378-2.6707,0.8476-3.163,2.0083c-0.2691,0.6332-0.1829,1.4056,0.2563,2.2964 c0.0039,0.0086,0.0083,0.0169,0.0117,0.0261c0.1336,0.3113,3.3498,7.6459,10.5313,8.8293c0.2652,0.0437,0.4545,0.2802,0.4399,0.5486 c-0.006,0.1006-0.0305,0.2019-0.0734,0.3004c-0.2962,0.6929-1.6317,1.6751-6.3029,2.3969 c-0.3816,0.0586-0.5295,0.5559-0.7572,1.5986c-0.0828,0.3795-0.1667,0.7523-0.2821,1.1443 c-0.0995,0.3402-0.3115,0.4994-0.6681,0.4994h-0.0581c-0.2475,0-0.5994-0.0443-1.0453-0.1318 c-0.7908-0.1547-1.6775-0.2972-2.8056-0.2972c-0.6588,0-1.3404,0.0576-2.0255,0.1709c-1.4014,0.2337-2.5913,1.0737-3.8507,1.964 C33.4947,50.3471,31.6052,51.6829,28.6601,51.6829z';
5 |
6 | const SnapshatIconSvgPath = (props) => ;
7 |
8 | export default SnapshatIconSvgPath;
9 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/poly.js:
--------------------------------------------------------------------------------
1 | import {
2 | LIST_ASSETS,
3 | LIST_ASSETS_SUCCESS,
4 | LIST_ASSETS_ERROR,
5 | LOAD_MORE,
6 | LOAD_MORE_SUCCESS,
7 | LOAD_MORE_ERROR,
8 | OPEN,
9 | CLOSE,
10 | SELECT_ASSET
11 | } from '../actions/poly';
12 | import { RESET } from '../actions/ar';
13 |
14 | const initialState = {
15 | assets: [],
16 | loadedAssets: {},
17 | loading: false,
18 | loadingMore: false,
19 | visible: false
20 | };
21 |
22 | export default function(state = initialState, action) {
23 | switch (action.type) {
24 | case LIST_ASSETS:
25 | return {
26 | ...state,
27 | loading: true
28 | };
29 | case LIST_ASSETS_SUCCESS:
30 | return {
31 | ...state,
32 | ...action.results,
33 | assets: action.assets,
34 | loading: false
35 | };
36 | case LIST_ASSETS_ERROR:
37 | return {
38 | ...state,
39 | error: action.error,
40 | loading: false
41 | };
42 | case LOAD_MORE:
43 | return {
44 | ...state,
45 | loadingMore: true
46 | };
47 | case LOAD_MORE_SUCCESS:
48 | return {
49 | ...state,
50 | ...action.results,
51 | assets: [...state.assets, ...action.assets],
52 | loadingMore: false
53 | };
54 | case LOAD_MORE_ERROR:
55 | return {
56 | ...state,
57 | error: action.error,
58 | loadingMore: false
59 | };
60 | case OPEN:
61 | return {
62 | ...state,
63 | visible: true
64 | };
65 | case CLOSE:
66 | return {
67 | ...state,
68 | visible: false
69 | };
70 | case SELECT_ASSET:
71 | return {
72 | ...state,
73 | loadedAssets: {
74 | ...state.loadedAssets,
75 | [action.asset.name]: action.asset
76 | }
77 | };
78 | case RESET:
79 | return {
80 | ...state,
81 | loading: false,
82 | loadingMore: false,
83 | visible: false
84 | };
85 | default:
86 | return state;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/screens/SvgD3ChordAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Chord from '../components/AnimatedSvgD3Chord';
8 |
9 | const innerRadius = 100;
10 | const outerRadius = innerRadius + 10;
11 | const matrixSizeMin = 2;
12 | const matrixSizeMax = 4;
13 | const matrixSize = Math.round(randomNumber(matrixSizeMin, matrixSizeMax));
14 | const min = 1;
15 | const max = 100;
16 | const chordColors = Array(matrixSize).fill().map(_ => randomColor());
17 |
18 | export default class SvgChordAnimation extends Component {
19 | constructor(props) {
20 | super(props);
21 | this.matrix = Array(matrixSize)
22 | .fill()
23 | .map(_ =>
24 | Array(matrixSize)
25 | .fill()
26 | .map(__ => new Animated.Value(randomNumber(min, max)))
27 | );
28 | }
29 |
30 | componentDidMount() {
31 | this.animate();
32 | }
33 |
34 | animate() {
35 | Animated.sequence(
36 | this.matrix.map(animValues =>
37 | Animated.sequence(
38 | animValues.map(animValue =>
39 | Animated.spring(animValue, {
40 | toValue: randomNumber(min, max)
41 | })
42 | )
43 | )
44 | )
45 | ).start(() => this.animate());
46 | }
47 |
48 | render() {
49 | const { width, height } = Dimensions.get('window');
50 | return (
51 |
52 |
68 |
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/screens/SvgPolylineAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Polyline from '../components/AnimatedSvgPolyline';
8 |
9 | export default class SvgPolylineAnimation extends Component {
10 | constructor(props) {
11 | super(props);
12 | const { width, height } = Dimensions.get('window');
13 | this.x1 = new Animated.Value(randomNumber(0, width));
14 | this.y1 = new Animated.Value(randomNumber(0, height));
15 | this.x2 = new Animated.Value(randomNumber(0, width));
16 | this.y2 = new Animated.Value(randomNumber(0, height));
17 | this.x3 = new Animated.Value(randomNumber(0, width));
18 | this.y3 = new Animated.Value(randomNumber(0, height));
19 | }
20 |
21 | componentDidMount() {
22 | this.animate();
23 | }
24 |
25 | animate = () => {
26 | const { width, height } = Dimensions.get('window');
27 | Animated.parallel([
28 | Animated.spring(this.x1, {
29 | toValue: randomNumber(0, width)
30 | }),
31 | Animated.spring(this.y1, {
32 | toValue: randomNumber(0, height)
33 | }),
34 | Animated.spring(this.x2, {
35 | toValue: randomNumber(0, width)
36 | }),
37 | Animated.spring(this.y2, {
38 | toValue: randomNumber(0, height)
39 | }),
40 | Animated.spring(this.x3, {
41 | toValue: randomNumber(0, width)
42 | }),
43 | Animated.spring(this.y3, {
44 | toValue: randomNumber(0, height)
45 | })
46 | ]).start(() => this.animate());
47 | };
48 |
49 | render() {
50 | const { width, height } = Dimensions.get('window');
51 | return (
52 |
53 |
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/screens/SvgPolygonAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomColor from '../randomColor';
6 | import randomNumber from '../randomNumber';
7 | import Polygon from '../components/AnimatedSvgPolygon';
8 |
9 | export default class SvgPolygonAnimation extends Component {
10 | constructor(props) {
11 | super(props);
12 | const { width, height } = Dimensions.get('window');
13 | this.x1 = new Animated.Value(randomNumber(0, width));
14 | this.y1 = new Animated.Value(randomNumber(0, height));
15 | this.x2 = new Animated.Value(randomNumber(0, width));
16 | this.y2 = new Animated.Value(randomNumber(0, height));
17 | this.x3 = new Animated.Value(randomNumber(0, width));
18 | this.y3 = new Animated.Value(randomNumber(0, height));
19 | }
20 |
21 | componentDidMount() {
22 | this.animate();
23 | }
24 |
25 | animate = () => {
26 | const { width, height } = Dimensions.get('window');
27 | Animated.parallel([
28 | Animated.spring(this.x1, {
29 | toValue: randomNumber(0, width)
30 | }),
31 | Animated.spring(this.y1, {
32 | toValue: randomNumber(0, height)
33 | }),
34 | Animated.spring(this.x2, {
35 | toValue: randomNumber(0, width)
36 | }),
37 | Animated.spring(this.y2, {
38 | toValue: randomNumber(0, height)
39 | }),
40 | Animated.spring(this.x3, {
41 | toValue: randomNumber(0, width)
42 | }),
43 | Animated.spring(this.y3, {
44 | toValue: randomNumber(0, height)
45 | })
46 | ]).start(() => this.animate());
47 | };
48 |
49 | render() {
50 | const { width, height } = Dimensions.get('window');
51 | return (
52 |
53 |
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-examples",
3 | "description": "UI, Animations, SVG, Flubber, D3",
4 | "author": "Ethan Tran (ethandt92@gmail.com)",
5 | "version": "0.1.0",
6 | "private": true,
7 | "devDependencies": {
8 | "babel-eslint": "^10.0.1",
9 | "babel-preset-expo": "^5.1.1",
10 | "eslint-plugin-react-hooks": "^1.6.0",
11 | "eslint-plugin-react-native-globals": "^0.1.2",
12 | "husky": "^2.3.0",
13 | "jest-expo": "^33.0.0",
14 | "lint-staged": "^8.1.7",
15 | "prettier": "^1.17.1"
16 | },
17 | "main": "node_modules/expo/AppEntry.js",
18 | "scripts": {
19 | "start": "expo start",
20 | "android": "expo start --android",
21 | "ios": "expo start --ios",
22 | "web": "expo start --web",
23 | "eject": "expo eject",
24 | "precommit": "lint-staged"
25 | },
26 | "jest": {
27 | "preset": "jest-expo"
28 | },
29 | "dependencies": {
30 | "@turf/turf": "^5.0.4",
31 | "color": "^3.1.2",
32 | "d3-array": "^1.2.1",
33 | "d3-axis": "^1.0.8",
34 | "d3-chord": "^1.0.4",
35 | "d3-collection": "^1.0.4",
36 | "d3-contour": "^1.1.1",
37 | "d3-geo": "^1.9.0",
38 | "d3-hexbin": "^0.2.2",
39 | "d3-hierarchy": "^1.1.5",
40 | "d3-interpolate-path": "^2.0.1",
41 | "d3-path": "^1.0.5",
42 | "d3-random": "^1.1.0",
43 | "d3-sankey": "^0.7.1",
44 | "d3-scale": "^1.0.7",
45 | "d3-scale-chromatic": "^1.1.1",
46 | "d3-shape": "^1.2.0",
47 | "d3-svg-annotation": "^1.18.0",
48 | "d3-voronoi": "^1.1.2",
49 | "expo": "^33.0.0",
50 | "expo-graphics": "^1.1.0",
51 | "expo-linear-gradient": "~5.0.1",
52 | "expo-three": "^3.0.0-alpha.8",
53 | "flubber": "^0.4.0",
54 | "lorem-ipsum-react-native": "^1.0.3",
55 | "predicthq": "^0.0.18",
56 | "qs": "^6.5.1",
57 | "react": "16.8.3",
58 | "react-game-kit": "^1.0.6",
59 | "react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz",
60 | "react-navigation": "3.11.0",
61 | "react-redux": "^5.0.6",
62 | "reactotron-react-native": "^3.6.4",
63 | "reactotron-redux": "^3.1.1",
64 | "redux": "^3.7.2",
65 | "redux-persist": "^5.4.0",
66 | "redux-thunk": "^2.2.0",
67 | "svg-path-properties": "^0.4.1",
68 | "three": "^0.106.2",
69 | "topojson": "^3.0.2"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/components/PanResponderTouchable.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, PanResponder } from 'react-native';
3 |
4 | /*
5 | Problem: Touchables will activate onPressOut when dragged, sometimes you don't want that
6 | Solution: This component tracks a drag threshold, sets a disabled state, and call onPressOut on release if it is not disabled
7 | */
8 |
9 | export default class PanResponderTouchable extends Component {
10 | static defaultProps = {
11 | onStartShouldSetPanResponderCapture: () => true,
12 | onPanResponderGrant: () => { },
13 | onPanResponderRelease: () => { },
14 | onPanResponderTerminationRequest: () => true,
15 | onPanResponderTerminate: () => { },
16 | dragThreshold: 1,
17 | onPressIn: () => { },
18 | onPress: () => { },
19 | onPressOut: () => { }
20 | }
21 | panResponder = PanResponder.create({
22 | onStartShouldSetPanResponderCapture: this.props.onStartShouldSetPanResponderCapture,
23 | onMoveShouldSetPanResponderCapture: (e, gs) => {
24 | const draggedLeft = gs.dx < -1 * this.props.dragThreshold;
25 | const draggedRight = gs.dx > this.props.dragThreshold;
26 | const draggedUp = gs.dy > this.props.dragThreshold;
27 | const draggedDown = gs.dy < -1 * this.props.dragThreshold;
28 | if (draggedLeft || draggedRight || draggedUp || draggedDown) {
29 | this.setState({ disabled: true });
30 | }
31 | return this.props.onMoveShouldSetPanResponderCapture(e, gs) || true;
32 | },
33 | onPanResponderGrant: () => {
34 | this.props.onPanResponderGrant();
35 | this.setState({
36 | disabled: false,
37 | tapped: true
38 | });
39 | this.props.onPressIn();
40 | this.props.onPress();
41 | },
42 | onPanResponderRelease: () => {
43 | this.props.onPanResponderRelease();
44 | if (!this.state.disabled) {
45 | this.props.onPressOut();
46 | }
47 | },
48 | onPanResponderTerminationRequest: this.props.onPanResponderTerminationRequest,
49 | onPanResponderTerminate: (evt, gestureState) => {
50 | this.props.onPanResponderTerminate();
51 | this.setState({
52 | disabled: true,
53 | tapped: false
54 | });
55 | }
56 | })
57 | render() {
58 | return ;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/SvgTextWrap.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Svg } from 'expo';
4 |
5 | const { Text, TSpan } = Svg;
6 |
7 | type Props = {
8 | text: string,
9 | width: number,
10 | fontSize: number
11 | };
12 |
13 | const defaultProps = {
14 | fontSize: 12
15 | };
16 |
17 | // https://gist.github.com/gka/7469245
18 | export function approximateCharWidth(c, { fontSize = 12, adjust } = {}) {
19 | let width;
20 | if (c === 'W' || c === 'M') {
21 | width = 15;
22 | } else if (c === 'w' || c === 'm') {
23 | width = 12;
24 | } else if (c === 'I' || c === 'i' || c === 'l' || c === 't' || c === 'f') {
25 | width = 4;
26 | } else if (c === 'r') {
27 | width = 8;
28 | } else if (c === c.toUpperCase()) {
29 | width = 12;
30 | } else {
31 | width = 10;
32 | }
33 | // adjust based on fontSize
34 | width *= fontSize / 12;
35 | // adjust based on custom function
36 | if (adjust) {
37 | width = adjust(width, c);
38 | }
39 | return width;
40 | }
41 |
42 | export function approximateTextWidth(s, options) {
43 | return s.split('').reduce((w, c) => {
44 | const width = approximateCharWidth(c, options);
45 | w += width;
46 | return w;
47 | }, 0);
48 | }
49 |
50 | export function getLines({ text, width }, options) {
51 | const words = text.split(/[ \t\r\n]+/).reverse().filter(w => w !== '');
52 | let word;
53 | let lines = [];
54 | let line = [];
55 | while ((word = words.pop())) {
56 | line.push(word);
57 | if (
58 | approximateTextWidth(line.join(' '), options) > width &&
59 | line.length > 1
60 | ) {
61 | line.pop();
62 | lines.push(line.join(' '));
63 | line = [word];
64 | }
65 | }
66 | if (line.length > 0) {
67 | lines.push(line.join(' '));
68 | }
69 | return lines;
70 | }
71 |
72 | class SvgTextWrap extends Component {
73 | props: Props;
74 | static defaultProps = typeof defaultProps;
75 | render() {
76 | const { text, width, ...props } = this.props;
77 | const lines = getLines(this.props);
78 | return (
79 |
80 | {lines.map(line =>
81 |
82 | {line}
83 |
84 | )}
85 |
86 | );
87 | }
88 | }
89 | SvgTextWrap.defaultProps = defaultProps;
90 | export default SvgTextWrap;
91 |
--------------------------------------------------------------------------------
/src/components/AnimatedLinearGradient.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { StyleSheet, Animated, Easing } from "react-native";
3 | import { LinearGradient as NativeLinearGradient } from "expo-linear-gradient";
4 |
5 | /**
6 | * Problem: LinearGradient requires an array for colors but animated values cannot
7 | * Solution: Map through all prop keys following pattern color0, color1, color2, ...
8 | */
9 | class LinearGradient extends Component {
10 | static defaultProps = {
11 | start: [0, 0.4],
12 | end: [1, 0.6]
13 | };
14 | render() {
15 | const colors = Object.keys(this.props)
16 | .filter(key => key.includes("color"))
17 | .map((propKey, i) => this.props[propKey]);
18 | return (
19 |
23 | );
24 | }
25 | }
26 | Animated.LinearGradient = Animated.createAnimatedComponent(LinearGradient);
27 |
28 | class AnimatedGradient extends Component {
29 | state = {
30 | animatedColors: this.props.colors.map(color => new Animated.Value(0))
31 | };
32 |
33 | componentWillReceiveProps(nextProps) {
34 | if (nextProps.colors !== this.props.colors) {
35 | this.interpolatedColors = this.interpolateColors(nextProps);
36 | this.state.animatedColors.forEach(animatedColor =>
37 | animatedColor.setValue(0)
38 | );
39 | Animated.parallel(
40 | this.state.animatedColors.map(animatedColor => {
41 | return Animated.timing(animatedColor, {
42 | toValue: 1,
43 | duration: this.props.speed,
44 | easing: Easing.linear
45 | });
46 | })
47 | ).start();
48 | }
49 | }
50 |
51 | interpolateColors = ({ colors }) => {
52 | return this.state.animatedColors.map((animatedColor, i) => {
53 | return animatedColor.interpolate({
54 | inputRange: [0, 1],
55 | outputRange: [this.props.colors[i], colors[i] || "transparent"]
56 | });
57 | });
58 | };
59 |
60 | interpolatedColors = this.interpolateColors(this.props);
61 |
62 | render() {
63 | const { style, colors, ...props } = this.props; // eslint-disable-line no-unused-vars
64 | const colorProps = this.interpolatedColors.reduce(
65 | (obj, interpolatedColor, i) => {
66 | obj["color" + i] = interpolatedColor;
67 | return obj;
68 | },
69 | {}
70 | );
71 | return (
72 |
77 | );
78 | }
79 | }
80 |
81 | export default AnimatedGradient;
82 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgD3ShapeLine.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Animated } from 'react-native';
4 | import * as d3 from 'd3-shape';
5 | import omit from 'lodash/omit';
6 |
7 | import Path from './AnimatedSvgPath';
8 | import { listen, removeListeners } from '../animatedListener';
9 | import type { AnimatedListener } from '../animatedListener';
10 |
11 | type Line = d3.Line;
12 |
13 | export const args = ['x', 'y', 'defined', 'curve'];
14 |
15 | function createGenerator(props, generator?: Line): Line {
16 | generator = generator || d3.line();
17 | return args.reduce((acc: Line, arg) => {
18 | const prop = props[arg];
19 | if (prop) {
20 | return acc[arg](prop);
21 | }
22 | return acc;
23 | }, generator);
24 | }
25 |
26 | function createPath(generator: Line, data): string {
27 | return generator(data);
28 | }
29 |
30 | class SvgD3ShapeLine extends Component {
31 | generator: Line;
32 | data: AnimatedListener;
33 | constructor(props) {
34 | super(props);
35 | this.generator = createGenerator(props);
36 | this.data = listen(props.data, _ => this.setNativeProps({ _listener: true }));
37 | }
38 | setNativeProps = (props = {}) => {
39 | const argChanged = args.some((key, index) => props[key] != null);
40 | if (argChanged) {
41 | this.generator = createGenerator(props, this.generator);
42 | }
43 | if (argChanged || props._listener) {
44 | props.d = createPath(this.generator, this.data.values);
45 | }
46 | this._component && this._component.setNativeProps(props);
47 | }
48 | shouldComponentUpdate(nextProps) {
49 | const argChanged = args.some((key, index) => nextProps[key] !== this.props[key]);
50 | const dataChanged = nextProps.data !== this.props.data;
51 | if (argChanged) {
52 | this.generator = createGenerator(nextProps, this.generator);
53 | }
54 | if (dataChanged) {
55 | removeListeners(this.data);
56 | this.data = listen(nextProps.data, _ => this.setNativeProps({ _listener: true }));
57 | }
58 | return argChanged || dataChanged;
59 | }
60 | componentWillUnmount() {
61 | removeListeners(this.data);
62 | }
63 | render() {
64 | const filteredProps = omit(this.props, args);
65 | const d = createPath(this.generator, this.data.values);
66 | return (
67 | (this._component = component)}
69 | {...filteredProps}
70 | d={d}
71 | />
72 | );
73 | }
74 | }
75 | SvgD3ShapeLine = Animated.createAnimatedComponent(SvgD3ShapeLine);
76 | export default SvgD3ShapeLine;
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-examples
2 | UI examples from [pttrns](pttrns.com), [mobile-patterns](www.mobile-patterns.com) converted to React Native
3 |
4 | Examples of SVG Animation with [react-native-svg](https://github.com/react-native-community/react-native-svg)
5 |
6 | Examples with [d3-annotation](https://github.com/susielu/d3-annotation), [d3-chord](https://github.com/d3/d3-chord), [d3-contour](https://github.com/d3/d3-contour), [d3-interpolate-path](https://github.com/d3/d3-interpolate-path), [d3-path](https://github.com/d3/d3-path), [d3-hexbin](https://github.com/d3/d3-hexbin), [d3-hierarchy](https://github.com/d3/d3-hierarchy), [d3-sankey](https://github.com/d3/d3-sankey), [d3-shape](https://github.com/d3/d3-shape), [d3-voronoi](https://github.com/d3/d3-voronoi), [flubber](https://github.com/veltman/flubber), [svg-path-properties](https://github.com/rveciana/svg-path-properties)
7 |
8 | D3 Examples from Mike Bostock's [Blocks](https://bl.ocks.org/mbostock) converted to React Native
9 |
10 | SVG Metaball Animation from [Varun](http://varun.ca/metaballs/)
11 |
12 | ARKit with [Poly](https://developers.google.com/poly/) and geolocation
13 |
14 | [Quartz](https://itunes.apple.com/us/app/quartz/id1076683233?mt=8)-like chat animations
15 |
16 | ## Screenshots
17 |
18 | 

19 |
20 | ## Gifs
21 |
22 | [Path Drawing](https://gfycat.com/NeighboringAggressiveAmericancrayfish)
23 |
24 | [Point Along Path Interpolation](https://gfycat.com/WiltedAggressiveChick)
25 |
26 | [Pie Animation](https://gfycat.com/VacantChiefEuropeanpolecat)
27 |
28 | [Anchor Walkthrough](https://gfycat.com/MadeupHandyHound)
29 |
30 | [Voronoi](https://gfycat.com/AlienatedVillainousKagu)
31 |
32 | [D3 Annotation](https://gfycat.com/EverlastingWindyAmericanbobtail)
33 |
34 | [Staggered D3 Line Animation](https://gfycat.com/FickleExcitableGermanshepherd)
35 |
36 | [Staggered Circle Animation with D3 GeoPath](https://gfycat.com/BruisedRemorsefulHarvestmouse)
37 |
38 | ## Expo
39 |
40 | https://exp.host/@ethantran2/react-native-examples
41 |
42 | ## Articles
43 |
44 | https://medium.com/@ethantran/animating-svg-in-react-native-cf1907831608
45 |
46 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
47 |
48 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
49 |
--------------------------------------------------------------------------------
/src/screens/SvgD3StackBars.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Scale from 'd3-scale';
5 | import * as d3Array from 'd3-array';
6 |
7 | import randomColor from '../randomColor';
8 | import Stack from '../components/AnimatedSvgD3ShapeStack';
9 | import G from '../components/AnimatedSvgG';
10 | import Rect from '../components/AnimatedSvgRect';
11 | import stackData from '../data/stackData';
12 |
13 | const keys = ['apples', 'bananas', 'cherries', 'dates'];
14 |
15 | // generate total for each row
16 | stackData.reduce((acc, row, index) => {
17 | row.total = 0;
18 | keys.forEach(key => {
19 | row.total += row[key];
20 | });
21 | return stackData;
22 | }, stackData);
23 |
24 | export default class SvgD3StackBars extends Component {
25 | constructor(props) {
26 | super(props);
27 | }
28 |
29 | render() {
30 | const { width, height } = Dimensions.get('window');
31 | let x = d3Scale
32 | .scaleBand()
33 | .range([0, width / 2])
34 | .paddingInner(0.05)
35 | .align(0.1);
36 | let y = d3Scale.scaleLinear().range([height / 2, 0]);
37 | let z = d3Scale.scaleOrdinal().range(keys.map(randomColor));
38 | x.domain(stackData.map(d => d.index));
39 | y.domain([0, d3Array.max(stackData, d => d.total)]).nice();
40 | z.domain(keys);
41 | return (
42 |
43 |
68 |
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/screens/SvgD3LineAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Scale from 'd3-scale';
5 | import * as d3Shape from 'd3-shape';
6 |
7 | import randomNumber from '../randomNumber';
8 | import randomColor from '../randomColor';
9 | import Line from '../components/AnimatedSvgD3ShapeLine';
10 | import LineRadial from '../components/AnimatedSvgD3ShapeLineRadial';
11 |
12 | const dataLength = 10;
13 | const min = 0;
14 | const max = 100;
15 |
16 | export default class SvgD3LineAnimation extends Component {
17 | constructor(props) {
18 | super(props);
19 | this.data = Array(dataLength).fill().map((_, index) => ({
20 | index,
21 | value: new Animated.Value(randomNumber(min, max))
22 | }));
23 | }
24 |
25 | componentDidMount() {
26 | this.animate();
27 | }
28 |
29 | animate() {
30 | Animated.sequence([
31 | Animated.delay(2000),
32 | ...this.data.map(item =>
33 | Animated.timing(item.value, {
34 | toValue: randomNumber(min, max),
35 | duration: 1000
36 | })
37 | )
38 | ]).start();
39 | }
40 |
41 | render() {
42 | const { width, height } = Dimensions.get('window');
43 | const radius = Math.min(width, height) / 2 - 30;
44 | let x = d3Scale.scaleLinear().range([0, width - 10]).domain([0, dataLength - 1]);
45 | let y = d3Scale.scaleLinear().range([0, width / 2]).domain([min, max]);
46 | let angle = d3Scale.scaleLinear().range([0, 2 * Math.PI]).domain([0, dataLength - 1]);
47 | let r = d3Scale.scaleLinear().range([0, radius]).domain([min, max]);
48 | return (
49 |
50 |
71 |
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/AnnotationCollection.js:
--------------------------------------------------------------------------------
1 | export default class AnnotationCollection {
2 | constructor({ annotations, accessors, accessorsInverse }) {
3 | this.accessors = accessors;
4 | this.accessorsInverse = accessorsInverse;
5 | this.annotations = annotations;
6 | }
7 |
8 | clearTypes(newSettings) {
9 | this.annotations.forEach(d => {
10 | d.type = undefined;
11 | d.subject = (newSettings && newSettings.subject) || d.subject;
12 | d.connector = (newSettings && newSettings.connector) || d.connector;
13 | d.note = (newSettings && newSettings.note) || d.note;
14 | });
15 | }
16 |
17 | setPositionWithAccessors() {
18 | this.annotations.forEach(d => {
19 | d.type.setPositionWithAccessors(this.accessors);
20 | });
21 | }
22 |
23 | editMode(editMode) {
24 | this.annotations.forEach(a => {
25 | if (a.type) {
26 | a.type.editMode = editMode;
27 | a.type.updateEditMode();
28 | }
29 | });
30 | }
31 |
32 | updateDisable(disable) {
33 | this.annotations.forEach(a => {
34 | a.disable = disable;
35 | if (a.type) {
36 | disable.forEach(d => {
37 | if (a.type[d]) {
38 | a.type[d].remove && a.type[d].remove();
39 | a.type[d] = undefined;
40 | }
41 | });
42 | }
43 | });
44 | }
45 |
46 | updateTextWrap(textWrap) {
47 | this.annotations.forEach(a => {
48 | if (a.type && a.type.updateTextWrap) {
49 | a.type.updateTextWrap(textWrap);
50 | }
51 | });
52 | }
53 |
54 | updateText() {
55 | this.annotations.forEach(a => {
56 | if (a.type && a.type.drawText) {
57 | a.type.drawText();
58 | }
59 | });
60 | }
61 |
62 | updateNotePadding(notePadding) {
63 | this.annotations.forEach(a => {
64 | if (a.type) {
65 | a.type.notePadding = notePadding;
66 | }
67 | });
68 | }
69 |
70 | get json() {
71 | return this.annotations.map(a => {
72 | const json = a.json;
73 | if (this.accessorsInverse && a.data) {
74 | json.data = {};
75 | Object.keys(this.accessorsInverse).forEach(k => {
76 | json.data[k] = this.accessorsInverse[k]({ x: a.x, y: a.y });
77 | });
78 | }
79 | return json;
80 | });
81 | }
82 |
83 | get noteNodes() {
84 | return this.annotations.map(a => ({
85 | ...a.type.getNoteBBoxOffset(),
86 | positionX: a.x,
87 | positionY: a.y
88 | }));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgD3ShapeLineRadial.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Animated } from 'react-native';
3 | import * as d3 from 'd3-shape';
4 | import omit from 'lodash/omit';
5 |
6 | import Path from './AnimatedSvgPath';
7 | import { listen, removeListeners } from '../animatedListener';
8 | import type { AnimatedListener } from '../animatedListener';
9 |
10 | type LineRadial = d3.LineRadial;
11 |
12 | export const args = ['angle', 'radius', 'defined', 'curve'];
13 |
14 | function createGenerator(props, generator?: LineRadial): LineRadial {
15 | generator = generator || d3.lineRadial();
16 | return args.reduce((acc: LineRadial, arg) => {
17 | const prop = props[arg];
18 | if (prop) {
19 | return acc[arg](prop);
20 | }
21 | return acc;
22 | }, generator);
23 | }
24 |
25 | function createPath(generator: LineRadial, data): string {
26 | return generator(data);
27 | }
28 |
29 | class SvgD3ShapeLineRadial extends Component {
30 | generator: LineRadial;
31 | data: AnimatedListener;
32 | constructor(props) {
33 | super(props);
34 | this.generator = createGenerator(props);
35 | this.data = listen(props.data, _ => this.setNativeProps({ _listener: true }));
36 | }
37 | setNativeProps = (props = {}) => {
38 | const argChanged = args.some((key, index) => props[key] != null);
39 | if (argChanged) {
40 | this.generator = createGenerator(props, this.generator);
41 | }
42 | if (argChanged || props._listener) {
43 | props.d = createPath(this.generator, this.data.values);
44 | }
45 | this._component && this._component.setNativeProps(props);
46 | }
47 | shouldComponentUpdate(nextProps) {
48 | const argChanged = args.some((key, index) => nextProps[key] !== this.props[key]);
49 | const dataChanged = nextProps.data !== this.props.data;
50 | if (argChanged) {
51 | this.generator = createGenerator(nextProps, this.generator);
52 | }
53 | if (dataChanged) {
54 | removeListeners(this.data);
55 | this.data = listen(nextProps.data, _ => this.setNativeProps({ _listener: true }));
56 | }
57 | return argChanged || dataChanged;
58 | }
59 | componentWillUnmount() {
60 | removeListeners(this.data);
61 | }
62 | render() {
63 | const filteredProps = omit(this.props, args);
64 | const d = createPath(this.generator, this.data.values);
65 | return (
66 | (this._component = component)}
68 | {...filteredProps}
69 | d={d}
70 | />
71 | );
72 | }
73 | }
74 | SvgD3ShapeLineRadial = Animated.createAnimatedComponent(SvgD3ShapeLineRadial);
75 | export default SvgD3ShapeLineRadial;
76 |
--------------------------------------------------------------------------------
/src/screens/FacebookMessengerSearch.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, ScrollView, View, Image, Text, TextInput } from 'react-native';
3 | import { Components } from 'expo';
4 | const { BlurView } = Components;
5 |
6 | import randomImage from '../randomImage';
7 |
8 | const items1 = [randomImage(100, 200), randomImage(100, 200), randomImage(100, 200), randomImage(100, 200)];
9 | const items2 = [randomImage(100, 200), randomImage(100, 200), randomImage(100, 200), randomImage(100, 200)];
10 | const items3 = [randomImage(100, 200), randomImage(100, 200), randomImage(100, 200), randomImage(100, 200)];
11 |
12 | const styles = StyleSheet.create({
13 | container: {
14 |
15 | },
16 | textInput: {
17 | textAlign: 'center',
18 | marginBottom: 10
19 | },
20 | category: {
21 | borderBottomWidth: StyleSheet.hairlineWidth,
22 | borderColor: 'white',
23 | padding: 10
24 | },
25 | categoryText: {
26 | color: 'white',
27 | backgroundColor: 'transparent',
28 | marginBottom: 10
29 | },
30 | itemContainer: {
31 | flexDirection: 'row',
32 | borderRadius: 10,
33 | marginRight: 10
34 | },
35 | image: {
36 | width: 100,
37 | height: 200
38 | }
39 | });
40 |
41 | export default class FlipboardCover extends Component {
42 | render() {
43 | return (
44 |
45 |
46 |
47 | Lorem
48 | {items1.map((item, i) => (
49 |
50 |
51 |
52 | ))}
53 |
54 |
55 | Ipsum
56 | {items2.map((item, i) => (
57 |
58 |
59 |
60 | ))}
61 |
62 |
63 | Dolor
64 | {items3.map((item, i) => (
65 |
66 |
67 |
68 | ))}
69 |
70 |
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/screens/SvgD3AreaAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Scale from 'd3-scale';
5 | import * as d3Shape from 'd3-shape';
6 |
7 | import randomNumber from '../randomNumber';
8 | import randomColor from '../randomColor';
9 | import Area from '../components/AnimatedSvgD3ShapeArea';
10 | import AreaRadial from '../components/AnimatedSvgD3ShapeAreaRadial';
11 |
12 | const dataLength = 10;
13 | const min = 0;
14 | const max = 100;
15 |
16 | export default class SvgD3AreaAnimation extends Component {
17 | constructor(props) {
18 | super(props);
19 | this.data = Array(dataLength).fill().map((_, index) => ({
20 | index,
21 | value: new Animated.Value(randomNumber(min, max))
22 | }));
23 | }
24 |
25 | componentDidMount() {
26 | this.animate();
27 | }
28 |
29 | animate() {
30 | Animated.sequence([
31 | Animated.delay(2000),
32 | ...this.data.map(item =>
33 | Animated.timing(item.value, {
34 | toValue: randomNumber(min, max),
35 | duration: 1000
36 | })
37 | )
38 | ]).start();
39 | }
40 |
41 | render() {
42 | const { width, height } = Dimensions.get('window');
43 | let x = d3Scale.scaleLinear().range([0, width - 10]).domain([0, dataLength - 1]);
44 | let y = d3Scale.scaleLinear().range([0, width / 2]).domain([min, max]);
45 | const outerRadius = width / 2;
46 | const innerRadius = 120;
47 | let angle = d3Scale.scaleLinear().range([0, 2 * Math.PI]).domain([0, dataLength - 1]);
48 | let r = d3Scale.scaleLinear().range([innerRadius, outerRadius]).domain([min, max]);
49 | return (
50 |
51 |
72 |
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/screens/ARExample/reducers/three.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 |
3 | import {
4 | INIT,
5 | ADD_OBJECT,
6 | ADD_OBJECTS,
7 | REMOVE_OBJECT,
8 | RESTORE_OBJECTS,
9 | SELECT_OBJECT,
10 | SELECT_OBJECT3D,
11 | DESELECT
12 | } from '../actions/ar';
13 |
14 | const initialState = {
15 | // used to highlight selected object edges
16 | highlights: null,
17 | // used for touch events to see if we touched an object
18 | raycaster: new THREE.Raycaster(),
19 | // need to track objects for raycaster
20 | object3Ds: [],
21 | // need to track objects in the scene for recalibrating object3Ds,
22 | objects: []
23 | };
24 |
25 | export default function(state = initialState, action) {
26 | switch (action.type) {
27 | case INIT:
28 | return { ...initialState, ...action.state };
29 | case ADD_OBJECT:
30 | return {
31 | ...state,
32 | objects: [...state.objects, action.object],
33 | object3Ds: [...state.object3Ds, action.object.object3D]
34 | };
35 | case ADD_OBJECTS:
36 | return {
37 | ...state,
38 | objects: [...state.objects, ...action.objects],
39 | object3Ds: [
40 | ...state.object3Ds,
41 | ...action.objects.map(object => object.object3D)
42 | ]
43 | };
44 | case REMOVE_OBJECT:
45 | return {
46 | ...state,
47 | objects: state.objects.filter(e => e !== action.object),
48 | object3Ds: state.object3Ds.filter(
49 | e => e !== action.object.object3D.userData.root
50 | )
51 | };
52 | case RESTORE_OBJECTS:
53 | return {
54 | ...state,
55 | objects: action.objects,
56 | object3Ds: action.objects.map(object => object.object3D)
57 | };
58 | case SELECT_OBJECT:
59 | return {
60 | ...state,
61 | highlights: action.highlights,
62 | selection: action.object,
63 | selection3D: action.object.object3D
64 | };
65 | case SELECT_OBJECT3D:
66 | return {
67 | ...state,
68 | highlights: action.highlights,
69 | selection: state.objects.find(
70 | object => object.object3D === action.object3D.userData.root
71 | ),
72 | selection3D: action.object3D.userData.root
73 | };
74 | case DESELECT:
75 | return {
76 | ...state,
77 | highlights: null,
78 | selection: null,
79 | selection3D: null
80 | };
81 | default:
82 | return state;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/screens/LolaTravelChat.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { StyleSheet, View, ScrollView } from 'react-native';
3 |
4 | import randomUser from '../randomUser';
5 | import randomImage from '../randomImage';
6 | import ProfileMessage from '../components/LolaTravelProfileMessage';
7 | import SliderItem from '../components/LolaTravelSliderItem';
8 | import TextMessage from '../components/LolaTravelTextMessage';
9 |
10 | const userSandra = { name: 'Sandra', image: randomUser() };
11 | const userRobin = { name: 'Robin' };
12 |
13 | const messages = [
14 | { type: 'text', user: userSandra, text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur nisl urna, imperdiet iaculis bibendum eu, scelerisque quis enim. Donec ut magna eu ligula gravida convallis quis sed leo. Nulla gravida fermentum nulla, sed rutrum augue ultricies nec. Donec lacinia efficitur tellus at ultricies. Proin porttitor pharetra efficitur. Sed consectetur, felis quis posuere laoreet, purus tortor ultricies metus, sed vehicula metus odio in sem. In accumsan, urna in pretium pellentesque, dui tortor tincidunt dui, non porttitor tellus ex eu nunc. Curabitur fermentum rutrum purus, quis volutpat purus feugiat eget.' },
15 | { type: 'text', user: userRobin, text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' },
16 | { type: 'profile', source: { uri: userSandra.image }, text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' },
17 | { type: 'text', user: userSandra, text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur nisl urna, imperdiet iaculis bibendum eu, scelerisque quis enim. ' },
18 | {
19 | type: 'slider', items: [{
20 | title: 'Lorem ipsum', text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', source: {
21 | uri: randomImage(400, 300)
22 | }
23 | }]
24 | }
25 | ];
26 |
27 | const styles = StyleSheet.create({
28 | containerSlider: {
29 | padding: 25
30 | }
31 | });
32 |
33 | function renderMessage(message, i) {
34 | if (message.type === 'text') {
35 | return ;
36 | } else if (message.type === 'profile') {
37 | return ;
38 | } else if (message.type === 'slider') {
39 | return (
40 |
41 | {message.items.map(renderSliderItem)}
42 |
43 | );
44 | }
45 | }
46 |
47 | function renderSliderItem(item, i) {
48 | return ;
49 | }
50 |
51 | export default class LolaTravelChat extends Component {
52 | render() {
53 | return (
54 |
55 | {messages.map(renderMessage)}
56 |
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/screens/SvgD3LinkAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomNumber from '../randomNumber';
6 | import randomColor from '../randomColor';
7 | import LinkHorizontal from '../components/AnimatedSvgD3ShapeLinkHorizontal';
8 | import LinkRadial from '../components/AnimatedSvgD3ShapeLinkRadial';
9 | import LinkVertical from '../components/AnimatedSvgD3ShapeLinkVertical';
10 |
11 | export default class SvgD3LineAnimation extends Component {
12 | constructor(props) {
13 | super(props);
14 | const { width, height } = Dimensions.get('window');
15 | this.source = [
16 | new Animated.Value(randomNumber(0, width)),
17 | new Animated.Value(randomNumber(0, height))
18 | ];
19 | this.target = [
20 | new Animated.Value(randomNumber(0, width)),
21 | new Animated.Value(randomNumber(0, height))
22 | ];
23 | }
24 |
25 | componentDidMount() {
26 | this.animate();
27 | }
28 |
29 | animate() {
30 | const { width, height } = Dimensions.get('window');
31 | Animated.sequence([
32 | Animated.spring(this.source[0], {
33 | toValue: randomNumber(0, width)
34 | }),
35 | Animated.spring(this.source[1], {
36 | toValue: randomNumber(0, height)
37 | }),
38 | Animated.spring(this.target[0], {
39 | toValue: randomNumber(0, width)
40 | }),
41 | Animated.spring(this.target[1], {
42 | toValue: randomNumber(0, height)
43 | })
44 | ]).start(() => this.animate());
45 | }
46 |
47 | render() {
48 | const { width, height } = Dimensions.get('window');
49 | return (
50 |
51 |
74 |
75 | );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/screens/SvgD3VoronoiAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Array from 'd3-array';
5 |
6 | import Voronoi from '../components/AnimatedSvgD3Voronoi';
7 | import Path from '../components/AnimatedSvgPath';
8 |
9 | const voronoiLength = 10;
10 |
11 | class PolygonPath extends Component {
12 | setNativeProps(props) {
13 | if (props.polygon) {
14 | props.d = 'M' + props.polygon.join('L') + 'Z';
15 | }
16 | this._component.setNativeProps(props);
17 | }
18 | render() {
19 | const { polygon, ...props } = this.props;
20 | return (
21 | (this._component = component)}
23 | {...props}
24 | d={'M' + polygon.join('L') + 'Z'}
25 | fill="none"
26 | stroke="black"
27 | />
28 | );
29 | }
30 | }
31 |
32 | export default class SvgVoronoiAnimation extends Component {
33 | constructor(props) {
34 | super(props);
35 | const { width } = Dimensions.get('window');
36 | this.data = d3Array
37 | .range(voronoiLength)
38 | .map(_ => [
39 | new Animated.Value(Math.random() * width),
40 | new Animated.Value(Math.random() * width)
41 | ]);
42 | }
43 |
44 | componentDidMount() {
45 | this.animate();
46 | }
47 |
48 | animate() {
49 | const { width } = Dimensions.get('window');
50 | Animated.sequence(
51 | this.data.map(item =>
52 | Animated.sequence([
53 | Animated.timing(item[0], {
54 | toValue: Math.random() * width,
55 | duration: 1000
56 | }),
57 | Animated.timing(item[1], {
58 | toValue: Math.random() * width,
59 | duration: 1000
60 | })
61 | ])
62 | )
63 | ).start(() => this.animate());
64 | }
65 |
66 | render() {
67 | const { width, height } = Dimensions.get('window');
68 | const size = [width, width];
69 | return (
70 |
71 |
84 |
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/screens/StaggeredMapAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as topojson from 'topojson';
5 |
6 | import randomNumber from '../randomNumber';
7 | import GeoPath from '../components/AnimatedSvgD3GeoPath';
8 | import Circle from '../components/AnimatedSvgCircle';
9 |
10 | async function fetchJSON() {
11 | const response = await fetch('https://d3js.org/us-10m.v1.json');
12 | return response.json();
13 | }
14 |
15 | export default class StaggeredMapAnimation extends Component {
16 | constructor(props) {
17 | super(props);
18 | this.state = { features: [] };
19 | const { width, height } = Dimensions.get('window');
20 | this.points = Array(8)
21 | .fill()
22 | .map(_ => [
23 | randomNumber(100, width - 100),
24 | randomNumber(50, 200)
25 | ]);
26 | this.r = this.points.map(_ => new Animated.Value(0));
27 | }
28 | async componentWillMount() {
29 | const us = await fetchJSON();
30 | const features = topojson.feature(us, us.objects.states).features;
31 | this.setState(
32 | {
33 | features
34 | },
35 | () => this.animate()
36 | );
37 | }
38 | animate() {
39 | const animation = Animated.sequence([
40 | Animated.delay(1000),
41 | Animated.stagger(
42 | 100,
43 | this.r.map(animValue =>
44 | Animated.spring(animValue, { toValue: 10 })
45 | )
46 | )
47 | ]);
48 | animation.start();
49 | }
50 | render() {
51 | const { width, height } = Dimensions.get('window');
52 | return (
53 |
54 |
78 |
79 | );
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgD3ShapeArea.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Animated } from 'react-native';
4 | import * as d3 from 'd3-shape';
5 | import omit from 'lodash/omit';
6 |
7 | import Path from './AnimatedSvgPath';
8 | import { listen, removeListeners } from '../animatedListener';
9 | import type { AnimatedListener } from '../animatedListener';
10 |
11 | type Area = d3.Area;
12 |
13 | export const args = [
14 | 'x',
15 | 'x0',
16 | 'x1',
17 | 'y',
18 | 'y0',
19 | 'y1',
20 | 'defined',
21 | 'curve',
22 | 'lineX0',
23 | 'lineY0',
24 | 'lineX1',
25 | 'lineY1'
26 | ];
27 |
28 | function createGenerator(props, generator?: Area): Area {
29 | generator = generator || d3.area();
30 | return args.reduce((acc: Area, arg) => {
31 | const prop = props[arg];
32 | if (prop) {
33 | return acc[arg](prop);
34 | }
35 | return acc;
36 | }, generator);
37 | }
38 |
39 | function createPath(generator: Area, data): string {
40 | return generator(data);
41 | }
42 |
43 | class SvgD3ShapeArea extends Component {
44 | generator: Area;
45 | data: AnimatedListener;
46 | constructor(props) {
47 | super(props);
48 | this.generator = createGenerator(props);
49 | this.data = listen(props.data, _ => this.setNativeProps({ _listener: true }));
50 | }
51 | setNativeProps = (props = {}) => {
52 | const argChanged = args.some((key, index) => props[key] != null);
53 | if (argChanged) {
54 | this.generator = createGenerator(props, this.generator);
55 | }
56 | if (argChanged || props._listener) {
57 | props.d = createPath(this.generator, this.data.values);
58 | }
59 | this._component && this._component.setNativeProps(props);
60 | };
61 | shouldComponentUpdate(nextProps) {
62 | const argChanged = args.some(
63 | (key, index) => nextProps[key] !== this.props[key]
64 | );
65 | const dataChanged = nextProps.data !== this.props.data;
66 | if (argChanged) {
67 | this.generator = createGenerator(nextProps, this.generator);
68 | }
69 | if (dataChanged) {
70 | removeListeners(this.data);
71 | this.data = listen(nextProps.data, _ => this.setNativeProps({ _listener: true }));
72 | }
73 | return argChanged || dataChanged;
74 | }
75 | componentWillUnmount() {
76 | removeListeners(this.data);
77 | }
78 | render() {
79 | const filteredProps = omit(this.props, args);
80 | const d = createPath(this.generator, this.data.values);
81 | return (
82 | (this._component = component)}
84 | {...filteredProps}
85 | d={d}
86 | />
87 | );
88 | }
89 | }
90 | SvgD3ShapeArea = Animated.createAnimatedComponent(SvgD3ShapeArea);
91 | export default SvgD3ShapeArea;
92 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgD3ShapeAreaRadial.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Animated } from 'react-native';
4 | import * as d3 from 'd3-shape';
5 | import omit from 'lodash/omit';
6 |
7 | import Path from './AnimatedSvgPath';
8 | import { listen, removeListeners } from '../animatedListener';
9 | import type { AnimatedListener } from '../animatedListener';
10 |
11 | type AreaRadial = d3.AreaRadial;
12 |
13 | export const args = ['angle', 'startAngle', 'endAngle', 'radius', 'innerRadius', 'outerRadius', 'defined', 'curve', 'lineStartAngle', 'lineInnerRadius', 'lineEndAngle', 'lineOuterRadius'];
14 |
15 | function createGenerator(props, generator?: AreaRadial): AreaRadial {
16 | generator = generator || d3.areaRadial();
17 | return args.reduce((acc: AreaRadial, arg) => {
18 | const prop = props[arg];
19 | if (prop) {
20 | return acc[arg](prop);
21 | }
22 | return acc;
23 | }, generator);
24 | }
25 |
26 | function createPath(generator: AreaRadial, data): string {
27 | return generator(data);
28 | }
29 |
30 | class SvgD3ShapeAreaRadial extends Component {
31 | listeners: any[];
32 | generator: AreaRadial;
33 | data: AnimatedListener;
34 | constructor(props) {
35 | super(props);
36 | this.generator = createGenerator(props);
37 | this.data = listen(props.data, _ => this.setNativeProps({ _listener: true }));
38 | }
39 | setNativeProps = (props = {}) => {
40 | const argChanged = args.some((key, index) => props[key] != null);
41 | if (argChanged) {
42 | this.generator = createGenerator(props, this.generator);
43 | }
44 | if (argChanged || props._listener) {
45 | props.d = createPath(this.generator, this.data.values);
46 | }
47 | this._component && this._component.setNativeProps(props);
48 | }
49 | shouldComponentUpdate(nextProps) {
50 | const argChanged = args.some((key, index) => nextProps[key] !== this.props[key]);
51 | const dataChanged = nextProps.data !== this.props.data;
52 | if (argChanged) {
53 | this.generator = createGenerator(nextProps, this.generator);
54 | }
55 | if (dataChanged) {
56 | removeListeners(this.data);
57 | this.data = listen(nextProps.data, _ => this.setNativeProps({ _listener: true }));
58 | }
59 | return argChanged || dataChanged;
60 | }
61 | componentWillUnmount() {
62 | removeListeners(this.data);
63 | }
64 | render() {
65 | const filteredProps = omit(this.props, args);
66 | const d = createPath(this.generator, this.data.values);
67 | return (
68 | (this._component = component)}
70 | {...filteredProps}
71 | d={d}
72 | />
73 | );
74 | }
75 | }
76 | SvgD3ShapeAreaRadial = Animated.createAnimatedComponent(SvgD3ShapeAreaRadial);
77 | export default SvgD3ShapeAreaRadial;
78 |
--------------------------------------------------------------------------------
/src/screens/SvgTSpanAnimation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * BUG: Cannot animate TSpan delta
3 | */
4 | import React, { Component } from 'react';
5 | import { View, Animated, Dimensions } from 'react-native';
6 | import { Svg } from 'expo';
7 |
8 | import randomColor from '../randomColor';
9 | import randomNumber from '../randomNumber';
10 | import Text from '../components/AnimatedSvgText';
11 | import TSpan from '../components/AnimatedSvgTSpan';
12 |
13 | const min = 1;
14 | const max = 50;
15 | const text = 'abc';
16 |
17 | export default class SvgTSpanAnimation extends Component {
18 | constructor(props) {
19 | super(props);
20 | this.dx = Array(text.length)
21 | .fill()
22 | .map(_ => new Animated.Value(randomNumber(min, max)));
23 | this.dy = Array(text.length)
24 | .fill()
25 | .map(_ => new Animated.Value(randomNumber(min, max)));
26 | this.fontSize = new Animated.Value(randomNumber(12, 50));
27 | }
28 |
29 | componentDidMount() {
30 | this.animate();
31 | }
32 |
33 | animate = () => {
34 | Animated.parallel([
35 | ...this.dx.map(animValue =>
36 | Animated.spring(animValue, {
37 | toValue: randomNumber(min, max)
38 | })
39 | ),
40 | ...this.dy.map(animValue =>
41 | Animated.spring(animValue, {
42 | toValue: randomNumber(min, max)
43 | })
44 | ),
45 | Animated.spring(this.fontSize, {
46 | toValue: randomNumber(12, 50)
47 | })
48 | ]).start(() => this.animate());
49 | };
50 |
51 | render() {
52 | const { width, height } = Dimensions.get('window');
53 | return (
54 |
55 |
89 |
90 | );
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/components/AnimatedSvgD3GeoPath.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { Component } from 'react';
3 | import { Animated } from 'react-native';
4 | import * as d3 from 'd3-geo';
5 | import omit from 'lodash/omit';
6 |
7 | import Path from './AnimatedSvgPath';
8 | import { listen, removeListeners } from '../animatedListener';
9 | import type { AnimatedListener } from '../animatedListener';
10 |
11 | type GeoPath = d3.GeoPath;
12 |
13 | type Props = GeoPath & {
14 | object?: Object
15 | };
16 |
17 | export const args = [
18 | 'area',
19 | 'bounds',
20 | 'centroid',
21 | 'measure',
22 | 'projection',
23 | 'pointRadius'
24 | ];
25 |
26 | function createGenerator(props, generator?: GeoPath): GeoPath {
27 | generator = generator || d3.geoPath();
28 | return args.reduce((acc: GeoPath, arg) => {
29 | const prop = props[arg];
30 | if (prop) {
31 | return acc[arg](prop);
32 | }
33 | return acc;
34 | }, generator);
35 | }
36 |
37 | function getPath(generator: GeoPath, object): string {
38 | return generator(object);
39 | }
40 |
41 | class SvgD3GeoPath extends Component {
42 | props: Props;
43 | generator: GeoPath;
44 | object: AnimatedListener;
45 | _component: any;
46 | _components: Object;
47 |
48 | constructor(props: Props) {
49 | super(props);
50 | this.generator = createGenerator(props);
51 | this.object = listen(props.object, _ =>
52 | this.setNativeProps({ _listener: true })
53 | );
54 | }
55 | setNativeProps = (props = {}) => {
56 | const argChanged = args.some((key, index) => props[key] != null);
57 | if (argChanged) {
58 | this.generator = createGenerator(props, this.generator);
59 | }
60 | if (argChanged || props.object || props._listener) {
61 | props.d = getPath(
62 | this.generator,
63 | props.object || this.object.values
64 | );
65 | }
66 | this._component && this._component.setNativeProps(props);
67 | };
68 | shouldComponentUpdate(nextProps: Props) {
69 | const argChanged = args.some(
70 | (key, index) => nextProps[key] !== this.props[key]
71 | );
72 | const objectChanged = nextProps.object !== this.props.object;
73 | if (argChanged) {
74 | this.generator = createGenerator(nextProps, this.generator);
75 | }
76 | if (objectChanged) {
77 | removeListeners(this.object);
78 | this.object = listen(nextProps.object, _ =>
79 | this.setNativeProps({ _listener: true })
80 | );
81 | }
82 | return argChanged || objectChanged;
83 | }
84 | componentWillUnmount() {
85 | removeListeners(this.object);
86 | }
87 | render() {
88 | const filteredProps = omit(this.props, args);
89 | const d = getPath(this.generator, this.object.values);
90 | return (
91 | (this._component = component)}
93 | {...filteredProps}
94 | d={d}
95 | />
96 | );
97 | }
98 | }
99 | SvgD3GeoPath = Animated.createAnimatedComponent(SvgD3GeoPath);
100 | export default SvgD3GeoPath;
101 |
--------------------------------------------------------------------------------
/src/screens/SvgTransformAnimation.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 |
5 | import randomNumber from '../randomNumber';
6 | import Rect from '../components/AnimatedSvgRect';
7 |
8 | const scaleMin = 1;
9 | const scaleMax = 5;
10 | const skewMin = 1;
11 | const skewMax = 5;
12 | const min = 50;
13 | const max = 100;
14 |
15 | export default class SvgTransformAnimation extends Component {
16 | constructor(props) {
17 | super(props);
18 | this.originX = new Animated.Value(randomNumber(0, max));
19 | this.originY = new Animated.Value(randomNumber(0, max));
20 | this.scaleX = new Animated.Value(randomNumber(scaleMin, scaleMax));
21 | this.scaleY = new Animated.Value(randomNumber(scaleMin, scaleMax));
22 | this.skewX = new Animated.Value(randomNumber(skewMin, skewMax));
23 | this.skewY = new Animated.Value(randomNumber(skewMin, skewMax));
24 | this.translateX = new Animated.Value(randomNumber(min, max));
25 | this.translateY = new Animated.Value(randomNumber(min, max));
26 | this.rotate = new Animated.Value(randomNumber(0, 360));
27 | }
28 |
29 | componentDidMount() {
30 | this.animate();
31 | }
32 |
33 | animate = () => {
34 | Animated.sequence([
35 | Animated.spring(this.originX, {
36 | toValue: randomNumber(0, max)
37 | }),
38 | Animated.spring(this.originY, {
39 | toValue: randomNumber(0, max)
40 | }),
41 | Animated.spring(this.scaleX, {
42 | toValue: randomNumber(scaleMin, scaleMax)
43 | }),
44 | Animated.spring(this.scaleY, {
45 | toValue: randomNumber(scaleMin, scaleMax)
46 | }),
47 | Animated.spring(this.skewX, {
48 | toValue: randomNumber(skewMin, skewMax)
49 | }),
50 | Animated.spring(this.skewY, {
51 | toValue: randomNumber(skewMin, skewMax)
52 | }),
53 | Animated.spring(this.translateX, {
54 | toValue: randomNumber(min, max)
55 | }),
56 | Animated.spring(this.translateY, {
57 | toValue: randomNumber(min, max)
58 | }),
59 | Animated.spring(this.rotate, {
60 | toValue: randomNumber(0, 360)
61 | })
62 | ]).start(() => this.animate());
63 | };
64 |
65 | render() {
66 | const { width, height } = Dimensions.get('window');
67 | return (
68 |
69 |
84 |
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/screens/SvgD3ClusterBasic.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Hierarchy from 'd3-hierarchy';
5 |
6 | import Cluster from '../components/AnimatedSvgD3HierarchyCluster';
7 | import G from '../components/AnimatedSvgG';
8 | import Path from '../components/AnimatedSvgPath';
9 | import Circle from '../components/AnimatedSvgCircle';
10 | import data from '../data/hierarchyData';
11 |
12 | function project(x, y) {
13 | const angle = (x - 90) / 180 * Math.PI,
14 | radius = y;
15 | return [radius * Math.cos(angle), radius * Math.sin(angle)];
16 | }
17 |
18 | export default class SvgClusterBasic extends Component {
19 | constructor(props) {
20 | super(props);
21 | }
22 |
23 | render() {
24 | const { width, height } = Dimensions.get('window');
25 | const root = d3Hierarchy
26 | .stratify()
27 | .parentId(d => d.id.substring(0, d.id.lastIndexOf('.')))(data).sort(
28 | (a, b) => a.height - b.height || a.id.localeCompare(b.id)
29 | );
30 | const size = [360, width / 2 - 120];
31 | return (
32 |
33 |
77 |
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/screens/RealTimeChartExample.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { View, Animated, Dimensions } from 'react-native';
3 | import { Svg } from 'expo';
4 | import * as d3Shape from 'd3-shape';
5 | import * as d3Scale from 'd3-scale';
6 |
7 | import randomColor from '../randomColor';
8 | import randomNumber from '../randomNumber';
9 | import Path from '../components/AnimatedSvgPath';
10 |
11 | const min = 0;
12 | const max = 100;
13 | const lineStrokeWidth = 5;
14 | const color = randomColor();
15 | const limit = 60 * 1;
16 | const duration = 1000;
17 | const intervalDuration = 1000;
18 | const animationDuration = intervalDuration / 2;
19 |
20 | function createLineProps(path) {
21 | return {
22 | d: path
23 | };
24 | }
25 |
26 | export default class RealTimeChartExample extends Component {
27 | constructor(props) {
28 | super(props);
29 | this.now = new Date(Date.now() - duration);
30 | const { width, height } = Dimensions.get('window');
31 | const curve = d3Shape.curveCatmullRom.alpha(0.5);
32 | this.x = d3Scale
33 | .scaleTime()
34 | .range([0, width - 10])
35 | .domain([this.now - (limit - 2) * duration, this.now - duration]);
36 | this.translateXAnimValue = new Animated.Value(0);
37 | this.y = d3Scale
38 | .scaleLinear()
39 | .range([0, width / 2])
40 | .domain([min, max]);
41 | this.lineGenerator = d3Shape
42 | .line()
43 | .curve(curve)
44 | .x((d, i) => this.x(this.now - (limit - 1 - i) * duration))
45 | .y(d => this.y(d));
46 | this.state = {
47 | data: Array(limit).fill().map((_, index) => randomNumber(min, max))
48 | };
49 | setInterval(_ => {
50 | this.now = new Date();
51 | let newData = [...this.state.data, randomNumber(min, max)];
52 | this.x.domain([this.now - (limit - 2) * duration, this.now - duration]);
53 | this.setState({
54 | data: newData
55 | }, () => {
56 | Animated.timing(this.translateXAnimValue, {
57 | toValue: this.x(this.now - (limit - 1) * duration),
58 | duration: animationDuration
59 | }).start(() => {
60 | this.setState(prevState => {
61 | let data = prevState.data;
62 | data.shift();
63 | return { data }
64 | }, () => {
65 | this.translateXAnimValue.setValue(0);
66 | });
67 | })
68 | });
69 | }, intervalDuration);
70 | }
71 | render() {
72 | const { width, height } = Dimensions.get('window');
73 | this.linePath = createLineProps(this.lineGenerator(this.state.data));
74 | return (
75 |
76 |
85 |
86 | );
87 | }
88 | }
89 |
--------------------------------------------------------------------------------