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

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;

Related

Create an expanding line from a point in react native

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.

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

Border Extends out of outline

I have a simple textbox to which i have given an outline. However, If you look closely you will see that the border and the border outline dont match. The white color of the View extends out of the border outline as well. How can I fix this?
export const AddressTextBox: React.FunctionComponent<AddressTextBoxProps> = ({
placeName,
}) => {
return (
<View style={styles.textBox}>
<Text style={styles.text}>{placeName}</Text>
</View>
);
};
export const styles = StyleSheet.create({
textBox: {
backgroundColor: 'white',
height: moderateScale(50),
width: '70%',
marginTop: moderateScale(40),
alignSelf: 'center',
borderRadius: moderateScale(20),
borderColor: '#383838',
borderWidth: 0.3,
alignItems: 'center',
flexDirection: 'row',
},
text: {
fontSize: moderateScale(13),
flex: 1,
marginHorizontal: 10,
textAlign: 'center',
},
});
Edit:
The problem doesn't exist in iOS simulator but does occur in Android phones.
I try with this and without moderateScale and it works fine
<View style={{ flex: 1, backgroundColor: 'pink' }}>
<View
style={{
backgroundColor: 'white',
height: 50,
width: '70%',
marginTop: 40,
alignSelf: 'center',
borderRadius: 20,
borderBottomColor: 'red',
borderTopColor: 'red',
borderRightColor: 'red',
borderLeftColor: 'red',
borderWidth: 0.3,
alignItems: 'center',
flexDirection: 'row'
}}>
<Text
style={{
fontSize: 13,
flex: 1,
marginHorizontal: 10,
textAlign: 'center'
}}>
{'vergaminol'}
</Text>
</View>
</View>

React Native - ScrollView height changes after keyboard open/close with animation

I animate my view up 120 pixels when the keyboard opens for a Modal I have. When the keyboard closes, the view returns 120 pixels down. The problem is, the area that goes off screen at the top, when it gets animated back, the view is slightly smaller, and it gets slightly smaller each time, Here is the code - I will put a comment to show the view that is shrinking:
<Animated.View
style={{
paddingTop: 5,
paddingBottom: 10,
paddingLeft: 10,
paddingRight: 10,
marginTop: this.state.yPosition,
marginBottom: this.state.yPositionPositive,
flex: 1,
marginLeft: (Dimensions.get('window').width - 300) / 4,
backgroundColor: 'rgba(0,0,0,0.8)',
width: 300,
borderRadius: 4,
borderWidth: 0,
}}>
<View style={{ flexDirection: 'column', justifyContent: 'space-between', flex: 1 }}>
<View style={{height: 126, backgroundColor: 'blue', alignItems: 'stretch'}}>
<ScrollView contentContainerStyle={{flexGrow:1}} style={{flexDirection: 'column', backgroundColor: '#e6fffe'}}>
<View style={{height: 90, backgroundColor: 'red', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingTop: 5, paddingBottom: 5, borderBottomColor: '#6de3dc', borderBottomWidth: 0}}>
<View style={{justifyContent: 'center', alignItems: 'center', flex: 0.5}}>
<View style={{flexDirection: 'column', justifyContent: 'space-between'}}>
<Image
source={require('../assets/billythekid2.jpg')}
style={{height: 60, width: 60, marginTop: Platform.OS === 'ios' ? 10 : 10, borderColor: '#6de3dc', borderWidth: 2, borderRadius: 30}}
/>
<View style={{flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginTop: 5}}>
<Ionicons
name='ios-star'
color='#eec400'
size={14}
/>
<Ionicons
name='ios-star'
color='#eec400'
size={14}
/>
<Ionicons
name='ios-star'
color='#eec400'
size={14}
/>
<Ionicons
name='ios-star'
color='#eec400'
size={14}
/>
<Ionicons
name='ios-star'
color='#eec400'
size={14}
/>
</View>
</View>
</View>
<View style={{flexDirection: 'column', backgroundColor: '#e6fffe', marginTop: Platform.OS === 'ios' ? 10 : 10, flex: 0.5}}>
<View style={{flexDirection: 'row', flex: 0.5}}>
<View style={{flex: 0, alignSelf: 'center'}}>
<Text style={{fontSize: 16, fontWeight: '700'}}>
eamon.white
</Text>
</View>
</View>
</View>
</View>
<View style={{flex: 0, marginBottom: 5, backgroundColor: '#e6fffe'}}>
<Text
style={{
width: 280,
flex: 0,
backgroundColor: '#e6fffe',
paddingLeft: 5,
borderWidth: 0,
borderRadius: 4,
color: '#000',
fontSize: 14,
}}>
{this.state.messageFromSender}
</Text>
</View>
</ScrollView>
</View>
<View style={{flex: 1}} onLayout={(event) => {
this.setState({height: event.nativeEvent.layout.height});
}}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={{ flexDirection: 'column', flex: 1 }}>
<Text
style={{
flex: 0,
width: Dimensions.get('window').width,
color: 'white',
fontWeight: '700',
marginTop: 5,
marginBottom: 5
}}>
Date(s) Needed:
</Text>
{this.showCalendar()}
</View>
</TouchableWithoutFeedback>
<View style={{ flex: 0, marginBottom: 10, justifyContent: 'center', flexDirection: 'column', marginTop: 0 }}>
<TextInput
style={{
width: 280,
borderColor: 'gray',
borderWidth: 1,
backgroundColor: '#ffffff',
paddingLeft: 5,
borderRadius: 4,
height: this.state.height - 313
}}
onChangeText={text => this.setState({ message: text })}
value={this.state.message}
multiline={true}
numberOfLines={2}
onFocus={this.animateUp}
placeholder='Type a message...'
/>
</View>
</View>
<View style={{ flex: 0.1, borderRadius: 4, borderWidth: 0, marginBottom: 10 }}>
<TouchableOpacity
activeOpacity={1}
style={{
backgroundColor: this.state.rentButtonBackground,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
width: 280,
borderRadius: 4,
borderWidth: 0,
}}
onPress={() => {
}}>
<Text
style={{
backgroundColor: this.state.rentButtonBackground,
textAlign: 'center',
color: 'white',
fontWeight: '900',
fontSize: 18,
borderRadius: 4,
borderWidth: 0,
}}>
RESPOND
</Text>
</TouchableOpacity>
</View>
<View style={{ flex: 0.1, borderRadius: 4, borderWidth: 0, marginBottom: 10 }}>
<TouchableOpacity
activeOpacity={1}
style={{
backgroundColor: this.state.rentButtonBackground,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
width: 280,
borderRadius: 4,
borderWidth: 0,
}}
onPress={() => {
}}>
<Text
style={{
backgroundColor: this.state.rentButtonBackground,
textAlign: 'center',
color: 'white',
fontWeight: '900',
fontSize: 18,
borderRadius: 4,
borderWidth: 0,
}}>
ACCEPT
</Text>
</TouchableOpacity>
</View>
<View style={{ flex: 0.1, borderRadius: 4, borderWidth: 0 }}>
<TouchableOpacity
activeOpacity={1}
style={{
backgroundColor: this.state.rentButtonBackground,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
width: 280,
borderRadius: 4,
borderWidth: 0,
}}
onPress={() => {
}}>
<Text
style={{
backgroundColor: this.state.rentButtonBackground,
textAlign: 'center',
color: 'white',
fontWeight: '900',
fontSize: 18,
borderRadius: 4,
borderWidth: 0,
}}>
DECLINE
</Text>
</TouchableOpacity>
</View>
</View>
</Animated.View>
Animations:
animateUp = () => {
Animated.parallel([
Animated.timing(this.state.yPosition, {
toValue: -120,
duration: 300,
}),
Animated.timing(this.state.yPositionPositive, {
toValue: 120,
duration: 300,
})
]).start();
}
Animated.parallel([
Animated.timing(this.state.yPosition, {
toValue: 0,
duration: 1,
}),
Animated.timing(this.state.yPositionPositive, {
toValue: 0,
duration: 1,
})
]).start()
It starts out like this:
Each time the keyboard opens and closes, and the animation happens, pushing the top of the modal off the screen, the ScrollView shrinks a little - until it ends up looking like this:
Any suggestions would be great.
If you need to animate a view when the keyboard opens, use KeyboardAvoidingView:
https://facebook.github.io/react-native/docs/keyboardavoidingview

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