Related
I've created a piechart with the help of Recharts. Now my requirement is that when I click on one slice of the piechart, the corresponding data (name, value, etc) needs to appear in a separate component. I've given this a wild try and landed up in the below code.
The RenderClick is the component that I'm trying to render with the onClick on each pie. But didn't know how to integrate with each click. In the screen grab below you could see that the conditional rendering is working as expected triggering the component and rendering the placeholder text "Rendered from RenderClick component". But just that I don't know how to render the values in the data array within the component and render the respective data of each slice of the pie.
The demoClick is the function that is triggered on the onClick and I'm getting the expected console logs of each slice of the pie as expected. (can be seen in the screen grab).
Screen grab of the rechart used in the application
Code:
import React, { useCallback, useState } from "react";
import { PieChart, Pie, Sector, Cell } from "recharts";
const data = [
{
name: "Top Funnel",
value: 40,
substatus: {
"25-Lead": 12,
"01-UpforScreening": 14,
"04-UpforScreening": 29,
},
},
{
name: "Mid Funnel",
value: 30,
substatus: {
"25-Lead": 12,
"01-UpforScreening": 14,
},
},
{
name: "Bottom Funnel",
value: 30,
substatus: {
"25-Lead": 2,
"01-UpforScreening": 1,
},
},
{
name: "Funnel Outcome",
value: 20,
substatus: {
"25-Lead": 4,
"01-UpforScreening": 8,
},
},
{
name: "Funnel Drop",
value: 20,
substatus: {
"25-Lead": 9,
"01-UpforScreening": 2,
},
},
];
const COLORS = ["#C92A2A", "#FF8042", "#FFBB28", "#00C49F", "#0088FE"];
const renderActiveShape = (props) => {
const RADIAN = Math.PI / 180;
const {
cx,
cy,
midAngle,
innerRadius,
outerRadius,
startAngle,
endAngle,
fill,
payload,
percent,
value,
} = props;
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? "start" : "end";
return (
<g>
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
<path
d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
stroke={fill}
fill="none"
/>
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" />
<text
x={ex + (cos >= 0 ? 1 : -1) * 12}
y={ey}
textAnchor={textAnchor}
fill="#333"
>{`Count: ${value}`}</text>
<text
x={ex + (cos >= 0 ? 1 : -1) * 12}
y={ey}
dy={18}
textAnchor={textAnchor}
fill="#999"
>
{`(${(percent * 100).toFixed(2)}%)`}
</text>
;
</g>
);
};
export default function PieChartData() {
const [activeIndex, setActiveIndex] = useState(0);
let[selected,setSelected]=useState(false);
const onPieEnter = useCallback(
(_, index) => {
setActiveIndex(index);
},
[setActiveIndex]
);
function demoOnClick(e) {
setSelected(true);
console.log(e.value);
console.log(e.substatus);
}
const RenderClick = (e) =>{
return(
<div>
Rendered from <i>RenderClick</i> component
{/* Data.name, Data.valueNeed and Data.status to be rendered here when each slice of the pie is selected */}
</div>
)
}
return (
<div>
<PieChart width={1000} height={310}>
<Pie
activeIndex={activeIndex}
activeShape={renderActiveShape}
data={data}
cx={600}
cy={150}
innerRadius={60}
outerRadius={80}
fill="#BF7F3F"
dataKey="value"
onMouseEnter={onPieEnter}
onClick={demoOnClick}
>
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
</PieChart>
<div>
{selected && <RenderClick />}
</div>
</div>
);
}
I request to help me unblock from this problem. Thanks in advance.
I am doing the d3 azimuthal equal-area projection in react-native, i used this example for this. its working fine but im updating rotate values using panGestureHandler this is also working but it's not smooth and it's take time to update map.
this the repo of this.
this is the code where i update rotate values:
const countryPaths = useMemo(() => {
const clipAngle = 150;
const projection = d3
.geoAzimuthalEqualArea()
// .rotate([0, -90])
.rotate([rotateX, rotateY])
.fitSize([mapExtent, mapExtent], {
type: 'FeatureCollection',
features: COUNTRIES,
})
.clipAngle(clipAngle)
.translate([dimensions.width / 2, mapExtent / 2]);
const geoPath = d3.geoPath().projection(projection);
const windowPaths = COUNTRIES.map(geoPath);
return windowPaths;
}, [dimensions, rotateX, rotateY]);
here is my complete code
App.js
import React, {useState, useMemo, useEffect, useRef} from 'react';
import {
StyleSheet,
View,
Dimensions,
Animated,
PanResponder,
Text,
SafeAreaView,
} from 'react-native';
import Map from './components/Map';
import COLORS from './constants/Colors';
import movingAverage from './functions/movingAverage';
import * as d3 from 'd3';
import covidData_raw from './assets/data/who_data.json';
export default function App(props) {
const dimensions = Dimensions.get('window');
const [stat, setStat] = useState('avg_confirmed');
const [date, setDate] = useState('2020-04-24');
//Data Manipulation
const covidData = useMemo(() => {
const countriesAsArray = Object.keys(covidData_raw).map((key) => ({
name: key,
data: covidData_raw[key],
}));
const windowSize = 7;
const countriesWithAvg = countriesAsArray.map((country) => ({
name: country.name,
data: [...movingAverage(country.data, windowSize)],
}));
const onlyCountriesWithData = countriesWithAvg.filter(
(country) => country.data.findIndex((d, _) => d[stat] >= 10) != -1,
);
return onlyCountriesWithData;
}, []);
const maxY = useMemo(() => {
return d3.max(covidData, (country) => d3.max(country.data, (d) => d[stat]));
}, [stat]);
const colorize = useMemo(() => {
const colorScale = d3
.scaleSequentialSymlog(d3.interpolateReds)
.domain([0, maxY]);
return colorScale;
});
return (
<SafeAreaView>
<View>
<Map
dimensions={dimensions}
data={covidData}
date={date}
colorize={colorize}
stat={stat}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: COLORS.primary,
alignItems: 'center',
justifyContent: 'center',
},
rotateView: {
width: 300,
height: 300,
backgroundColor: 'black',
shadowOpacity: 0.2,
},
});
map.js
import React, {useMemo, useState, useEffect} from 'react';
import {StyleSheet, View, Animated, PanResponder} from 'react-native';
//LIBRARIES
import Svg, {G, Path, Circle} from 'react-native-svg';
import * as d3 from 'd3';
import {
PanGestureHandler,
PinchGestureHandler,
State,
} from 'react-native-gesture-handler';
//CONSTANTS
import {COUNTRIES} from '../constants/CountryShapes';
import COLORS from '../constants/Colors';
//COMPONENTS
import Button from './Button';
const Map = (props) => {
const [countryList, setCountryList] = useState([]);
const [translateX, setTranslateX] = useState(0);
const [translateY, setTranslateY] = useState(0);
const [lastTranslateX, setLastTranslateX] = useState(0);
const [lastTranslateY, setLastTranslateY] = useState(0);
const [buttonOpacity, _] = useState(new Animated.Value(0));
const [scale, setScale] = useState(1);
const [prevScale, setPrevScale] = useState(1);
const [lastScaleOffset, setLastScaleOffset] = useState(0);
const [rotateX, setrotateX] = useState();
const [rotateY, setrotateY] = useState();
const {dimensions, data, date, colorize, stat} = props;
//Gesture Handlers
const panStateHandler = (event) => {
if (event.nativeEvent.oldState === State.UNDETERMINED) {
setLastTranslateX(translateX);
setLastTranslateY(translateY);
}
if (event.nativeEvent.oldState === State.ACTIVE) {
Animated.timing(buttonOpacity, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}
};
const panGestureHandler = (event) => {
console.log('event', event.nativeEvent);
setrotateX(event.nativeEvent.x);
setrotateX(event.nativeEvent.y);
setTranslateX(-event.nativeEvent.translationX / scale + lastTranslateX);
setTranslateY(-event.nativeEvent.translationY / scale + lastTranslateY);
};
const pinchStateHandler = (event) => {
if (event.nativeEvent.oldState === State.UNDETERMINED) {
setLastScaleOffset(-1 + scale);
}
if (event.nativeEvent.oldState === State.ACTIVE) {
Animated.timing(buttonOpacity, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}
};
const pinchGestureHandler = (event) => {
if (
event.nativeEvent.scale + lastScaleOffset >= 1 &&
event.nativeEvent.scale + lastScaleOffset <= 5
) {
setPrevScale(scale);
setScale(event.nativeEvent.scale + lastScaleOffset);
setTranslateX(
translateX -
(event.nativeEvent.focalX / scale -
event.nativeEvent.focalX / prevScale),
);
setTranslateY(
translateY -
(event.nativeEvent.focalY / scale -
event.nativeEvent.focalY / prevScale),
);
}
};
//Initialize Map Transforms
const initializeMap = () => {
setTranslateX(0);
setTranslateY(0);
setScale(1);
setPrevScale(1);
setLastScaleOffset(0);
Animated.timing(buttonOpacity, {
toValue: 0,
duration: 1000,
useNativeDriver: true,
}).start();
};
//Create Map Paths
const mapExtent = useMemo(() => {
return dimensions.width > dimensions.height / 2
? dimensions.height / 2
: dimensions.width;
}, [dimensions]);
const countryPaths = useMemo(() => {
const clipAngle = 150;
const projection = d3
.geoAzimuthalEqualArea()
// .rotate([0, -90])
.rotate([rotateX, rotateY])
.fitSize([mapExtent, mapExtent], {
type: 'FeatureCollection',
features: COUNTRIES,
})
.clipAngle(clipAngle)
.translate([dimensions.width / 2, mapExtent / 2]);
const geoPath = d3.geoPath().projection(projection);
const windowPaths = COUNTRIES.map(geoPath);
return windowPaths;
}, [dimensions, rotateX, rotateY]);
useEffect(() => {
setCountryList(
countryPaths.map((path, i) => {
const curCountry = COUNTRIES[i].properties.name;
const isCountryNameInData = data.some(
(country) => country.name === curCountry,
);
const curCountryData = isCountryNameInData
? data.find((country) => country.name === curCountry)['data']
: null;
const isDataAvailable = isCountryNameInData
? curCountryData.some((data) => data.date === date)
: false;
const dateIndex = isDataAvailable
? curCountryData.findIndex((x) => x.date === date)
: null;
return (
<Path
key={COUNTRIES[i].properties.name}
d={path}
stroke={COLORS.greyLight}
strokeOpacity={0.3}
strokeWidth={0.6}
fill={
isDataAvailable
? colorize(curCountryData[dateIndex][stat])
: COLORS.greyLight
}
opacity={isDataAvailable ? 1 : 0.4}
/>
);
}),
);
}, [rotateX, rotateY]);
return (
<View>
<PanGestureHandler
onGestureEvent={(e) => panGestureHandler(e)}
onHandlerStateChange={(e) => panStateHandler(e)}>
<PinchGestureHandler
onGestureEvent={(e) => pinchGestureHandler(e)}
onHandlerStateChange={(e) => pinchStateHandler(e)}>
<Svg
width={dimensions.width}
height={dimensions.height / 2}
style={styles.svg}>
<G
// transform={`scale(${scale}) translate(${-translateX},${-translateY})`}
>
<Circle
cx={dimensions.width / 2}
cy={mapExtent / 2}
r={mapExtent / 2}
fill={COLORS.lightPrimary}
/>
{countryList.map((x) => x)}
</G>
</Svg>
</PinchGestureHandler>
</PanGestureHandler>
</View>
);
};
const styles = StyleSheet.create({
svg: {},
rotateView: {
width: 100,
height: 400,
backgroundColor: 'black',
shadowOffset: {height: 1, width: 1},
shadowOpacity: 0.2,
},
});
export default Map;
how i fixed this issue is :
I cange countries json to countries-110m.json';
delete the rotateX, rotateY and replace by translateX translateY
new rotate code is: .rotate([-translateX, translateY])
if any doubts please check my complete source code from Github
Hello everyone we are building an app for a company. My task is to create an image crop feature for profile pictures. I am using the react-easy-crop library. I have implemented nearly all the features the only missing thing is zooming in to a certain pixel. Our company doesn't want users to be able to zoom in less than 600 X 600 pixels. What I meany by that is when users zoom in the image size can't be lower than 600 x 600 pixels. here is my code structure (Be aware this not the full code I have reduced it for complexity issues)
import React, { useEffect, useRef, useState, FC, useCallback, SetStateAction } from 'react';
import Cropper from 'react-easy-crop';
import Resizer from 'react-image-file-resizer';
import { Text, Button, DragAndDrop } from '#shared/components';
import { Modal } from '#shared/components/Modal/lazy';
import { getOrientation } from 'get-orientation';
import { COLOR_NAMES } from '#src/settings/colors.constants';
import { Icon } from '#src/shared/components/icon';
import { Centered } from '#src/shared/components/layout/centered';
import { useDispatch } from 'react-redux';
import { Flex } from 'rebass';
import { Dispatch } from 'redux';
import { ZoomAndApplyContainer, CropContainer } from '#app/shared/components/crop-image/styled';
import { FileValue } from '#shared/components/upload-single-document/interfaces';
import { UploadSingleImageProps } from '#app/shared/components/upload-single-image/interfaces';
import { CoverPicEditComponent, ImageUploadContainer, PicEditComponent } from '#app/shared/components/upload-single-image/styled';
import { MODAL_NAMES, MODAL_TYPES } from '#app/shared/store/constants/modal.constants';
import { ShowModalAC, HideModalAC } from '#app/shared/store/actions/modal.actions';
import { NumberOfBytesInOneMB, TOASTER_APPEARANCES } from '#app/shared/constants';
import { SetToastsActionCreator } from '#app/shared/store/actions/toast.actions';
import { validateFileType } from '#app/utils/validations';
import { PRIMARY } from '../button/button.constants';
import { FormikFieldErrorMessage } from '../formik-field/styled';
const readFile: any = (file: any) =>
new Promise((resolve: any) => {
const reader: any = new FileReader();
reader.addEventListener('load', () => resolve(reader.result), false);
reader.readAsDataURL(file);
});
const createImage: any = (url: any) =>
new Promise((resolve: any, reject: any) => {
const image: any = new Image();
image.addEventListener('load', () => resolve(image));
image.addEventListener('error', (error: any) => reject(error));
image.setAttribute('crossOrigin', 'anonymous'); // needed to avoid cross-origin issues on CodeSandbox
image.src = url;
});
const getRadianAngle: any = (degreeValue: any) => (degreeValue * Math.PI) / 180;
const ORIENTATION_TO_ANGLE: any = {
3: 180,
6: 90,
8: -90,
};
/**
* This function was adapted from the one in the ReadMe of https://github.com/DominicTobias/react-image-crop
* #param {File} image - Image File url
* #param {Object} pixelCrop - pixelCrop Object provided by react-easy-crop
* #param {number} rotation - optional rotation parameter
*/
const getCroppedImg: any = async (imageSrc: any, pixelCrop: any, rotation: any = 0) => {
const image: any = await createImage(imageSrc);
const canvas: any = document.createElement('canvas');
const ctx: any = canvas.getContext('2d');
const maxSize: any = Math.max(image.width, image.height);
const safeArea: any = 2 * ((maxSize / 2) * Math.sqrt(2));
// set each dimensions to double largest dimension to allow for a safe area for the
// image to rotate in without being clipped by canvas context
canvas.width = safeArea;
canvas.height = safeArea;
// translate canvas context to a central location on image to allow rotating around the center.
ctx.translate(safeArea / 2, safeArea / 2);
ctx.rotate(getRadianAngle(rotation));
ctx.translate(-safeArea / 2, -safeArea / 2);
// draw rotated image and store data.
ctx.drawImage(image, safeArea / 2 - image.width * 0.5, safeArea / 2 - image.height * 0.5);
const data: any = ctx.getImageData(0, 0, safeArea, safeArea);
// set canvas width to final desired crop size - this will clear existing context
canvas.width = pixelCrop.width;
canvas.height = pixelCrop.height;
// paste generated rotate image with correct offsets for x,y crop values.
ctx.putImageData(data, Math.round(0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x), Math.round(0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y));
// As Base64 string
// return canvas.toDataURL('image/jpeg');
// As a blob
return new Promise((resolve: any) => {
canvas.toBlob((file: any) => {
resolve(file);
}, 'image/jpeg');
});
};
const getRotatedImage: any = async (imageSrc: any, rotation: number = 0) => {
const image: any = await createImage(imageSrc);
const canvas: any = document.createElement('canvas');
const ctx: any = canvas.getContext('2d');
const orientationChanged: boolean = rotation === 90 || rotation === -90 || rotation === 270 || rotation === -270;
if (orientationChanged) {
canvas.width = image.height;
canvas.height = image.width;
} else {
canvas.width = image.width;
canvas.height = image.height;
}
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate((rotation * Math.PI) / 180);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
return new Promise((resolve: any) => {
canvas.toBlob((file: any) => {
resolve(URL.createObjectURL(file));
}, 'image/jpeg');
});
};
export const UploadSingleImage: FC<UploadSingleImageProps> = ({
setFieldValue,
setFieldTouched,
name,
extensionName,
width = '600',
height = '600',
errorMessage,
isDisabled,
fileId,
extension,
title,
validationRules,
isCoverPhoto,
customContainerCss,
onChange,
editIconName = 'edit',
onUploaded,
}: UploadSingleImageProps) => {
const [value, setValue]: [FileValue, React.Dispatch<SetStateAction<FileValue>>] = useState(null);
const [imgSrc, setImgSrc]: any = useState(null);
const [maxZoom, setMaxZoom]: any = useState(1);
const [rotation, setRotation]: any = useState(0);
const [crop, setCrop]: any = useState({ x: 0, y: 0 });
const [imageSendingFail, setImageSendingFail]: [boolean, React.Dispatch<SetStateAction<boolean>>] = useState(true);
const [zoom, setZoom]: any = useState(1);
const [croppedAreaPixels, setCroppedAreaPixels]: any = useState(null);
const showCroppedImage: any = useCallback(async () => {
try {
const cropedImage: any = await getCroppedImg(imgSrc, croppedAreaPixels, rotation);
Resizer.imageFileResizer(
cropedImage,
600,
600,
'JPEG',
100,
0,
(file: any) => {
onChange(file, setValue);
dispatch(HideModalAC(MODAL_NAMES.IMAGE_CROP_MODAL));
setImgSrc(null);
},
'blob'
);
} catch (e) {
console.error(e);
}
}, [imgSrc, croppedAreaPixels, rotation]);
const imageInput: React.MutableRefObject<HTMLInputElement> = useRef();
const dispatch: Dispatch = useDispatch();
const toast: any = useCallback((toasts: any) => dispatch(SetToastsActionCreator(toasts)), [dispatch]);
const onCropComplete: any = useCallback((croppedArea: any, croppedAreaPixel: any) => {
setCroppedAreaPixels(croppedAreaPixel);
}, []);
const handleFileDrop: any = (e: any) => {
const files: any = e.dataTransfer.files;
if (files && files.length === 1) {
validateImage(files[0]);
}
};
const onFileChange: any = async (e: any) => {
if (e.target.files && e.target.files.length === 1) {
const file: any = e.target.files[0];
validateImage(file);
}
};
const onClick: any = (e: any) => {
setZoom(1);
e.target.value = '';
};
const validateImage: (file: File) => void = async (file: any) => {
setImageSendingFail(false);
// const imageDataUrl: any = await readFile(file);
// setImgSrc(imageDataUrl);
if (setFieldTouched) {
setFieldTouched(name);
}
if (validateFileType(toast, validationRules?.fileTypes, file)) {
let imageDataUrl: any = await readFile(file);
const img: any = createImage(imageDataUrl);
if (!validationRules || validateImg(toast, validationRules, img, file)) {
const orientation: any = await getOrientation(file);
const rotationPortion: any = ORIENTATION_TO_ANGLE[orientation];
if (rotation) {
imageDataUrl = await getRotatedImage(imageDataUrl, rotationPortion);
}
setImgSrc(imageDataUrl);
dispatch(ShowModalAC(MODAL_NAMES.IMAGE_CROP_MODAL));
} else {
imageInput.current.value = '';
setImageSendingFail(true);
}
}
};
useEffect(() => {
if (fileId && extension) {
setValue({ fileId, extension });
}
}, [fileId, extension]);
useEffect(() => {
if (setFieldValue) {
setFieldValue(name, value?.fileId);
setFieldValue(extensionName, value?.extension);
}
if (onUploaded && value?.fileId) {
onUploaded(name, value);
}
}, [value?.fileId, value?.extension]);
return (
<Flex justifyContent={'center'} alignItems={'center'} flexDirection={'column'} css={{ height: '100%' }} name={name}>
{imgSrc && (
<Modal bodyCss={{ padding: 0 }} bodyHeight='90%' width={'70%'} height={'85%'} borderRadius={'4px'} modalId={MODAL_NAMES.IMAGE_CROP_MODAL} headingText={'Resmi Düzenle'} type={MODAL_TYPES.NORMAL}>
<CropContainer>
<div style={{ width: '80%', height: '70%', position: 'relative' }}>
<Cropper
image={imgSrc}
crop={crop}
zoom={zoom}
rotation={rotation}
aspect={1 / 1}
onCropChange={setCrop}
onCropComplete={onCropComplete}
onZoomChange={setZoom}
restrictPosition={false}
onRotationChange={setRotation}
minZoom={0.5}
maxZoom={maxZoom}
onMediaLoaded={(imageSize: any) => {
if (imageSize.naturalWidth > 600) {
setMaxZoom(600 / Math.max(imageSize.height, imageSize.width));
} else {
setMaxZoom(1);
}
console.log(imageSize);
}}
/>
</div>
<ZoomAndApplyContainer>
<input type='range' value={zoom} min={0.5} max={maxZoom} step={0.05} onChange={(e: any) => setZoom(e.target.value)} />
<input type='range' value={rotation} min={0} max={360} step={10} onChange={(e: any) => setRotation(e.target.value)} />
<Button button={PRIMARY} onClick={showCroppedImage}>
Upload
</Button>
</ZoomAndApplyContainer>
</CropContainer>
</Modal>
)}
<DragAndDrop handleDrop={handleFileDrop}>
<ImageUploadContainer customContainerCss={customContainerCss} url={fileId && extension && `${fileId}${extension}`} width={width} height={height} errorMessage={errorMessage}>
{value?.fileId && value?.extension && isCoverPhoto && !isDisabled && (
<CoverPicEditComponent>
<label htmlFor={name}>
<Icon margin={'auto'} name={'upload-white'} />
<Text color={COLOR_NAMES.REMAX_WHITE} customWeight={1}>
Yeni kapak fotoğrafı yükle
</Text>
</label>
</CoverPicEditComponent>
)}
{value?.fileId && value?.extension && !isCoverPhoto && !isDisabled && (
<PicEditComponent className='edit-icon-container' htmlFor={name}>
<Icon name={editIconName} />
</PicEditComponent>
)}
{!(value?.fileId && value?.extension) && (
<Centered css={{ flexDirection: 'column' }}>
<Text customSize={[2, 3, 3]} lineHeight={1} customWeight={1} color={COLOR_NAMES.REMAX_TEXT_GREY_7f7f7f} css={{ textAlign: 'center', width: '145px', paddingBottom: '12px' }}>
{title}
</Text>
<label htmlFor={name}>
<Text customSize={[2, 3, 3]} customWeight={1} color={COLOR_NAMES.REMAX_BLUE_SELECTED_1451EF} textDecoration={'underline'}>
Dosya Seç
</Text>
</label>
</Centered>
)}
<input id={name} ref={imageInput} name={name} type='file' onChange={onFileChange} onClick={onClick} />
</ImageUploadContainer>
</DragAndDrop>
{/* Eğer resim yok ve upload işlemi fail olduysa hata mesajı basılsın */}
{!value?.fileId && imageSendingFail && errorMessage && <FormikFieldErrorMessage elipsis={true}>{errorMessage}</FormikFieldErrorMessage>}
</Flex>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
here is my solution for the problem: this callback inside the cropper
onMediaLoaded={(imageSize: any) => {
if (imageSize.naturalWidth > 600) {
setMaxZoom(600 / Math.max(imageSize.height, imageSize.width));
} else {
setMaxZoom(1);
}
}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
but this code doesn't ensure that I have a max zoom restriction that produces 600 x 600 in the end. (It works for some images but some others not. I would really appreciate any suggestion thanks in advance.
const onCropComplete: any = useCallback((croppedArea: any, croppedAreaPixel: any) => {
if(croppedAreaPixel.width < requiredWidth || croppedAreaPixel.height< requiredHeight){
/** do something here to disallow zoom
* this is a workaround
*/
}
setCroppedAreaPixels(croppedAreaPixel);
}, []);
Note: I want a solution for react-native.I want to make a tooltip and a draggable cursor on the area chart in which there is a line which shows the datapoint of the area chart through draggable cursor. I just want to mark the datapoint which is from the data set only not the and y value of touch point of the screen, just only x and y value on the graph when touching on a screen it gives the nearest value of the data set corresponding to the screen.
I search on the internet I found something similar but it was for the react and used a scatter graph as I am very new to victory native library and react-native can someone tell how to make a react-native equivalent of it with the area chart. Here is the link where I was referring to see..
Click me
I just want to make a similar thing with the area chart in victory native with draggable cursor and tooltip which only mark the data point on the chart only not on the screen coordinates.
Can anyone provide me with the react-native code how to implement it as similar to the link provided above as per my requirement with a victory area chart?
Also including react code which I want to convert to react-native.
Please, someone, help me out.
/* Victory requires `react#^15.5.0` and `prop-types#^15.5.0` */
const {
VictoryLine, VictoryChart, VictoryCursorContainer, VictoryLabel, VictoryScatter
} = Victory;
const { range, first, last } = _; // lodash
const mountNode = document.getElementById('app');
const allData = range(750).map((x) => ({x, y: x + 30 * Math.random()}));
const findClosestPointSorted = (data, value) => {
// assumes 3 things:
// 1. data is sorted by x
// 2. data points are equally spaced
// 3. the search is 1-dimentional (x, not x and y)
if (value === null) return null;
const start = first(data).x;
const range = (last(data).x - start);
const index = Math.round((value - start)/range * (data.length - 1));
return data[index];
};
class App extends React.Component {
constructor() {
super();
this.state = {
activePoint: null
};
}
handleCursorChange(value) {
this.setState({
activePoint: findClosestPointSorted(allData, value)
});
}
render() {
const { activePoint } = this.state;
const point = activePoint ?
<VictoryScatter data={[activePoint]} style={{data: {size: 100} }}/>
: null;
return (
<div>
<VictoryChart
containerComponent={
<VictoryCursorContainer
dimension="x"
onChange={this.handleCursorChange.bind(this)}
cursorLabel={cursor => `${activePoint.x}, ${Math.round(activePoint.y)}`}
/>
}
>
<VictoryLine data={allData} style={{data: {stroke: '#999'} }}/>
{point}
</VictoryChart>
</div>
);
}
}
ReactDOM.render(
<App/>,
mountNode
);
import React, { Component } from 'react'
import { Text, StyleSheet, View } from 'react-native'
import {VictoryArea,VictoryChart,createContainer,VictoryTooltip,VictoryScatter,VictoryLine } from 'victory-native';
import {range, first, last,maxBy } from 'lodash';
import Svg,{Line} from 'react-native-svg';
const VictoryZoomVoronoiContainer = createContainer( "cursor","voronoi");
const data = range(20,81).map((x) => ({x, y: x*x}));
const findClosestPointSorted = (data, value) => {
if (value === null) return null;
const start = first(data).x;
const range = (last(data).x - start);
const index = Math.round((value - start)/range * (data.length - 1));
return data[index];
};
export default class Chart extends Component {
componentWillMount()
{
this.setState({ymax:maxBy(data,function(o) { return o.y; }).y})
}
state = {
activePoint:null,
data:data,
ymax :0
}
handleCursorChange(value) {
this.setState({
activePoint: findClosestPointSorted(data, value)
});
}
render() {
const { activePoint } = this.state;
const point = activePoint ?
<VictoryScatter name = "scatter" data={[activePoint]} style={{data: {size: 200,fill:'#ffffff',stroke:'#1bad53',strokeWidth:2} }}/>
: null;
return (
<View>
<VictoryChart
height={300}
width={350}
containerComponent={
<VictoryZoomVoronoiContainer
voronoiDimension="x"
cursorDimension="x"
voronoiBlacklist={["scatter"]}
labelComponent={<VictoryTooltip style={{fill:'red'}} flyoutStyle={{
fill: 'rgba(52, 52, 52, 0.8)',}}/>}
onCursorChange={(value)=>{this.handleCursorChange(value)}}
labels={cursor => {
try {
return(activePoint.x?`${activePoint.x}, ${Math.round(activePoint.y)}\ndjh`:null)
} catch (error) {
console.log(error)
}
}}
/>
}
>
<VictoryArea
name = "area"
data={data}
interpolation="cardinal"
style={{
data: {
fill: '#1bad53',
stroke: '#05a543',
strokeWidth: 2
}
}}
/>
{point}
{activePoint?<Line x1= {50} x2="300" y1={250-(200/this.state.ymax)*activePoint.y} y2={250-(200/this.state.ymax)*activePoint.y} stroke="black" strokeWidth="1"/>:null}
</VictoryChart>
</View>
)
}
}
I have the following component:
class Ball extends React.Component {
constructor(props) {
super(props);
this.state = { frame: 0, position: this.positionBall(0) };
this.nextFrame();
}
nextFrame() {
this.setState(prevState => ({
frame: prevState.frame + 1,
position: this.positionBall(prevState.frame + 1)
}));
requestAnimationFrame(() => this.nextFrame());
}
render() {
return (
<svg style={{ width: "100%", height: "100%" }}>
<circle
cx={this.state.position}
cy={this.height() / 2}
r={this.radius()}
/>
</svg>
);
}
height() {
return document.documentElement.clientHeight;
}
width() {
return document.documentElement.clientWidth;
}
radius() {
return this.height() / 10;
}
positionBall(frame) {
const maximumPosition = this.width() - 2 * this.radius();
const maximumFrame = 120;
const slope = maximumPosition / maximumFrame;
const position = Math.round(
maximumPosition +
this.radius() -
1 * Math.abs(frame % (maximumFrame * 2) - maximumFrame) * slope
);
return position;
}
}
It is a very simple animation, but it doesn't run entire smooth all the time, especially on wide screens. How can I improve this code?
See this codesandbox:
https://codesandbox.io/s/22kzjy21n