I am developing something similar to this
I have many ways to acheive this using css and Scss but i couldn't find anything for react native ,if anyone has any idea how to do this.Their help will be much appreciated.Thank you.
Use cos and sin function to place the images
Working example: https://snack.expo.io/#msbot01/trusting-bagel
import React, { Component } from 'react';
import { Text, View, StyleSheet, ScrollView, FlatList, Image } from 'react-native';
import Constants from 'expo-constants';
import Highlighter from 'react-native-highlight-words';
const size = 200 ;
const symbolSize = 16;
const radius = size / 2;
const center = radius;
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
higightedTexts:''
}
}
componentDidMount(){
this.setupA();
this.setupB();
this.setupC();
this.setupD();
}
degToRad(deg) {
return deg * Math.PI / 180;
}
setupA(){
const angleRad = this.degToRad(0);
const x = radius * Math.cos(angleRad) + center - symbolSize / 2;
const y = radius * Math.sin(angleRad) + center - symbolSize / 2;
this.setState({
x: x,
y: y
})
}
setupB(){
const angleRad = this.degToRad(90);
const x = radius * Math.cos(angleRad) + center - symbolSize / 2;
const y = radius * Math.sin(angleRad) + center - symbolSize / 2;
this.setState({
x2: x,
y2: y
})
}
setupC(){
const angleRad = this.degToRad(180);
const x = radius * Math.cos(angleRad) + center - symbolSize / 2;
const y = radius * Math.sin(angleRad) + center - symbolSize / 2;
this.setState({
x3: x,
y3: y
})
}
setupD(){
const angleRad = this.degToRad(270);
const x = radius * Math.cos(angleRad) + center - symbolSize / 2;
const y = radius * Math.sin(angleRad) + center - symbolSize / 2;
this.setState({
x4: x,
y4: y
})
}
render() {
return (
<View style={{ flex: 1, justifyContent:'center', alignItems:'center' }}>
<View
style={[{backgroundColor:'red',
width: size,
height: size,
borderRadius: size / 2,
}]}
>
<Image
style={{width: 40,
height: 40,
borderRadius:20,
left: this.state.x-20,
top: this.state.y ,
position:'absolute'}}
source={{
uri:
'https://icons.iconarchive.com/icons/graphicloads/100-flat/256/home-icon.png',
}}
/>
<Image
style={{width: 40,
height: 40,
borderRadius: 20,
left: this.state.x2,
top: this.state.y2-20 ,
position:'absolute'}}
source={{
uri:
'https://icons.iconarchive.com/icons/graphicloads/100-flat/256/home-icon.png',
}}
/>
<Image
style={{width: 40,
height: 40,
borderRadius: 20,
left: this.state.x3,
top: this.state.y3 ,
position:'absolute'}}
source={{
uri:
'https://icons.iconarchive.com/icons/graphicloads/100-flat/256/home-icon.png',
}}
/>
<Image
style={{width: 40,
height: 40,
borderRadius: 20,
left: this.state.x4-10,
top: this.state.y4,
position:'absolute'}}
source={{
uri:
'https://icons.iconarchive.com/icons/graphicloads/100-flat/256/home-icon.png',
}}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({});
React native doesn't have any special library do this you can do this by using CSS:
You can do something like this:
import { Dimensions, View, Image } from 'react-native';
import React, { Component } from 'react';
class App extends Component{
render(){
return(
<View style={{flex:1,justifyContent:"center",alignItems:"center"}}>
<View
style = {{
borderRadius: Math.round(Dimensions.get('window').width + Dimensions.get('window').height) / 2,
width: Dimensions.get('window').width * 0.8,
height: Dimensions.get('window').width * 0.8,
borderWidth:5,
borderColor:"red",
justifyContent: 'center',
alignItems: 'center'
}}
underlayColor = '#ccc'
>
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",bottom:Dimensions.get('window').width * 0.7}} />
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",bottom:Dimensions.get('window').width * 0.6,right: 20}} />
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",bottom:Dimensions.get('window').width * 0.6,left: 20}} />
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",top:Dimensions.get('window').width * 0.7}} />
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",top:Dimensions.get('window').width * 0.6,right: 20}} />
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",top:Dimensions.get('window').width * 0.6,left: 20}} />
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",bottom:Dimensions.get('window').width * 0.3,left:-20}} />
<Image source={require('./assets/main.jpg')} style={{height:50,width:50,position:"absolute",bottom:Dimensions.get('window').width * 0.3,right:-20}} />
</View>
</View>
)
}
}
export default App;
Hope this helps!
Related
I have a donut-like circle (progress circle) in React component and I want to add conic gradient to it. How to do that?
I know that in SVG we can not use conic gradients. I thought it can be done by using mask and usual block with added css with gradient but not sure how to do it correctly.
Now it looks like this:
React component:
import React from 'react';
import { Box, Text } from '#chakra-ui/react';
const GradientProgress = ({ modifier, score, size, strokeWidth }) => {
const DIAMETER = 51;
const WIDTH = DIAMETER + strokeWidth;
const RADIUS = DIAMETER / 2;
const CIRC = 2 * Math.PI * RADIUS;
const foregroundCirc = (CIRC * score) / 100;
const frontCirc = (CIRC * modifier) / 100;
return (
<Box
position='relative'
style={{ width: `${size}px`, height: `${size}px` }}
sx={{
circle: {
background:
'conic-gradient(from 270deg, #ff4800 10%, #dfd902 35%, #20dc68, #0092f4, #da54d8 72% 75%, #ff4800 95%)',
},
}}
>
<svg
className='donut'
transform='rotate(-90)'
viewBox={`0 0 ${WIDTH} ${WIDTH}`}
>
<circle
className='donut-ring'
cx={RADIUS + strokeWidth / 2}
cy={RADIUS + strokeWidth / 2}
fill='transparent'
pathLength={CIRC}
r={RADIUS}
stroke='#d2d3d4'
strokeWidth={strokeWidth}
/>
<circle
className='donut-segment'
cx={RADIUS + strokeWidth / 2}
cy={RADIUS + strokeWidth / 2}
fill='transparent'
opacity={0.5}
pathLength={CIRC}
r={RADIUS}
stroke='green'
strokeDasharray={`${frontCirc} ${CIRC - frontCirc}`}
strokeDashoffset={0}
strokeLinecap='round'
strokeWidth={strokeWidth}
/>
<circle
className='donut-segment'
cx={RADIUS + strokeWidth / 2}
cy={RADIUS + strokeWidth / 2}
fill='transparent'
pathLength={CIRC}
r={RADIUS}
stroke='red'
strokeDasharray={`${foregroundCirc} ${CIRC - foregroundCirc}`}
strokeDashoffset={0}
strokeLinecap='round'
strokeWidth={strokeWidth}
/>
</svg>
<Text>{modifier || score}</Text>
</Box>
);
};
export default GradientProgress;
You can achieve this by converting your circle to a mask and assign it to a foreignObject, this object contains a div with the conic-gradient style.
Here is an example how it works:
const control = document.getElementById('control');
const circle = document.getElementsByClassName('circle')[0];
const bg = document.getElementsByClassName('bg')[0];
control.addEventListener('input', function(event) {
circle.style.setProperty('--progress', event.target.valueAsNumber);
const deg = (event.target.valueAsNumber/100) * 360;
bg.style.setProperty('background', `conic-gradient(#00bcd4, #ffeb3b ${deg}deg)`);
});
.root {
width: 400px;
height: 400px;
text-align: center;
}
svg {
}
.circle {
stroke: white;
stroke-width: 3;
stroke-linecap: round;
stroke-dasharray: calc((2 * 3.14) * 45);
stroke-dashoffset: calc((2 * 3.14 * 45) * (1 - calc(var(--progress, 50) / 100)));
transform-origin: 50% 50%;
transform: rotate(-87deg);
}
.bg {
background: conic-gradient(#00bcd4, #ffeb3b 180deg);
width: 100%;
height: 100%;
}
<div class='wrap'>
<div class='root'>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="mask">
<circle class='circle' cx="50" cy="50" r="45" stroke='white' stroke-width='3' fill='none' />
</mask>
</defs>
<foreignObject x="0" y="0" width="100" height="100" mask="url(#mask)">
<div class='bg'></div>
</foreignObject>
</svg>
<input id="control" type="range" value="60" />
</div>
</div>
I am trying to crop an image in react by using the react-image-crop library, and I got the cropping functionality working.
import React, { useCallback, useRef, useState } from "react";
import ReactCrop, { Crop } from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";
export const ImageCropper = () => {
const [upImg, setUpImg] = useState<string>(
"https://www.vintagemovieposters.co.uk/wp-content/uploads/2020/04/IMG_5274-scaled.jpeg"
);
const imgRef = useRef<HTMLImageElement | null>(null);
const [crop, setCrop] = useState<Partial<Crop>>({
unit: "%",
aspect: 0.68,
height: 100
});
const onLoad: (image: HTMLImageElement) => boolean | void = useCallback(
(img) => {
imgRef.current = img;
const aspect = 0.68;
const width =
img.width / aspect < img.height * aspect
? 100
: ((img.height * aspect) / img.width) * 100;
const height =
img.width / aspect > img.height * aspect
? 100
: (img.width / aspect / img.height) * 100;
const y = (100 - height) / 2;
const x = (100 - width) / 2;
setCrop({
unit: "%",
width,
height,
x,
y,
aspect
});
},
[]
);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<ReactCrop
src={upImg}
onImageLoaded={onLoad}
crop={crop}
onChange={(crop, percentageCrop) => {
setCrop(percentageCrop);
}}
keepSelection={true}
/>
<div
style={{
width: imgRef.current?.width! * (crop.width! / 100),
height: imgRef.current?.height! * (crop.height! / 100),
overflow: "hidden"
}}
>
<img
alt="cropped_image"
src={upImg}
style={{
width: imgRef.current?.width!,
height: imgRef.current?.height!,
transform: `translate(-${
(crop.x! / 100) * imgRef.current?.width!
}px, -${(crop.y! / 100) * imgRef.current?.height!}px )`
}}
/>
</div>
</div>
);
};
However, what I am trying to achieve is:
keep the original image after cropping
put the image in a preview div with specific dimensions (235px x 346px)
transform the image to fit within that preview div with the same defined crop
make sure the preview div matches with the highlighted crop
what I tried is the code above, but the issue with it is that the width + height change dynamically.
I tried to use fixed width and height values, but the cropping is off.
I also tried using the scale property in transform, but it was off too:
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<ReactCrop
src={upImg}
onImageLoaded={onLoad}
crop={crop}
onChange={(crop, percentageCrop) => {
console.log("percent", percentageCrop);
setCrop(percentageCrop);
}}
keepSelection={true}
/>
<div
style={{
width: 235,
height: 346,
overflow: "hidden"
}}
>
<img
alt="cropped_image"
src={upImg}
style={{
width: imgRef.current?.width!,
height: imgRef.current?.height!,
transform: `translate(-${
(crop.x! / 100) * imgRef.current?.width!
}px, -${(crop.y! / 100) * imgRef.current?.height!}px) scale(${
crop.width/100
}, ${crop.height/100})`
}}
/>
</div>
</div>
);
I need to figure out how to constrain them to (235px x 346px), and "zoom" the image to match the crop from react-image-crop.
How can I do that?
Example in code sandbox
I have a flatlist rendering a posterlist but i want it to loop infinitely so for example if posterIds are [1,2,3,4] I want it to loop 1,2,3,4,1,2,3,4,1,2,3,4,... etc
import { View, FlatList, Dimensions } from "react-native";
import moviesData from "../Fixtures/movieData.json";
import Poster from "./Poster";
const screenWidth = Dimensions.get("screen").width;
const ITEM_SIZE = screenWidth * 0.8;
const SPACING = screenWidth * 0.015;
const FULLSIZE = ITEM_SIZE + SPACING * 2;
const PosterList = ({ posterStyle, onPressPoster }) => {
return (
<View style={{ width: "100%", height: "100%" }}>
<FlatList
data={moviesData}
snapToAlignment="center"
snapToInterval={FULLSIZE}
bounces={false}
decelerationRate="fast"
renderItem={({ item }) => (
<Poster
imageUrl={item.poster}
style={[
posterStyle,
{ width: ITEM_SIZE, marginHorizontal: SPACING },
]}
onPressPoster={onPressPoster}
/>
)}
horizontal
showsHorizontalScrollIndicator={false}
/>
</View>
);
};
export default PosterList;
Current Output:
Expected Output:
Current Code Implementation:
import React from 'react'
import { LineChart, XAxis, YAxis } from 'react-native-svg-charts'
import { View, Text } from 'react-native'
import { Line, Circle, G, Rect, } from 'react-native-svg'
export default class Chart extends React.PureComponent {
handleFill = (value) => {
if (value>100){
return "rgba(190, 30, 45, 0.5)"
}
else if (value>80 && value<=100){
return "yellow"
}
else{
return "#CCE6D0"
}
}
render() {
const data = [
...
];
const values = data.map(a => a.value);
const days = data.map(a => a.day);
const axesSvg = { fontSize: 10, fill: 'grey' };
const verticalContentInset = { top: 10, bottom: 10 }
const xAxisHeight = 30
const Decorator = ({x, y, data}) => {
return values.map((value, index) => (
<View>
<View style={{marginBottom:50}}>
<Rect
x={x(index)-1.75}
y={y(value+8)}
width="4"
height="40"
fill={this.handleFill(value)}
/>
</View>
<Circle
key={index}
cx={x(index)}
cy={y(value)}
r={2}
stroke={'#639123'}
fill={'#606060'}
/>
</View>
));
};
return (
<View>
<Text style={{fontSize:10}}>BPM</Text>
<View style={{ height: 200, flexDirection: 'row' }}>
<YAxis
...
/>
<View style={{ flex: 1, marginLeft: 10 }}>
<LineChart
...
>
<Decorator />
</LineChart>
<XAxis
....
/>
</View>
</View>
</View>
)
}
}
I am using the react-native-svg-charts library to render a line chart with custom handlers.
As seen in the code, the handler consists of a circle and a rect, which simply do not show up. Could this be a library version problem? I have updated both react-native-svg and react-native-svg-charts to the latest version. What am I missing? Any ideas?
I've gotten it to work like this:
import React from 'react';
import {LineChart, XAxis, YAxis} from 'react-native-svg-charts';
import {View, Text} from 'react-native';
import {Svg, Line, Circle, G, Rect} from 'react-native-svg';
export default class CustomLineChartOne extends React.PureComponent {
handleFill = (value) => {
if (value > 100) {
return 'rgba(190, 30, 45, 0.5)';
} else if (value > 80 && value <= 100) {
return 'yellow';
} else {
return '#CCE6D0';
}
};
render() {
const xAxisLabels = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
const data = [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24];
const Decorator = ({x, y, data}) => {
return data.map((value, index) => (
<View>
<Rect
x={x(index) - 1.75}
y={y(value + 8)}
width="4"
height="40"
rx="2"
fill={this.handleFill(value)}
/>
<Circle
key={index}
cx={x(index)}
cy={y(value)}
r={2}
stroke={'#639123'}
fill={'#606060'}
/>
</View>
));
};
return (
<View
style={{
height: 200,
flexDirection: 'row',
padding: 20,
}}>
<YAxis
data={data}
contentInset={{top: 20, bottom: 20}}
svg={{
fill: 'black',
fontSize: 10,
}}
/>
<View style={{flex: 1}}>
<LineChart
style={{flex: 1}}
data={data}
svg={{stroke: 'rgb(134, 65, 244)'}}
contentInset={{top: 10, bottom: 10, left: 10, right: 10}}>
<Decorator />
</LineChart>
<XAxis
style={{marginHorizontal: -10}}
data={data}
formatLabel={(value, index) => xAxisLabels[index]}
contentInset={{left: 10, right: 10}}
svg={{fontSize: 10, fill: 'black'}}
/>
</View>
</View>
);
}
}
The important parts to look at here are how the data and xAxisLabels are defined, how the labels are formatted and how data is mapped over inside the decorator to create the bars and circles.
It's not entirely clear what the exact problem was with your code as you've left out some important parts, but I think the problem was mainly related to how you had set up your data.
The result looks like this:
I'm pretty new to react native and having some trouble with setting a new state. Everything works fine until the third textinput shows up, when i start writing inside it the counter is stuck at 1, when it should be at 2, and furthermore update the textInputList too two TextInput elements. I want to understand why the counter does not change, and how to solve this issue:)
import React from 'react';
import { useState } from 'react';
import { Button, View, StyleSheet, TextInput } from 'react-native';
import colour from '../constants/Colors';
import StartButton from '../components/Buttons/BackToBackButton';
function ShowNames(props) {
return (
<View style={styles.lineContainer}>
<TextInput
style = {{ width: '70%', height: 40, borderColor: 'white', borderWidth: 2 }}
placeholder='Legg in navn her'
placeholderTextColor='white'
selectionColor='black'
onChangeText={props.handleTextChange}
/>
</View>
)
}
export default function BackToBack(props) {
const [nameList, setList] = useState([]);
const [counter, setCounter] = useState(0);
const [textInputList, setInputList] = useState(null)
const handleTextChange = (text, id) => {
tempList = nameList
tempList[id] = text
setList(tempList)
if (id == counter) {
setCounter(counter + 1)
AddNameInputs()
}
}
function AddNameInputs()
var tempList = [];
for (let i = 0; i < counter; i++) {
console.log('i: ' + i)
tempList.push(
<View style={styles.lineContainer} key={i+2}>
<TextInput
style={{ width: '70%', height: 40, borderColor: 'white', borderWidth: 2 }}
placeholder='Legg in navn her'
placeholderTextColor='white'
selectionColor='black'
onChangeText={(text) => handleTextChange(text, i+2)}
/>
</View>
)
}
setInputList(tempList)
}
return (
<View style={styles.container}>
<ShowNames handleTextChange={(text) => handleTextChange(text, 0)} />
<ShowNames handleTextChange={(text) => handleTextChange(text, 1)} />
{textInputList}
<StartButton title={"Start!"} height={100} />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colour.lightTurquoise,
alignItems: 'center',
justifyContent: 'flex-start',
paddingTop: 20,
paddingBottom: 20
// borderWidth: 4,
// borderColor: 'yellow'
},
lineContainer: {
flexDirection: 'row',
paddingBottom: 20
}
});
I think the problem is this line tempList = nameList. It would assume you are referencing the same object when you're setting state and not trigger the relevant updates. So the quickest fix would be to just clone it with the spread operator like tempList = [...nameList] ?