React-Native HighCharts Boost Module Usage - react-native

I have a data set of four series each with 250000 points so approximately one million in total. I am creating an app with React Native. I tried using minimal HighCharts Wrapper but couldn't make the boost module work. Here is my App.js:
import React from 'react';
import { StyleSheet, View } from 'react-native';
import HighchartsReactNative from '#highcharts/highcharts-react-native'
const modules = ['boost']
export default class App extends React.Component {
constructor(props) {
super(props);
let chartSeries = []
for(let i = 0; i < 4 ; i ++){
let series = {data:[]};
for(let i= 0; i < 2500; i++){
let point = [i+1,Math.floor(220-Math.random(0,120)*10)]
series.data.push(point)
}
chartSeries.push(series);
}
this.state = {
chartOptions: {
boost:{
enabled:true,
seriesThreshold:2000
},
chart:{
zoomType:'xy'
},
series: chartSeries
}
};
}
render() {
return (
<View style={styles.container}>
<HighchartsReactNative
styles={styles.container}
options={this.state.chartOptions}
modules={modules}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
justifyContent: 'center',
flex: 1
}
});
It is really simple and it renders eventually but it takes too much time. Boost module doesn't seem to work correctly. Is my usage wrong here?

It does not work correctly, because you set the boost.seriesThreshold equal to 2000 what means that the chart does not switch into the boost module until it has got at least 2000 series.
I think you had intention to set the plotOptions.series.boostThreshold to configure the boost so that it will turn on when certain series has 2000 points, didn't you?
In sum, you just need to set the mentioned seriesThreshold to 1, remove the boost: true (as it is redundant, and true by default), and then set the plotOptions.series.boostThreshold parameter equal to 2000.
API References:
https://api.highcharts.com/highcharts/boost.seriesThreshold
https://api.highcharts.com/highcharts/plotOptions.series.boostThreshold

Related

Animated state changes without cycling values using interpolation

I'm trying to implement a simple color transition in react native. The current use case is for an Animated.TextInput, I want the backgroundColor to transition depending on the state. Let's say the component can have the following visual states:
state
description
normal
unfocussed input field
error
unfocussed input field with an error
warning
unfocussed input field with a warning
focus
focussed input field disregarding the error/warning flags
A simplified version of such component:
import React, { useState, useEffect } from 'react'
import { Animated, TextInput as RNTextInput } from 'react-native'
const AnimatedTextInput = Animated.createAnimatedComponent(RNTextInput)
interface Props {
errorMessage?: string
warningMessage?: string
}
enum IndicatorState {
Normal,
Error,
Warning,
Focus
}
export const TextInput: React.FunctionComponent<Props> = (props) => {
const [isInFocus, setIsInFocus] = useState(false)
useEffect(() => {
Animated.timing(indicatorStateAnim, {
toValue: getIndicatorState(),
duration: 100,
useNativeDriver: false
})
}, [props.errorMessage, props.warningMessage, isInFocus])
const getIndicatorState = (): IndicatorState => {
if (isInFocus) return IndicatorState.Focus
if (props.errorMessage) return IndicatorState.Error
if (props.warningMessage) return IndicatorState.Error
return IndicatorState.Normal
}
const indicatorStateAnim = new Animated.Value(getIndicatorState())
return (
<AnimatedTextInput
onFocus={() => setIsInFocus(true)}
onBlur={() => setIsInFocus(false)}
style={{
backgroundColor: indicatorStateAnim.interpolate({
inputRange: [
IndicatorState.Normal,
IndicatorState.Error,
IndicatorState.Warning,
IndicatorState.Focus
],
outputRange: [
'#e3e3e3',
'#ff0000',
'#ffff00',
'#0000ff'
]
})
}}
/>
)
}
Using this method switching between two states does transition but it does so by cycling through the interpolated values (as expected of course). A little diagram showing the logic and difference between the current – expected – situation and the desired outcome of transitions.
Possible transition graphed in current and desired situation:
Example of transition between states in current and desired situation:
In short; I'm looking for a solution for transitioning between multiple visual states using an animation without cycling through interpolated values (a.k.a. disco input fields)

commas in Decimals using <Text>

I have a value which is in a Holder which is supposed to show currency and it shows the currency directly from the REST API, fine and good , but rather than show the value like this
123,456,789.00
It shows like this 123456789.00 I have been trying to format it to no avail. Is there a way to do this in React Native? The holder is looking like this
{this.state.balance}
Searched the internet and could not come up with something good at this point.
Use toLocaleString() to add those commas.
Sample Example:
let num = 123456789.00
console.log(num.toLocaleString())
Full Working Example: Expo Snack
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
let num = 123456789.1;
export default function App() {
const toCommaConvertion = (num) => {
num += 0.001;
let str = [...num.toLocaleString()];
str.pop();
return str.join('');
};
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{toCommaConvertion(num)}</Text>
</View>
);
}
You should convert the number to comma seperated formate
class BalanceExample extends Component {
constructor(props) {
super(props);
this.state = {
balance: 123456789,
};
}
numberWithCommas = (x) => x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
render() {
return (
<Text>{this.numberWithCommas(this.state.balance)}</Text>
);
}
}
export default BalanceExample;

How can I pass the current theme of the app into StyleSheet.create()?

Due to various limitations, I cannot store the current theme in props. I have to use a custom props that I cannot modify at the moment. I have been able to get around this by using React Context to store the current theme.
I have a class called pageStyle. In it, I have a pageContainer that I'd like to be able to set backgroundColor either to dark or light values.
I am able to get the current theme in a separate class R by using this.context.theme. R is also where I am rendering the FlatList object and passing style={pageStyle.pageContainer}.
I could create two separate pageContainers, called pageContainerDark and pageContainerLight, and have the correct respective backgroundColors for both. However, this feels messy to me. This would also require having a method for each style page to determine which pageContainer to use and is not scalable whatsoever. I have also tried using a global variable but was having some issues with this.
Is there a cleaner way to set pageContainer's backgroundColor based on the current theme?
I have attached the pageStyle.ts file below.
//pageStyle.ts
import { StyleSheet} from 'react-native';
const pageStyles = StyleSheet.create({
pageContainer: {
flex: 1,
backgroundColor: "#232323"
},
// pageContainerDark: {
// flex: 1,
// backgroundColor: "#232323"
// },
// pageContainerLight: {
// flex: 1,
// backgroundColor: "#FFFFFF"
// },
pageContentContainer: {
paddingBottom: 0
}
});
// export function getPageContainerFromPageStyles(theme: string) {
// if (theme == 'light') {
// return pageStyle.pageContainerForLightTheme
// } else {
// return pageStyle.pageContainerForDarkTheme
// }
// }
export { pageStyle };

Trying to load obj & mtl file with Three.js in React Native

Main objective : Load animated models exported from Maya into React Native app
Exported files : obj, mtl & png file
I have setup https://github.com/react-community/react-native-webgl in my React Native project and it is working properly.
Now, when I am trying to load the MTL file using the MTLLoader, I am getting following error:
Can't find variable: document
Apparently, the MTLLoader is calling TextureLoader which internally calls some load function which has 'document' reference. So what could be the solution to this ?
Here are the two files that I am using:
three.js
const THREE = require("three");
global.THREE = THREE;
if (!window.addEventListener)
window.addEventListener = () => { };
// require("three/examples/js/renderers/Projector");
require("three/examples/js/loaders/MTLLoader");
require("three/examples/js/loaders/OBJLoader");
export default THREE;
ThreeView.js
import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
import { WebGLView } from "react-native-webgl";
import THREE from "./three";
import { image } from "src/res/image";
export default class ThreeView extends Component {
requestId: *;
componentWillUnmount() {
cancelAnimationFrame(this.requestId);
}
onContextCreate = (gl: WebGLRenderingContext) => {
const rngl = gl.getExtension("RN");
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const renderer = new THREE.WebGLRenderer({
canvas: {
width,
height,
style: {},
addEventListener: () => { },
removeEventListener: () => { },
clientHeight: height
},
context: gl
});
renderer.setSize(width, height);
renderer.setClearColor(0xffffff, 1);
let camera, scene;
let cube;
function init() {
camera = new THREE.PerspectiveCamera(75, width / height, 1, 1100);
camera.position.y = 150;
camera.position.z = 500;
scene = new THREE.Scene();
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load('female-croupier-2013-03-26.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load('female-croupier-2013-03-26.obj', function (object) {
scene.add(object);
}, onLoading, onErrorLoading);
}, onLoading, onErrorLoading);
}
const onLoading = (xhr) => {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
};
const onErrorLoading = (error) => {
console.log('An error happened', error);
};
const animate = () => {
this.requestId = requestAnimationFrame(animate);
renderer.render(scene, camera);
// cube.rotation.y += 0.05;
gl.flush();
rngl.endFrame();
};
init();
animate();
};
render() {
return (
<View style={styles.container}>
<WebGLView
style={styles.webglView}
onContextCreate={this.onContextCreate}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
},
webglView: {
width: 300,
height: 300
}
});
This error is as others have said caused by threejs trying to use features from a browser which react-native does not have.
I've gotten so far as to be able to load the textures (which is the stage you're getting the error from) by monkey patching the texture loader to use the loader in react-native-webgl. Add this in your init function (right near the top preferably).
//make sure you have defined renderer and rngl
/*
const renderer = new THREE.WebGLRenderer(...)
const rngl = gl.getExtension("RN");
*/
const loadTexture = async function(url, onLoad, onProgress, onError) {
let textureObject = new THREE.Texture();
console.log("loading",url,'with fancy texture loader');
let properties = renderer.properties.get(textureObject);
var texture = await rngl.loadTexture({yflip: false, image: url});
/*
rngl.loadTexture({ image: url })
.then(({ texture }) => {
*/
console.log("Texture [" + url + "] Loaded!")
texture.needsUpdate = true;
properties.__webglTexture = texture;
properties.__webglInit = true;
console.log(texture);
if (onLoad !== undefined) {
//console.warn('loaded tex', texture);
onLoad(textureObject);
}
//});
return textureObject;
}
THREE.TextureLoader.prototype.load = loadTexture;
This solves the problem of loading textures and I can see them load in Charles but they still don't render on a model so I'm stuck past that point. Technically a correct answer but you'll be stuck as soon as you've implemented it. I'm hoping you can comment back and tell me you've gotten further.
I had a similar setup and encountered same issue. My option was to switch to JSONLoader which doesn’t need document to render in react-native. So, I just loaded my model in Blender with a three-js addon, then exported it as json. Just check out this process of adding a three-js adon to Blender
https://www.youtube.com/watch?v=mqjwgTAGQRY
All the best
this might get you closer:
The GLTF format supports embedding texture images (as base64). If your asset pipeline allows it, you could convert to GLTF and then load into three/react-native.
I had to provide some "window" polyfills for "decodeUriComponent" and "atob" because GLTFLoader uses FileLoader to parse the base64:
I've successfully loaded embedded buffers, but you'll need more polyfills to load textures. TextureLoader uses ImageLoader, which uses document.createElementNS
You are using the MTLLoader which uses TextureLoader, and the TextureLoader uses the ImageLoader.
The imageloader uses the document.createElementNS() function.
what i did to solve this was to directly call the THREEjs TextureLoader:
let texture = new THREE.Texture(
url //URL = a base 64 JPEG string in this case
);
(for the use of Texture check the Texture documentation)
Then i used the Image class from React native (instead of the THREEjs Image, which requires the DOM to be constructed) to give that to the Texture as a property:
import { Image } from 'react-native';
var img = new Image(128, 128);
img.src = url;
texture.normal = img;
And then finally map the texture over the target material:
const mat = new THREE.MeshPhongMaterial();
mat.map = texture;
In the react native documentation it will explain how the react native Image element can be used, it supports base64 encoded JPEG.
Maybe there's a way for you to single out the part where it calls for the TextureLoader and replace that part with this answer. Let me know how it works out.
side note, i havent tried to display this yet in my webGLView, but in the logs it looked like normal threejs objects, it's worth the try
Use TextureLoader from expo-three
import { TextureLoader } from "expo-three";
export function loadTexture(resource) {
if (textureCache[resource]) {
return textureCache[resource].clone();
}
const texture = new TextureLoader().load(resource);
texture.magFilter = NearestFilter;
texture.minFilter = NearestFilter;
textureCache[resource] = texture;
return texture;
}
Source: https://github.com/EvanBacon/Expo-Crossy-Road/blob/master/src/Node/Generic.js

React Native atob() / btoa() not working without remote JS debugging

I have a testing app in react native, and all works fine when I have enabled the debug js remotely. It works fine in device (from XCode) and simulator, after run:
react-native run ios
The problem is that if I stop remote js debugging, the login test not works anymore.The login logic is very simple, I'm making a fetch to an api to test a login, the API endpoint is over https.
What I need to change?
Updated: This code works perfetly with JS Debug Remote Enabled, if I disable it, it not works anymore.
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react'
import {
AppRegistry,
StyleSheet,
View,
Button,
Alert
} from 'react-native'
export default class MyClass extends Component {
constructor (props) {
super(props)
this.testFetch = this.testFetch.bind(this)
}
async testFetch () {
const email = 'email#example.com'
const password = '123456'
try {
const response = await fetch('https://www.example.com/api/auth/login', {
/* eslint no-undef: 0 */
method: 'POST',
headers: {
'Accept': 'application/json' /* eslint quote-props: 0 */,
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa(email + ':' + password)
}
})
Alert.alert('Error fail!', 'Fail')
console.log(response)
} catch (error) {
Alert.alert('Error response!', 'Ok')
}
}
render () {
return (
<View style={styles.container}>
<Button
onPress={this.testFetch}
title="Test me!"
/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5
}
})
AppRegistry.registerComponent('testingReactNative', () => MyClass)
Thanks.
That's the ways I fixed it. As #chemitaxis suggests, add base-64 module from NPM:
npm i -S base-64
Based on it, I propose a couple of ways to use it:
Importing it in files you need it
Then, you can import 'encode' and 'decode' methods using aliases, this way:
import {decode as atob, encode as btoa} from 'base-64'
Of course, using aliases is optional.
Polyfill way
You can set atob and btoa as global variables on React Native. Then, you won't need to import them on each file you need it. You have to add this code:
import {decode, encode} from 'base-64'
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob) {
global.atob = decode;
}
You need to place it at the beginning of your index.js, so that it can be loaded before another file uses atob and btoa.
I suggest you to copy it on a separate file (let's say base64Polyfill.js), and then import it on index.js
Here you go (https://sketch.expo.io/BktW0xdje). Create a separate component (e.g. Base64.js), import it and it's ready to use. For instance Base64.btoa('123');
// #flow
// Inspired by: https://github.com/davidchambers/Base64.js/blob/master/base64.js
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const Base64 = {
btoa: (input:string = '') => {
let str = input;
let output = '';
for (let block = 0, charCode, i = 0, map = chars;
str.charAt(i | 0) || (map = '=', i % 1);
output += map.charAt(63 & block >> 8 - i % 1 * 8)) {
charCode = str.charCodeAt(i += 3/4);
if (charCode > 0xFF) {
throw new Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
},
atob: (input:string = '') => {
let str = input.replace(/=+$/, '');
let output = '';
if (str.length % 4 == 1) {
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (let bc = 0, bs = 0, buffer, i = 0;
buffer = str.charAt(i++);
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
buffer = chars.indexOf(buffer);
}
return output;
}
};
export default Base64;
The main part of your question has been answered, but I see there's still some uncertainty about why it works with remote debugging enabled.
When debugging remotely, the JavaScript code is actually running in Chrome, and the diffs to the virtual dom are being communicated to the native app over a web socket.
http://facebook.github.io/react-native/docs/javascript-environment
atob and btoa are available in the context of the browser, and that's why it works there.
When you stop debugging, though, the JavaScript is again interpreted in a process on your device or simulator, which doesn't have access to functions that are provided by the browser.