Create an expanding line from a point in react native - javascript

im trying to create a personality picker as seen in the image below. The intended use of it is that depending on the score of each personality from 0 to 5 a black line will be drawn and expand to each of the five circles to indicate the score.
Here is my code so far to create this, I have gotten the circles in line and I am currently creating the black lines however I am unable to do them dynamically and I am having to plot each point specifically which may be a problem for different screensizes. I am hoping there is a better way than what I am doing right now as it is a tonne of nested Views forming shapes. Any help would be great on how to make this simpiler / more dynamic!
Thanks :)
return (
<View style={{margin: 32}}>
<Text style={{fontFamily: 'Roboto_400Regular', fontSize: 36}}>Plot your{"\n"}personality</Text>
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={{
fontFamily: 'Roboto_400Regular',
fontSize: 14,
color: '#878787',
paddingTop: 4,
alignSelf: 'flex-end'
}}>Use this to match with similar people?</Text>
<Switch
trackColor={{false: "#878787", true: "#5BF675"}}
thumbColor="#f4f3f4"
ios_backgroundColor="#878787"
onValueChange={toggleSwitch}
value={isEnabled}
/>
</View>
<View style={{alignItems: 'center', justifyContent: 'center', marginTop: 32}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 275,
width: 275,
borderRadius: 275 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 225,
width: 225,
borderRadius: 225 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 175,
width: 175,
borderRadius: 175 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 125,
width: 125,
borderRadius: 125 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 75,
width: 75,
borderRadius: 75 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '90deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '135deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '180deg'}],
position: 'absolute',
}}
/>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: 90.5,
bottom: -3,
left: 0,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '0deg'}],
position: 'absolute',
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: 137.5,
bottom: -52,
right: -116,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}],
position: 'absolute',
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: 137.5,
alignItems: 'center',
justifyContent: 'center',
bottom: -72,
left: -70,
transform: [{ rotate: '90deg'}],
position: 'absolute',
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: 137.5,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '135deg'}, {translateX: 68}],
position: 'absolute',
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: 137.5,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '180deg'}, {scaleX: 0}],
position: 'absolute',
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: 137.5,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '225deg'}, {translateX: 68}],
position: 'absolute',
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: 137.5,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '270deg'}, {translateX: 68}],
position: 'absolute',
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: (137.5),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '315deg'}, {translateX: 68}],
position: 'absolute',
}}
/>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
);

I have found an answer, although I am not 100% satisfied and believe it may fall over when screens resize.
import React from 'react';
import {View} from "react-native";
interface PersonalityPlotterDialProps {
value: number
}
function PersonalityPlotterDial(props: PersonalityPlotterDialProps) {
const {value} = props;
const widthCalculator = (value: number) => {
switch (value) {
case 0:
return 0;
case 1:
return 37.5;
case 2:
return 62.5;
case 3:
return 87.5;
case 4:
return 112.5;
case 5:
return 137.5;
default:
return 0;
}
};
const translateCalculator = (value: number) => {
switch (value) {
case 0:
return 0;
case 1:
return 1.05;
case 2:
return 1.75;
case 3:
return 2.5;
case 4:
return 3.2;
case 5:
return 3.95;
default:
return 0;
}
}
const displayCalculator = () => {
if (widthCalculator(value) === 0) {
return 'none'
}
}
return (
<View style={{alignItems: 'center', justifyContent: 'center', marginTop: 32, marginBottom: 64}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 275,
width: 275,
borderRadius: 275 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 225,
width: 225,
borderRadius: 225 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 175,
width: 175,
borderRadius: 175 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 125,
width: 125,
borderRadius: 125 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 75,
width: 75,
borderRadius: 75 / 2,
alignItems: 'center',
justifyContent: 'center',
position: 'relative'
}}>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '90deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '135deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '180deg'}],
position: 'absolute',
}}
/>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
bottom: -3,
left: 0,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '0deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '90deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '135deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
bottom: -3,
right: 0,
transform: [{ rotate: '180deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '225deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '270deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '315deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
);
}
export default PersonalityPlotterDial;

In general, there is a cleaner solution for this (maybe I will add one later), but for now, lets try and make your code work.
You can have dynamic line size by passing the width to each View component with a math formula. meaning, think of a way to represent the size of each line. for example:
0: 0px,
1: 50px,
2: 100px
and so on... (the 50px should be the radius divided by 5)
Now, for each View tag, you can just set the width by multiplying 50px with the value of the size of it(0-5: 0-250px).
This should do the trick.
(I assume you have a state or a way to keep the values of each label, and by that you multiply this value with 50px for each line)
Hey, sorry for taking so long I wasn't available so much. anyway, I wrote a small piece of code that does the behavior you want. the design isn't amazing but it does the trick. I used styled-components just for fun, you can run it easily by installing styled-component or just converting it to regular form:
App.js
import { useState } from 'react';
import styled from 'styled-components';
import Line from './components/line';
function App() {
const [values, setValues] = useState(config);
return (
<Container>
<Circle>
<Center/>
<Lines>{values.map((value) => <Line {...value} />)}</Lines></Circle>
<Menu>
{values.map((line, index) =>
<SizeButtons>
<Button onClick={() => {
const copyValues = [...values];
copyValues[index].value = line.value - 1 > 0 ? line.value - 1 : 0;
setValues(copyValues);
}}>-</Button>
<Button onClick={() => {
const copyValues = [...values];
copyValues[index].value = line.value + 1 < 5 ? line.value + 1 : 5;
setValues(copyValues);
}}>+</Button>
</SizeButtons>
)}
</Menu>
</Container>
);
}
export default App;
const Container = styled.div`
position: relative;
margin-top: 200px;
margin-left: 200px;
`;
const Circle = styled.div`
position: relative;
width: 250px;
height: 250px;
border: 1px solid black;
border-radius: 50%;
position: absolute;
top: 0;
left: 0;
`;
const Center = styled.div`
position: absolute;
top: 116px;
left: 116px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: black;
`;
const Lines = styled.div`
position: absolute;
top: 125px;
left: 125px;
`;
const SizeButtons = styled.div``;
const Menu = styled.div`
position: absolute;
top: 0;
right: 0;
`;
const Button = styled.button``;
const config = [
{ value: 1, degree: 0 },
{ value: 1, degree: 45, },
{ value: 1, degree: 90, left: '2px' },
{ value: 1, degree: 135, top: '2px', left: '1px' },
{ value: 1, degree: 181, top: '2px' },
{ value: 1, degree: 225, top: '0px' },
{ value: 1, degree: 270, top: '0px' },
{ value: 1, degree: 315, top: '0px' },
]
line.js:
import React from 'react';
import styled from 'styled-components'
const Line = (props) => {
return (
<Container {...props}/>
);
};
export default Line;
const Container = styled.div`
width: ${props => props.value * 25 }px;
background: black;
height: 2px;
transform: rotate(${props => props.degree}deg);
transform-origin: top left;
position: absolute;
top: ${props => props.top};
left: ${props => props.left}
`;
let me know if something is unclear.

Related

How to add width to line unequally around a point react native

Hello I am tried to achieve this image below, where as you increment the score of an attribute the line increases in size depending on the score.
I am at the point where the lines increases in size and decreases in size correctly. However I am finding that as they increase and decrease in size its hard to make them do this around the central point of the circles. Furthermore as I increase the width it adds to both ends of the line rather than just to one side - therefore Im ending up with something like this.
Here the lines can decrease and increase but they are not doing so around a point. Any help would be great. Here is my code so far:
import React from 'react';
import {View} from "react-native";
import { withAnchorPoint } from 'react-native-anchor-point';
interface PersonalityPlotterDialProps {
value: number
}
function PersonalityPlotterDial(props: PersonalityPlotterDialProps) {
const {value} = props;
const widthCalculator = (value: number) => {
switch (value) {
case 0:
return 0;
case 1:
return 37.5;
case 2:
return 62.5;
case 3:
return 87.5;
case 4:
return 112.5;
case 5:
return 137.5;
default:
return 0;
}
};
const displayCalculator = () => {
if (widthCalculator(value) === 0) {
return 'none'
}
}
return (
<View style={{alignItems: 'center', justifyContent: 'center', marginTop: 32, marginBottom: 64}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 275,
width: 275,
borderRadius: 275 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 225,
width: 225,
borderRadius: 225 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 175,
width: 175,
borderRadius: 175 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 125,
width: 125,
borderRadius: 125 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 75,
width: 75,
borderRadius: 75 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '90deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '135deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '180deg'}],
position: 'absolute',
}}
/>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
bottom: -3,
left: 0,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '0deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
bottom: -52,
right: -116,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
bottom: -72,
left: -70,
transform: [{ rotate: '90deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
bottom: -50,
right: -19,
transform: [{ rotate: '135deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
bottom: -3,
right: 0,
transform: [{ rotate: '180deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '225deg'}, {translateX: 68}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
bottom: 67,
right: -67,
transform: [{ rotate: '270deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
bottom: 46,
right: -116,
transform: [{ rotate: '315deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
);
}
export default PersonalityPlotterDial;
Please let me know if im missing anything as I think it may be some way of doing it with transformations around a point - any help even pointing me in the right direction would be great!
I have come up with a solution but dislike it and think it would very easily break on different screen sizes but for now it works. Here is my new code:
import React from 'react';
import {View} from "react-native";
interface PersonalityPlotterDialProps {
value: number
}
function PersonalityPlotterDial(props: PersonalityPlotterDialProps) {
const {value} = props;
const widthCalculator = (value: number) => {
switch (value) {
case 0:
return 0;
case 1:
return 37.5;
case 2:
return 62.5;
case 3:
return 87.5;
case 4:
return 112.5;
case 5:
return 137.5;
default:
return 0;
}
};
const translateCalculator = (value: number) => {
switch (value) {
case 0:
return 0;
case 1:
return 1.05;
case 2:
return 1.75;
case 3:
return 2.5;
case 4:
return 3.2;
case 5:
return 3.95;
default:
return 0;
}
}
const displayCalculator = () => {
if (widthCalculator(value) === 0) {
return 'none'
}
}
return (
<View style={{alignItems: 'center', justifyContent: 'center', marginTop: 32, marginBottom: 64}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 275,
width: 275,
borderRadius: 275 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 225,
width: 225,
borderRadius: 225 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 175,
width: 175,
borderRadius: 175 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 125,
width: 125,
borderRadius: 125 / 2,
alignItems: 'center',
justifyContent: 'center'
}}>
<View style={{
borderColor: '#DBDBDB',
borderWidth: 1,
height: 75,
width: 75,
borderRadius: 75 / 2,
alignItems: 'center',
justifyContent: 'center',
position: 'relative'
}}>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '90deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '135deg'}],
position: 'absolute',
}}
/>
<View
style={{
borderColor: '#DBDBDB',
borderWidth: 1,
width: 275,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '180deg'}],
position: 'absolute',
}}
/>
<View style={{position: 'absolute', justifyContent: 'center', alignItems: 'center'}}>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
bottom: -3,
left: 0,
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '0deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '45deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '90deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '135deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
bottom: -3,
right: 0,
transform: [{ rotate: '180deg'}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '225deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '270deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
<View
style={{
backgroundColor: '#000',
borderWidth: 3,
width: widthCalculator(value),
alignItems: 'center',
justifyContent: 'center',
transform: [{ rotate: '315deg'}, {translateX: (translateCalculator(value)) * 17.5}],
position: 'absolute',
display: displayCalculator(),
}}
/>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
);
}
export default PersonalityPlotterDial;

How to make an element to be always in the top right corner of the screen

I am trying to make an element with style={styles.settingsButtonContainer} not to depend on position of other elements on the view, but to be always in the top right corner of the screen. I am trying in this way:
<View style={styles.container}>
<RCActivityIndicator animating={showActivityIndicator} />
<RCModal title={alertTitle} visible={alertVisible} onButtonPress={alertOnPress} />
<ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'center' }}>
<Text style={styles.title}>Noughts and Crosses</Text>
<Text style={styles.tip}>Get {winCondition()} in row, column or diagonal</Text>
<TouchableHighlight underlayColor="#000000" style={[styles.button, styles.newGameButton]} onPress={setInitialFieldState}>
<Text style={styles.buttonText}>New game</Text>
</TouchableHighlight>
<Text style={styles.turnContainer}>Turn: <Text style={[styles.turn, { color: turn === 'X' ? '#2E86C1' : '#E74C3C'}]}>{turn}</Text></Text>
<Field state={fieldState} size={fieldSize} winner={winCombination} onCellPress={onCellPress} />
</ScrollView>
<View style={styles.settingsButtonContainer}>
<TouchableHighlight underlayColor={theme.colors.secondary} style={styles.settingsButton} onPress={onSettingsPress}>
<Image source={require('../img/settings.png')} style={styles.settingsIcon} />
</TouchableHighlight>
</View>
</View>
const styles = StyleSheet.create({
container: {
flex: 1
},
title: {
textAlign: 'center',
fontSize: 33,
fontFamily: theme.fonts.primary,
color: theme.colors.text
},
button: {
alignItems: 'center',
padding: 5,
margin: 5,
marginLeft: 10,
marginRight: 10,
borderRadius: 20,
height: 35,
justifyContent: 'center'
},
newGameButton: {
backgroundColor: theme.colors.primary,
paddingLeft: 15,
paddingRight: 15,
},
buttonText: {
fontSize: 25,
fontFamily: theme.fonts.primary,
color: theme.colors.text
},
tip: {
textAlign: 'center',
fontSize: 25,
marginLeft: 10,
marginRight: 10,
fontFamily: theme.fonts.primary,
color: theme.colors.text
},
turnContainer: {
textAlign: 'center',
fontSize: 33,
fontFamily: theme.fonts.primary,
color: theme.colors.text,
marginLeft: 10,
},
turn: {
fontSize: 30,
fontFamily: theme.fonts.noughtsAndCrosses,
},
privacy: {
textAlign: 'center',
color: theme.colors.secondaryText,
fontSize: 15,
textDecorationLine: "underline",
marginTop: 20
},
settingsButtonContainer: {
position: 'absolute',
top: 5,
right: 5,
height: 50,
width: 50,
alignSelf: 'flex-end',
},
settingsButton: {
height: 50,
width: 50,
borderRadius: 25,
},
settingsIcon: {
height: 50,
width: 50
},
settings: {
marginTop: 30
},
buttonsContainer: {
flexDirection: 'row',
alignItems: 'center'
}
});
But it just takes a full row for itself, so the ScrollView doesn't take 100% height. How to make it work?
try to wrap the settingsButtonContainer
and use
settingButtonContainerWrapped: {
alignItem: 'flex-end',
flexDirection : 'row',
},
Actually the issue was in another element, that was on top of the ScrollView - it was RCActivityIndicator who took this place. I styled it, and it worked.
Use css:
[eleSelector] {
position: absolute;
right: 0;
top: 0;
}
Check HTML element Selector in CSS

Focus Shape Inside Expo Camera

I am trying to have a focus area shape in my live camera like the picture below (circle is where one would take the image while everything out of the circle is darkened). I am currently using the expo camera exactly how it is shown in the code below. Does anyone know how I could add in a circle or square on top of this so that the user would know exactly where to take a picture of an object. Any help would be appreciated.
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1 }}
type={this.state.type}
flashMode={this.state.flashMode}
ref={(camera) => (this.camera = camera)}
zoom={this.state.cameraZoomValue}
autoFocus={Camera.Constants.AutoFocus.on}
whiteBalance={Camera.Constants.WhiteBalance.auto}
faceDetectorSettings={{
mode: FaceDetector.Constants.Mode.accurate,
detectLandmarks: FaceDetector.Constants.Landmarks.all,
runClassifications: FaceDetector.Constants.Classifications.all,
minDetectionInterval: 500,
tracking: true,
}}
onFacesDetected={this.onFacesDetected}
onFacesDetectionError={this.onFacesDetectionError}
>
</Camera>
</View>
you could try to add some view with border(top, left, right, bottom) and setting position absolutely to achieve it. Here is my code(not sort out very clean), but I think it could help.
import { StyleSheet, View ,Dimensions} from "react-native";
class App extends Component {
render() {
const { height, width } = Dimensions.get("window");
const maskRowHeight = Math.round((height - 200) / 20);
const maskColWidth = (width - 200) / 2;
return (
<View style={styles.app}>
<View style={styles.maskOutter}>
<View
style={[
{ flex: maskRowHeight },
styles.maskRow,
styles.maskFrame
]}
/>
<View style={[{ flex: 40 }, styles.maskCenter]}>
<View style={[{ width: maskColWidth }, styles.maskFrame]} />
<View style={styles.maskInner}>
<View
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: 10,
borderColor: "#FFFFFF",
borderTopWidth: 1,
}}
/>
<View
style={{
position: "absolute",
bottom: 0,
left: 0,
width: "100%",
height: 10,
borderColor: "#FFFFFF",
borderBottomWidth: 1,
}}
/>
<View
style={{
position: "absolute",
top: 0,
left: 0,
width: 20,
height: "100%",
borderColor: "#FFFFFF",
borderLeftWidth: 1,
}}
/>
<View
style={{
position: "absolute",
top: 0,
right: 0,
width: 20,
height: "100%",
borderColor: "#FFFFFF",
borderRightWidth: 1,
}}
/>
<View
style={{
position: "absolute",
top: 0,
left: 0,
width: 30,
height: 30,
borderColor: "#00BED6",
borderTopWidth: 4,
borderLeftWidth: 4
}}
/>
<View
style={{
position: "absolute",
top: 0,
right: 0,
width: 30,
height: 30,
borderColor: "#00BED6",
borderTopWidth: 4,
borderRightWidth: 4
}}
/>
<View
style={{
position: "absolute",
bottom: 0,
left: 0,
width: 30,
height: 30,
borderColor: "#00BED6",
borderBottomWidth: 4,
borderLeftWidth: 4
}}
/>
<View
style={{
position: "absolute",
bottom: 0,
right: 0,
width: 30,
height: 30,
borderColor: "#00BED6",
borderBottomWidth: 4,
borderRightWidth: 4
}}
/>
</View>
<View style={[{ width: maskColWidth }, styles.maskFrame]} />
</View>
<View
style={[
{ flex: maskRowHeight },
styles.maskRow,
styles.maskFrame
]}
>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
app: {
flex:1
},
cameraView: {
flex: 1,
justifyContent: "flex-start"
},
maskOutter: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
alignItems: "center",
justifyContent: "space-around"
},
maskInner: {
width: 300,
backgroundColor: "transparent"
},
maskFrame: {
backgroundColor: "#1C355E",
opacity: 0.7
},
maskRow: {
width: "100%"
},
maskCenter: { flexDirection: "row" },
rectangleText: {
justifyContent: "center",
alignItems: "center",
width: "100%",
flex: 1,
textAlign: "center",
color: "white"
}
});
export default App;
looks like this...
And you could change color what you want.
Code on sandbox

To Show different color to view on button click in react native

enter image description hereenter image description herei'm Working on a view i have successfully shown the first color which is fixed i want another color to appear while on click of button. i have worked on dice and the total sum of two dice are showing . i want that whichever total number is coming it shows in the view as shown in image and that color changes.
this.state={
// This is our Default number value
NumberHolder : 1,
NumberHold : 1,
sum: 0,
}
}
GenerateRandomNumber = () => {
let RandomNumber = Math.floor(Math.random() * 6) + 1;
let RandomNumber1 = Math.floor(Math.random() * 6) + 1 ;
let sum = RandomNumber + RandomNumber1;
this.setState({
NumberHolder : RandomNumber,
NumberHold : RandomNumber1,
sum
});
}
getDice(diceNum) {
let diceSource = require(`./images/dice1.png`)
if (diceNum === 2) {
diceSource = require(`./images/dice2.png`)
} else if (diceNum === 3) {
diceSource = require(`./images/dice3.png`)
} else if (diceNum === 4) {
diceSource = require(`./images/dice4.png`)
} else if (diceNum === 5) {
diceSource = require(`./images/dice5.png`)
} else if (diceNum === 6) {
diceSource = require(`./images/dice6.png`)
} else {
diceSource = require(`./images/dice1.png`)
}
return (
<Image style={styles.dice}source={diceSource} />
)
}
render() {
// this is just for the demo purposes
const imageSize = this.state.NumberHolder * 50
return (
<View style={styles.MainContainer} >
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginRight:30,
}}>
<View style={{backgroundColor: '#006400'}}>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:40,
backgroundColor: '#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign:
'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
1
</Text>
</View>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:90,
borderWidth: 1,
borderColor: '#000000',
backgroundColor: '#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign:
'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
2
</Text>
</View>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:130,
borderWidth: 1,
borderColor: '#000000',
backgroundColor: '#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign:
'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
3
</Text>
</View>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:170,
borderWidth: 1,
borderColor: '#000000',
backgroundColor: '#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign: 'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
4
</Text>
</View>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:210,
borderWidth: 1,
borderColor: '#000000',
backgroundColor: '#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign:
'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
5
</Text>
</View>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:260,
backgroundColor: '#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign:
'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
6
</Text>
</View>
</View>
</View>
<View
style={{
borderBottomWidth: 2,
borderBottomColor: 'black',
width: 270,
marginLeft:18,
marginBottom:340,
}}
/>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:40,
marginTop:1
}}>
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
borderWidth: 1,
borderColor: '#000000',
marginTop:50,
backgroundColor: '#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign:
'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
7
</Text>
</View>
</View>
<Button style={{marginTop:40}} title="Roll again" onPress=
{this.GenerateRandomNumber} />
</View>
I want that whenever the total number is shown it shows in the view with different color.enter image description here
To change the color of a single view, based of a state variable, you just need to put a condition on the style prop.
For example:
<View style={{ position: 'absolute',
top: 0,
left: 20,
width: 50,
height: 50,
marginLeft:40,
backgroundColor: this.state.sum===1?"red":'#006400'}}>
<Text style={{justifyContent: 'center',flex: 1,textAlign:
'center',
marginTop:10,
color: '#ffffff',
alignItems: 'center'}}>
1
</Text>
</View>
As you have rendered them like that you have to do it on every View.
I would suggest to cycle trough a function to create a as they are all the same expect for the number.
toggleColor = () => {
this.setState(p => {
return {
flag: !p.flag
}
})
}
render() {
let color = 'red'
if (flag) { // you can change this condition to anything
color = 'green'
} else {
color = 'red'
}
return (
<View style={{backgroundColor:color}}></View>
)
}

React Native - how to catch touch events behind an absolute position component

I'm trying to have a popup dialog (created as a component with position: 'absolute') and to close it when user clicks outside of the popup.
I'm new to React Native so other things I did might be wrong, forgive me for that :)
This is a screenshot of the popup when it is open
This is some of the code:
From the Popup component:
render() {
if (!this.props.open) {
return <View />
}
return (
<View style={styles.container}>
<View style={styles.popupStyle}>
<View style={{flex:1, justifyContent: 'center', marginRight: 10}}>
<View style={styles.amountEnterStyle}>
<TextInput
keyboardType={'numeric'}
underlineColorAndroid='transparent'
autoCorrect={false}
style={styles.textInputStyle}
value={this.state.amount}
onChangeText={this._onAmountEnter.bind(this)} />
<Text style={styles.currencyTextStyle}>NIS</Text>
</View>
<View style={styles.separatorStyle} />
<View style={styles.categorySectionStyle}>
{this._renderCategoryDropdown()}
</View>
</View>
<View style={{justifyContent: 'center'}}>
<TouchableWithoutFeedback
onPress={this._onAddPaymentClicked.bind(this)} >
<View style={{width: 110, height:100, backgroundColor: "#ff0000"}} />
</TouchableWithoutFeedback>
</View>
</View>
</View>
);
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
width: undefined,
height: undefined,
justifyContent: 'center',
alignItems: 'center',
},
amountEnterStyle: {
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row'
},
textInputStyle: {
width: 100,
fontSize: 50,
fontWeight: 'bold',
height: 65,
flex: 1,
marginTop: -20,
paddingBottom: 0,
marginBottom: 0,
marginLeft:10,
textAlignVertical: 'center'
},
currencyTextStyle: {
fontSize: 50,
color: '#c0c0c0',
marginTop: -20,
textAlignVertical: 'center'
},
separatorStyle: {
height: 2,
marginTop: 0,
backgroundColor: '#c0c0c0',
},
categorySectionStyle: {
justifyContent: 'flex-start',
flexDirection: 'row',
alignItems: 'flex-start',
marginTop: 8
},
categoryColorDotStyle: {
width: 20,
height: 20,
borderRadius: 100/2,
marginLeft: 10,
marginTop: 5,
},
categoryTextStyle: {
fontSize: 24,
marginHorizontal: 4,
color: '#c0c0c0',
marginLeft: 10,
paddingRight: 10,
marginTop: -3
},
popupStyle: {
width: Dimensions.get('window').width - 60,
flexDirection: 'row',
justifyContent: 'space-between',
height: 150,
paddingRight:10,
paddingLeft:10,
borderRadius: 2,
margin: 20,
padding: 10,
opacity: 1,
borderRadius: 10,
backgroundColor: '#ffffff'
},
dropdownSeparator: {
height: 1,
backgroundColor: 'cornflowerblue'
},
And this is the hosting component:
render() {
var totalIncome = this.props.balance.totalIncome;
var totalExpanses = this.props.balance.totalExpanses;
return (
<View style={{flex:1, justifyContent: 'space-between'}}>
<Image pointerEvents='none' source={require('../../res/background.png')} style={styles.container} />
<MainScreenHeader onSettingPress={this._onBalanceSettingsPress.bind(this)}/>
<MainBalanceCounter totalCount={totalIncome} currentCount={totalIncome-totalExpanses} />
<View style={{justifyContent: 'center', alignItems: 'center', marginBottom: 150}}>
<AddPaymentButton onPress={this._onAddPaymentPress.bind(this)} />
<PaymentPopup
open={this.state.paymentPopupOpen}
onAddPaymentClicked={this._onPaymentPopupAddClicked.bind(this)} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
width: undefined,
height: undefined,
backgroundColor:'transparent',
justifyContent: 'center',
alignItems: 'center',
},
innerContainer: {
justifyContent: 'space-around',
alignItems: 'center',
flex: 1,
backgroundColor: 'transparent'
},
});
I was trying man things to have a view that will catch clicks that happens outside of the popup, but no success.
Any help will be much appreciated,
Thanks,
Giora.
have you tried out react-native-modalbox? I use it in my applications and it works pretty great. If you have touch events behind the modal, it will close. Also, you can define how large you'd like the modal to be. It doesn't have to take up the whole screen.
Otherwise, you were on the right track. You'd need a TouchableWithoutFeedback component that covers the full width and height of the screen. Your popup would be alongside of this component with a higher zIndex. Let me know if you'd like to go this route and I can provide more advice. Cheers!

Categories

Resources