React-native animation time series with percentage or alternative for it - reactjs

I'm trying to port react component to React-Native using react-native's Animated
I managed to create the circle and it's orbit and working good.
but I don't know how to write those rules in react-native's Animated
in #keyframes fulfilling-bouncing-circle-spinner-orbit-animation I need to wait till the half of the animation then I should scale it down and up twice
and in #keyframes fulfilling-bouncing-circle-spinner-circle-animation I don't know how to add and remove styles using react native animation, kept thinking about it but I didn't come up with an idea.
So is there's a way to add time series and do what I want in those frames the same way
#keyframes works.
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const BouncingCircle = styled.div`
height: ${props => props.size}px;
width: ${props => props.size}px;
position: relative;
animation: fulfilling-bouncing-circle-spinner-animation infinite
${props => props.animationDuration}ms ease;
* {
box-sizing: border-box;
}
.orbit {
height: ${props => props.size}px;
width: ${props => props.size}px;
position: absolute;
top: 0;
left: 0;
border-radius: 50%;
border: calc(${props => props.size}px * 0.03) solid ${props => props.color};
animation: fulfilling-bouncing-circle-spinner-orbit-animation infinite
${props => props.animationDuration}ms ease;
}
.circle {
height: ${props => props.size}px;
width: ${props => props.size}px;
color: ${props => props.color};
display: block;
border-radius: 50%;
position: relative;
border: calc(${props => props.size}px * 0.1) solid ${props => props.color};
animation: fulfilling-bouncing-circle-spinner-circle-animation infinite
${props => props.animationDuration}ms ease;
transform: rotate(0deg) scale(1);
}
#keyframes fulfilling-bouncing-circle-spinner-animation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#keyframes fulfilling-bouncing-circle-spinner-orbit-animation {
0% {
transform: scale(1);
}
50% {
transform: scale(1);
}
62.5% {
transform: scale(0.8);
}
75% {
transform: scale(1);
}
87.5% {
transform: scale(0.8);
}
100% {
transform: scale(1);
}
}
#keyframes fulfilling-bouncing-circle-spinner-circle-animation {
0% {
transform: scale(1);
border-color: transparent;
border-top-color: inherit;
}
16.7% {
border-color: transparent;
border-top-color: initial;
border-right-color: initial;
}
33.4% {
border-color: transparent;
border-top-color: inherit;
border-right-color: inherit;
border-bottom-color: inherit;
}
50% {
border-color: inherit;
transform: scale(1);
}
62.5% {
border-color: inherit;
transform: scale(1.4);
}
75% {
border-color: inherit;
transform: scale(1);
opacity: 1;
}
87.5% {
border-color: inherit;
transform: scale(1.4);
}
100% {
border-color: transparent;
border-top-color: inherit;
transform: scale(1);
}
}
`;
const propTypes = {
size: PropTypes.number,
animationDuration: PropTypes.number,
color: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
};
const defaultProps = {
size: 60,
color: '#fff',
animationDuration: 4000,
className: '',
};
const FulfillingBouncingCircleSpinner = ({
size,
color,
animationDuration,
className,
style,
...props
}) => (
<BouncingCircle
size={size}
color={color}
animationDuration={animationDuration}
className={`fulfilling-bouncing-circle-spinner${
className ? ' ' + className : ''
}`}
style={style}
{...props}
>
<div className="circle" />
<div className="orbit" />
</BouncingCircle>
);
FulfillingBouncingCircleSpinner.propTypes = propTypes;
FulfillingBouncingCircleSpinner.defaultProps = defaultProps;
export default FulfillingBouncingCircleSpinner;
/** #flow **/
import React, { useEffect, useState } from 'react';
import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet';
import { Animated, Easing, StyleSheet } from 'react-native';
import { AnimationUtils } from '../animationUtils';
type EpicProps = {
size?: number,
duration?: number,
color?: string,
style?: ViewStyleProp
};
const EpicDefaultProps = {
size: 200,
color: 'red',
duration: 1000
};
export const FulfillingBouncingCircleSpinner = ({ size, color, duration, style, ...props }: EpicProps) => {
const [animate] = useState(new Animated.Value(0));
const spinnerStyle = StyleSheet.create({
container: {
height: size,
width: size,
position: 'relative'
},
circle: {
height: size,
width: size,
borderColor: color,
borderRadius: size * 0.5,
position: 'relative',
borderWidth: size * 0.1
},
orbit: {
height: size,
width: size,
position: 'absolute',
top: 0,
left: 0,
borderColor: color,
borderRadius: size * 0.5,
borderWidth: size * 0.03
}
});
const containerRotation = AnimationUtils.interpolation(animate, [0, 1], ['0deg', '360deg']);
const circle = AnimationUtils.interpolation(animate, [0, 1], [1, 1.4]);
const orbit = AnimationUtils.interpolation(animate, [0, 1], [1, 0.8]);
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(animate, { toValue: 1, duration: duration, useNativeDriver: true, easing: Easing.back() }),
Animated.timing(animate, { toValue: 0, duration: duration, useNativeDriver: true, easing: Easing.back() })
])
).start();
}, [animate, duration]);
return (
<Animated.View style={[style, spinnerStyle.container, { transform: [{ rotate: containerRotation }] }]} {...props}>
<Animated.View style={[spinnerStyle.circle, { transform: [{ scaleX: circle }, { scaleY: circle }] }]} />
<Animated.View style={[spinnerStyle.orbit, { transform: [{ scaleX: orbit }, { scaleY: orbit }] }]} />
</Animated.View>
);
};
FulfillingBouncingCircleSpinner.defaultProps = EpicDefaultProps;

I have spend it sometime on React animation docs and I have came up with a solution if anyone interested or can enhance it I would much appreciate it.
/** #flow **/
import React, { useEffect, useState } from 'react';
import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet';
import { Animated, Easing, StyleSheet } from 'react-native';
type EpicProps = {
size?: number,
duration?: number,
color?: string,
style?: ViewStyleProp
};
const EpicDefaultProps = {
size: 200,
color: 'red',
duration: 1000
};
export const FulfillingBouncingCircleSpinner = ({ size, color, duration, style, ...props }: EpicProps) => {
const [animate] = useState(new Animated.Value(0));
const spinnerStyle = StyleSheet.create({
container: {
height: size,
width: size,
position: 'relative'
},
circle: {
height: size,
width: size,
borderColor: color,
borderRadius: size * 0.5,
position: 'relative',
borderWidth: size * 0.1
},
orbit: {
height: size,
width: size,
borderColor: color,
position: 'absolute',
top: 0,
left: 0,
borderRadius: size * 0.5,
borderWidth: size * 0.03
}
});
const animateStyle = {
container: {
transform: [
{
rotate: animate.interpolate({
inputRange: [0, 9, 10],
outputRange: ['0deg', '360deg', '360deg']
})
}
]
},
orbit: {
transform: [
{
scale: animate.interpolate({
inputRange: [0, 6, 7, 8, 9, 10],
outputRange: [1, 1, 0.8, 1, 0.8, 1]
})
}
]
},
circle: {
transform: [
{
scale: animate.interpolate({
inputRange: [0, 6, 7, 8, 9, 10],
outputRange: [1, 1, 1.4, 1, 1.4, 1]
})
}
],
borderColor: animate.interpolate({
inputRange: [0, 4, 5, 9, 10],
outputRange: ['transparent', 'transparent', color, color, 'transparent']
}),
borderTopColor: animate.interpolate({
inputRange: [0, 10],
outputRange: [color, color]
}),
borderRightColor: animate.interpolate({
inputRange: [0, 1, 2, 9, 10],
outputRange: ['transparent', 'transparent', color, color, 'transparent']
}),
borderBottomColor: animate.interpolate({
inputRange: [0, 2, 3, 9, 10],
outputRange: ['transparent', 'transparent', color, color, 'transparent']
})
}
};
useEffect(() => {
Animated.loop(
Animated.timing(animate, {
toValue: 10,
duration: duration * 4,
easing: Easing.inOut(Easing.ease)
})
).start();
}, [animate, duration]);
return (
<Animated.View style={[style, spinnerStyle.container, animateStyle.container]} {...props}>
<Animated.View style={[spinnerStyle.circle, animateStyle.circle]} />
<Animated.View style={[spinnerStyle.orbit, animateStyle.orbit]} />
</Animated.View>
);
};
FulfillingBouncingCircleSpinner.defaultProps = EpicDefaultProps;

Related

custom node of #ant-design/flowchart

import { Flowchart } from '#ant-design/flowchart';
import "#ant-design/flowchart/dist/index.css";
const IndicatorNode = (props:any) => {
const { size = { width: 120, height: 50 }, data } = props;
const { width, height } = size;
const { label = 'john', stroke = '#ccc', fill = '#fff', fontFill, fontSize } = data;
return (
<div
className="indicator-container"
style={{
position: 'relative',
display: 'block',
background: '#fff',
border: '1px solid #84b2e8',
borderRadius: '2px',
padding: '10px 12px',
overflow: 'hidden',
boxShadow: '0 1px 4px 0 rgba(0,0,0,0.20)',
width,
height,
borderColor: stroke,
backgroundColor: fill,
color: fontFill,
fontSize,
}}
>
<div style={{ color: fontFill }}>{label}</div>
</div>
);
};
export designer = () => {
return (
<div style={{ height: 600 }}>
<Flowchart
onSave={(d) => {
console.log(d);
}}
toolbarPanelProps={{
position: {
top: 0,
left: 0,
right: 0,
},
}}
scaleToolbarPanelProps={{
layout: 'horizontal',
position: {
right: 0,
top: -40,
},
style: {
width: 150,
height: 39,
left: 'auto',
background: 'transparent',
},
}}
canvasProps={{
position: {
top: 40,
left: 0,
right: 0,
bottom: 0,
},
}}
nodePanelProps={{
position: { width: 160, top: 40, bottom: 0, left: 0 },
defaultActiveKey: ['custom'], // ['custom', 'official']
registerNode: {
title: 'abc',
key:"abc",
nodes: [
{
component: IndicatorNode,
popover: () => <div>abcs</div>,
name: 'custom-node-indicator',
width: 120,
height: 50,
label: 'abc',
},
],
},
}}
detailPanelProps={{
position: { width: 200, top: 40, bottom: 0, right: 0 },
}}
/>
</div>
);
}
I want to create new custom code with api configurations and once I click on that node it shows the editable custom properties on right panel.
in custom node of #ant-design/flowchart can I add my custom editable properties? that are showing on right panel once I clink on node.. Can someone provide me any reference for that?

Page transition animation in framer motion (React)

As title says i wanna do a page transition like https://allhero.co.jp with framer motion in React Js
i tried:
.anim-page{
&,._red{
position: fixed;
top: 0;
width: 100%;
height: 100%;
z-index:999;
}
&._black{
background-color: black;
}
._red{
background-color: red;
}
}
//-------------------------------------
const page_anim = {
red: {
hidden: { scaleX: 0 },
visible: {
scaleX: [1, 0],
transformOrigin: 'right',
transition: { duration: 0.5, {delay:0.3} },
},
},
black: {
hidden: { right: '100%' },
visible: {
// skewX: ['-25deg', '0deg'],
right: '0%',
transition: { duration: 0.7 },
},
},
}
// -----------------------------------
<motion.div
className='anim-page _black'
variants={page_anim.black}
initial='hidden'
animate='visible'
>
<motion.div className='_red' variants={page_anim.red}></motion.div>
</motion.div>
it kinda looks like that, but i don't know if this is the proper right way of doing it and it doesn't look that much. Importantly i wanna make it reusable.
i liked the feature that the black goes away when page is loaded
I think this is a gsap thing, but i did, not that great but i like it
AnimLoading.jsx
import { motion } from 'framer-motion'
import React from 'react'
const AnimLoading = () => {
const page_anim = {
red: {
hidden: { scaleX: 0 },
visible: {
scaleX: [1, 0],
transformOrigin: 'right',
transition: { duration: 0.4, delay: 0.2 },
},
},
black: {
hidden: { right: '100%' },
visible: {
skewX: ['0deg', '-5deg', '0deg'],
right: ['100%', '0%'],
transition: { duration: 0.5 },
},
done: {
right: '-100%',
skewX: ['0deg', '-5deg', '0deg'],
transition: { duration: 0.5 },
},
},
text: {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { delay: 0.4 } },
done: { opacity: 0 },
},
}
return (
<motion.div
className='anim-page _black'
variants={page_anim.black}
initial='hidden'
animate='visible'
exit='done'
>
<motion.div className='_red' variants={page_anim.red}></motion.div>
<motion.h1 variants={page_anim.text} className='text-norm light'>
LOADING...
</motion.h1>
</motion.div>
)
}
export default AnimLoading
bit of scss
.anim-page{
&,._red{
position: fixed;
top: 0;
width: 100%;
height: 100%;
z-index:999;
}
&._black{
background-color: ${({ theme }) => theme.bg_light};
box-shadow: 0 2px 7px ${({ theme }) => theme.shadow_1};
}
._red{
width: 70%;
right: 0;
background-color: ${({ theme }) => theme.bg_blue};
}
h1{
line-height: 100vh;
font-size:7vw;
display: grid;
place-items: center;
}
}
App.js
const route = location.pathname.split('/')[1]
const [animate, setAnimate] = useState(false)
useEffect(() => {
setAnimate(true)
setTimeout(() => {
setAnimate(false)
}, 1000)
}, [route])
<AnimatePresence>{animate && <AnimLoading />}</AnimatePresence>
I'm using React Router, this happens all the time route changes...

Using keyframes in JSS

I'm trying to apply an animation to an arrow in a react component using JSS. I can't seem to get the keyframes correct and getting the error TypeError: container.addRule(...).addRule is not a function
const useStyles = createUseStyles({
'#keyframes sdb05': {
from: {
transform: 'rotate(-45deg) translate(0, 0)',
opacity: 0
},
to: {
transform: 'rotate(-45deg) translate(-20px, 20px)',
opacity: 1
}
},
arrow:{
animationName: '$sdb05',
zIndex: 2,
display: 'inline-block',
'-webkit-transform': 'translate(0, -50%)',
transform: 'translate(0, -50%)',
color: '#fff',
letterSpacing: '.1em',
textDecoration: 'none',
transition: 'opacity .3s',
width: 50,
height: 50,
backgroundColor: 'rgb(255,255,255, 0.5)',
borderRadius: '50%',
'&:after': {
position: 'absolute',
bottom: 0,
left: 0,
content: '',
width: '100%',
height: '80%',
background: '-webkit-linear-gradient(top,rgba(0,0,0,0) 0,rgba(0,0,0,.8) 80%,rgba(0,0,0,.8) 100%)'
},
'& a': {
'& span':{
position: 'absolute',
top: 8,
left: '50%',
width: 24,
height: 24,
marginLeft: -12,
borderLeft: '2px solid #000',
borderBottom: '2px solid #000',
'-webkit-transform': 'rotate(-45deg)',
transform: 'rotate(-45deg)',
'-webkit-animation': 'sdb05 1.5s infinite',
'animation': 'sdb05 1.5s infinite',
boxSizing: 'border-box'
},
'&:hover': {
opacity: .5
}
}
}
});
const Arrow = () => {
const classes = useStyles();
return (
<div className={classes.arrow}>
<span></span>
</div>
);
};
How can I correctly set my keyframes to enable the animation?
The animation should function like this: link
If you are using JSS v10 or higher, the code you posted now is correct, you can see what is being generated in the browser and here https://cssinjs.org/repl
Now you have some problem with the actual animation, not with JSS.
Add the #keyframes on the top level, not inside of the rule

Import ol-ext to a React - Open Layers 6 project

I'm trying to set up a React project where I want to use some of the tools provided by the ol-ext. I looked around and found some Codesandbox projects from the creator of the library but I was unable to make them work properly because of the way the library is imported in the projects causes an error.
Is there a problem with the syntax or incompatibility with ol-ext and the other Open Layers versions?
For anyone coming to the same problem I managed to make it work and I implemented the Interaction Transform Features example. Here is the code below:
JS:
// Import stylesheets
import './style.css';
import "ol/ol.css";
import "ol-ext/dist/ol-ext.css";
import Transform from "ol-ext/interaction/Transform";
import Stamen from 'ol/source/Stamen';
import { Map, View } from "ol";
import { defaults } from "ol/control";
import * as olEvents from 'ol/events';
import TileLayer from 'ol/layer/Tile';
import { Style, Fill, Text, Icon, Stroke, RegularShape } from "ol/style";
import {Polygon, LineString, Point, Circle} from 'ol/geom';
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import Feature from "ol/Feature";
var interaction = new Transform ({
enableRotatedTransform: false,
/* Limit interaction inside bbox * /
condition: function(e, features) {
return ol.extent.containsXY([-465960, 5536486, 1001630, 6514880], e.coordinate[0], e.coordinate[1]);
},
/* */
addCondition: olEvents.condition,
// filter: function(f,l) { return f.getGeometry().getType()==='Polygon'; },
// layers: [vector],
hitTolerance: 2,
translateFeature: false,
scale: true,
rotate: true,
translate: true,
stretch: true
});
var circle = new RegularShape({
fill: new Fill({color:[255,255,255,0.01]}),
stroke: new Stroke({width:1, color:[0,0,0,0.01]}),
radius: 8,
points: 10
});
interaction.setStyle ('rotate',
new Style({
text: new Text ({
text:'\uf0e2',
font:"16px Fontawesome",
textAlign: "left",
fill:new Fill({color:'red'})
}),
image: circle
}));
// Center of rotation
interaction.setStyle ('rotate0',
new Style({
text: new Text ({
text:'\uf0e2',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'red' })
}),
}));
// Style the move handle
interaction.setStyle('translate',
new Style({
text: new Text ({
text:'\uf047',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'red' })
})
}));
// Layers
var layers = [
new TileLayer({
title:'terrain-background',
source: new Stamen({ layer: 'terrain' })
})
]
// The map
var map = new Map({
target: null,
view: new View({
zoom: 5,
center: [261720, 5951081]
}),
controls: defaults({ "attribution": false }),
layers: layers
});
// Style
function getStyle(feature) {
return [ new Style({
image: new RegularShape({
fill: new Fill({ color: [0,0,255,0.4]}),
stroke: new Stroke({color: [0,0,255,1],width: 1}),
radius: 10,
points: 3,
angle: feature.get('angle')||0
}),
fill: new Fill({color: [0,0,255,0.4]}),
stroke: new Stroke({color: [0,0,255,1],width: 1})
})];
}
// New vector layer
var vector = new VectorLayer({
name: 'Vecteur',
source: new VectorSource({ wrapX: false }),
style: getStyle
})
map.addLayer(vector);
vector.getSource().addFeature(new Feature(new Polygon([[[34243, 6305749], [-288626, 5757848], [210354, 5576845], [300000, 6000000], [34243, 6305749]]])));
vector.getSource().addFeature(new Feature(new LineString([[406033, 5664901], [689767, 5718712], [699551, 6149206], [425601, 6183449]])));
vector.getSource().addFeature(new Feature(new Point( [269914, 6248592])));
vector.getSource().addFeature(new Feature(new Circle( [500000, 6400000], 100000 )));
// Set cursor style
Transform.prototype.Cursors['rotate'] = 'url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAXAgMAAACdRDwzAAAAAXNSR0IArs4c6QAAAAlQTFRF////////AAAAjvTD7AAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH2wwSEgUFmXJDjQAAAEZJREFUCNdjYMAOuCCk6goQpbp0GpRSAFKcqdNmQKgIILUoNAxIMUWFhoKosNDQBKDgVAilCqcaQBogFFNoGNjsqSgUTgAAM3ES8k912EAAAAAASUVORK5CYII=\') 5 5, auto';
Transform.prototype.Cursors['rotate0'] = 'url(\'data:image/png;base64,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\') 5 5, auto';
map.addInteraction(interaction);
class App extends Component {
state = {
name: 'React',
styles: false,
scale: true,
stretch: true,
disableStretch: false,
rotate: true,
translate: true,
translateFeature: false,
forceMatching: false,
info: "Hello there"
};
setHandleStyleInput = (event) => {
this.setState({styles: event.target.checked})
if (!interaction instanceof Transform) return;
if (event.target.checked) {
// Style the rotate handle
var circle = new RegularShape({
fill: new Fill({color:[255,255,255,0.01]}),
stroke: new Stroke({width:1, color:[0,0,0,0.01]}),
radius: 8,
points: 10
});
interaction.setStyle ('rotate',
new Style({
text: new Text ({
text:'\uf0e2',
font:"16px Fontawesome",
textAlign: "left",
fill:new Fill({color:'yellow'})
}),
image: circle
}));
// Center of rotation
interaction.setStyle ('rotate0',
new Style({
text: new Text ({
text:'\uf0e2',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
}),
}));
// Style the move handle
interaction.setStyle('translate',
new Style({
text: new Text ({
text:'\uf047',
font:"20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
})
}));
interaction.setStyle ('scaleh1',
new Style({
text: new Text ({
text:'\uf07d',
font:"bold 20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
})
}));
interaction.style.scaleh3 = interaction.style.scaleh1;
interaction.setStyle('scalev',
new Style({
text: new Text ({
text:'\uf07e',
font:"bold 20px Fontawesome",
fill: new Fill({ color:[255,255,255,0.8] }),
stroke: new Stroke({ width:2, color:'yellow' })
})
}));
interaction.style.scalev2 = interaction.style.scalev;
} else {
interaction.setDefaultStyle();
}
// Refresh
interaction.set('translate', interaction.get('translate'));
}
setPropertieScale = (p) =>{
this.setState({scale: p.target.checked})
if (p.target.checked) this.setState({ disableStretch: false});
else this.setState({ disableStretch: true});
}
setPropertieStretch = (p) =>{
this.setState({stretch: p.target.checked})
}
setPropertieRotate = (p) =>{
this.setState({rotate: p.target.checked})
}
setPropertieTranslate = (p) =>{
this.setState({translate: p.target.checked})
}
setPropertieTranslateFeature = (p) =>{
this.setState({translateFeature: p.target.checked})
}
componentDidMount() {
map.setTarget("map");
}
componentDidUpdate(prevState) {
/** Style the transform handles for the current interaction
*/
if(this.state.scale !== prevState.scale || this.state.stretch !== prevState.stretch || this.state.rotate !== prevState.rotate || this.state.translate !== prevState.translate || this.state.styles !== prevState.styles){
var interaction = new Transform ({
enableRotatedTransform: false,
/* Limit interaction inside bbox * /
condition: function(e, features) {
return ol.extent.containsXY([-465960, 5536486, 1001630, 6514880], e.coordinate[0], e.coordinate[1]);
},
/* */
addCondition: olEvents.condition,
// filter: function(f,l) { return f.getGeometry().getType()==='Polygon'; },
// layers: [vector],
hitTolerance: 2,
translateFeature: this.state.translateFeature,
scale: this.state.scale,
rotate: this.state.rotate,
translate: this.state.translate,
stretch: this.state.stretch
});
interaction.set('translate', interaction.get('translate'));
// Style handles
}
}
render() {
var info = "Heyyou"
// Events handlers
var startangle = 0;
var d=[0,0];
// Handle rotate on first point
var firstPoint = false;
interaction.on (['select'], function(e) {
if (firstPoint && e.features && e.features.getLength()) {
interaction.setCenter(e.features.getArray()[0].getGeometry().getFirstCoordinate());
}
});
interaction.on (['rotatestart','translatestart'], function(e){
// Rotation
startangle = e.feature.get('angle')||0;
// Translation
d=[0,0];
});
interaction.on('rotating', (e)=>{
info= "rotate: "+((e.angle*180/Math.PI -180)%360+180).toFixed(2)
// Set angle attribute to be used on style !
e.feature.set('angle', startangle - e.angle);
});
interaction.on('translating', (e) =>{
d[0]+=e.delta[0];
d[1]+=e.delta[1];
info= "translate: "+d[0].toFixed(2)+","+d[1].toFixed(2)
if (firstPoint) {
interaction.setCenter(e.features.getArray()[0].getGeometry().getFirstCoordinate());
}
});
interaction.on('scaling', (e) => {
info= "scale: "+e.scale[0].toFixed(2)+","+e.scale[1].toFixed(2)
if (firstPoint) {
interaction.setCenter(e.features.getArray()[0].getGeometry().getFirstCoordinate());
}
});
interaction.on(['rotateend', 'translateend', 'scaleend'], (e) =>{
this.setState({info: info})
info= ""
});
return (
<div>
<div id="map" style={{width:"500px", height:"400px"}}></div>
<div className="options" >
<h2>Options:</h2>
<ul><li>
<input
className="style"
type="checkbox"
checked={this.state.styles}
onChange={this.setHandleStyleInput} />
<label> styles transform handles (using fontawesome)</label>
</li><li>
<input
className="scale"
type="checkbox"
checked={this.state.scale}
onChange={this.setPropertieScale} /><label> enable scale</label>
</li><li>
<input
className="stretch"
type="checkbox"
checked={this.state.stretch}
onChange={this.setPropertieStretch} disabled={this.state.disableStretch} />
<label> enable stretch</label>
</li><li>
<input
className="rotate"
type="checkbox"
checked={this.state.rotate}
onChange={this.setPropertieRotate} />
<label> enable rotate</label>
</li><li>
<input
className="translate"
type="checkbox"
checked={this.state.translate}
onChange={this.setPropertieTranslate} />
<label> enable translate</label>
</li><li>
<input
className="translateFeature"
type="checkbox"
checked={this.state.translateFeature}
onChange={this.setPropertieTranslateFeature} />
<label> translate when click on feature</label>
</li><li>
SetRotateCenter:
<button onClick={()=>{firstPoint=false; interaction.setCenter()}}>objects</button>
<button onClick={()=>{firstPoint=false; interaction.setCenter(map.getView().getCenter())}}>view center</button>
<button onClick={()=>{firstPoint=true;}}>first point</button>
</li><li>
<hr/>
Use <i>Shift</i> to add object to tranform
<hr/>
Use <i>Shift</i> key to preserve proportions when scaling (see keepAspectRatio).
<br />
Use <i>Ctrl</i> key to modify the center when scaling.
</li></ul>
<div style={{background:"white", padding:"0 0.45em"}}><span id="info"></span>{this.state.info}</div>
</div>
</div>
);
}
}
HTML:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<div id="root"></div>
CSS:
h1, p {
font-family: Lato;
}
a.icss-github-corner,
a.icss-github-corner-left {
font-size: 2.5em;
position: fixed;
top:0;
right: 0;
z-index: 1000;
color: #fff;
background-color: #333;
padding: .5em 2em 0;
text-align: center;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-transform-origin: 3.9em 3em;
-ms-transform-origin: 3.9em 3em;
transform-origin: 3.9em 3em;
overflow: hidden;
}
a.icss-github-corner-left {
left: 0;
right: auto;
-webkit-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
transform: rotate(-45deg);
-webkit-transform-origin: 1em 3.1em;
-ms-transform-origin: 1em 3.1em;
transform-origin: 1em 3.1em;
}
a.icss-github-corner:hover i,
a.icss-github-corner-left:hover i{
-webkit-animation: vertical 2s ease;
animation: vertical 2s ease;
}
a.icss-github-corner i,
a.icss-github-corner-left i,
i.icss-github-corner {
color: #fff;
position: relative;
display:inline-block;
font-style: normal;
background-color:currentColor;
-webkit-box-sizing: border-box;
box-sizing: border-box;
vertical-align: middle;
width: .8em;
height: .6em;
-webkit-border-radius: 45% 45% 45% 45% / 50%;
border-radius: 45% 45% 45% 45% / 50%;
background-color: currentColor;
-webkit-box-shadow: 0 .35em 0 -.2em,
0 .38em 0 -.2em,
0 .41em 0 -.2em,
0 .44em 0 -.2em,
0 .47em 0 -.2em;
box-shadow: 0 .35em 0 -.2em,
0 .38em 0 -.2em,
0 .41em 0 -.2em,
0 .44em 0 -.2em,
0 .47em 0 -.2em;
margin: .12em .1em .23em;
}
a.icss-github-corner i:before,
a.icss-github-corner-left i:before,
i.icss-github-corner:before {
content: "";
border-width: 0;
position: absolute;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border-width: .15em .15em;
border-style: solid;
-webkit-border-radius: 0.02em 60% 100% 80%;
border-radius: 0.02em 60% 100% 80%;
left: 0;
top: -.07em;
-webkit-transform: rotate(20deg);
-ms-transform: rotate(20deg);
transform: rotate(20deg);
}
a.icss-github-corner i:after,
a.icss-github-corner-left i:after,
i.icss-github-corner:after {
content: "";
border-width: 0;
position: absolute;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border-width: .15em .15em;
border-style: solid;
-webkit-border-radius: 0.02em 80% 100% 60%;
border-radius: 0.02em 80% 100% 60%;
left: .5em;
top: -.07em;
-webkit-transform: rotate(65deg);
-ms-transform: rotate(65deg);
transform: rotate(65deg);
}
#-webkit-keyframes vertical {
0%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
4%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
8%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
12%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
16%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
20%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
22%,100%{-webkit-transform:translate(0,0);transform:translate(0,0)}
}
#keyframes vertical {
0%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
4%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
8%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
12%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
16%{-webkit-transform:translate(0,-3px);transform:translate(0,-3px)}
20%{-webkit-transform:translate(0,3px);transform:translate(0,3px)}
22%,100%{-webkit-transform:translate(0,0);transform:translate(0,0)}
}
/**/
body {
font-family: 'Lucida Grande',Verdana,Geneva,Lucida,Arial,Helvetica,sans-serif;
font-size: 16px;
}
a, i, b {
color: #337ab7;
text-decoration: none;
}
button i {
color: #fff;
}
.ol-control.ol-bar .ol-control button i {
color: #fff;
}
a:hover {
text-decoration: underline;
}
a.title {
text-decoration: none;
}
h1 {
background: #1f6b75 url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACE1BMVEX///8A//8AgICA//8AVVVAQID///8rVVVJtttgv98nTmJ2xNgkW1ttyNsmWWZmzNZYxM4gWGgeU2JmzNNr0N1Rwc0eU2VXxdEhV2JqytQeVmMhVmNoydUfVGUgVGQfVGQfVmVqy9hqy9dWw9AfVWRpydVry9YhVmMgVGNUw9BrytchVWRexdGw294gVWQgVmUhVWPd4N6HoaZsy9cfVmQgVGRrytZsy9cgVWQgVWMgVWRsy9YfVWNsy9YgVWVty9YgVWVry9UgVWRsy9Zsy9UfVWRsy9YgVWVty9YgVWRty9Vsy9aM09sgVWRTws/AzM0gVWRtzNYgVWRuy9Zsy9cgVWRGcHxty9bb5ORbxdEgVWRty9bn6OZTws9mydRfxtLX3Nva5eRix9NFcXxOd4JPeINQeIMiVmVUws9Vws9Vw9BXw9BYxNBaxNBbxNBcxdJexdElWWgmWmhjyNRlx9IqXGtoipNpytVqytVryNNrytZsjZUuX210k5t1y9R2zNR3y9V4lp57zth9zdaAnKOGoaeK0NiNpquV09mesrag1tuitbmj1tuj19uktrqr2d2svcCu2d2xwMO63N+7x8nA3uDC3uDFz9DK4eHL4eLN4eIyYnDX5OM5Z3Tb397e4uDf4uHf5uXi5ePi5+Xj5+Xk5+Xm5+Xm6OY6aHXQ19fT4+NfhI1Ww89gx9Nhx9Nsy9ZWw9Dpj2abAAAAWnRSTlMAAQICAwQEBgcIDQ0ODhQZGiAiIyYpKywvNTs+QklPUlNUWWJjaGt0dnd+hIWFh4mNjZCSm6CpsbW2t7nDzNDT1dje5efr7PHy9PT29/j4+Pn5+vr8/f39/f6DPtKwAAABTklEQVR4Xr3QVWPbMBSAUTVFZmZmhhSXMjNvkhwqMzMzMzPDeD+xASvObKePPa+ffHVl8PlsnE0+qPpBuQjVJjno6pZpSKXYl7/bZyFaQxhf98hHDKEppwdWIW1frFnrxSOWHFfWesSEWC6R/P4zOFrix3TzDFLlXRTR8c0fEEJ1/itpo7SVO9Jdr1DVxZ0USyjZsEY5vZfiiAC0UoTGOrm9PZLuRl8X+Dq1HQtoFbJZbv61i+Poblh/97TC7n0neCcK0ETNUrz1/xPHf+DNAW9Ac6t8O8WH3Vp98f5lCaYKAOFZMLyHL4Y0fe319idMNgMMp+zWVSybUed/+/h7I4wRAG1W6XDy4XmjR9HnzvDRZXUAYDFOhC1S/Hh+fIXxen+eO+AKqbs+wAo30zDTDvDxKoJN88sjUzDFAvBzEUGFsnADoIvAJzoh2BZ8sner+Ke/vwECuQAAAABJRU5ErkJggg==") no-repeat scroll 10px center;
color: #fff;
font-size: 1.5em;
padding: 0.5em 50px;
margin:0;
}
h2 {
color: #337ab7;
font-size:1.1em;
margin: 0.5em 0;
}
.info {
background:#f5f5f5;
padding:0.5em;
margin: 1em 0;
}
.info ul {
margin:0;
}
#map {
float:left;
margin-right:1em;
background:#ddd;
}
.ol-attribution img {
vertical-align:middle;
}
.layerSwitcher {
display:inline-block;
background:#cdf;
padding:0.5em;
}
.btn {
color:#fff;
background:#369;
padding:0.5em;
text-decoration:none;
cursor:pointer;
display:inline-block;
margin-right:0.5em;
}
.block,
.options {
display: table;
margin: 0.5em;
position: relative;
z-index: 1;
margin:1em;
}
.options {
background: #def;
padding: 0.5em;
}
.options ul {
list-style: none;
padding-left: 0.5em;
}
i[class*="icss-"] {
position: relative;
display:inline-block;
font-style: normal;
background-color:currentColor;
box-sizing: border-box;
vertical-align: middle;
font-size: 1.5em;
}
i[class*="icss-"]:before,
i[class*="icss-"]:after {
content: "";
border-width: 0;
position: absolute;
}
i.icss-book {
width:1em;
height:.8em;
background-color: transparent;
margin: 0 .03em .08em 0;
}
i.icss-book:before {
height: .8em;
width: 0.7em;
box-shadow: inset 0 0 0 0.15em,
inset 0 -.48em,
.07em .07em;
border: 0.07em solid transparent;
border-width: 0 0.07em .07em 0;
border-radius: .05em .15em .1em .1em / .05em .05em .1em .05em;
transform: skewX(-20deg);
left: 0.15em;
}
i.icss-book:after {
width: .2em;
height: .2em;
background-color: transparent;
border: 0.05em solid currentColor;
border-color: currentColor transparent transparent currentColor;
border-radius: 50%;
transform: rotate(-45deg);
top: 0.67em;
left: .018em;
box-shadow: .13em -.15em 0 -.05em,
.51em -.33em 0 -.05em;
}
.experimental {
color:#fff;
background: #f91;
padding:.2em .5em;
display: inline-block;
-webkit-transform: rotate(-5deg);
transform: rotate(-5deg);
margin: -1em 0;
}
.ol-attribution ul {
font-size: .8em;
}
Also here is a working example: StackBlitz

How to display value with '%' react chart-race

i want to display at chart-race value with percentage. example 75%
however, the value of chart-race accepts only number. how would I able to include '%'
here's the code from 'https://www.npmjs.com/package/react-chart-race'
import React, { Component } from 'react';
import ChartRace from 'react-chart-race';
class Form extends Component{
constructor(props){
super(props);
this.state = {
data: []
};
setInterval(() => {
this.handleChange();
}, 0);
}
getRandomInt(min, max){
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
handleChange(){
const data = [
{ id: 0, title: 'Ayfonkarahisar', value: 10, color: '#50c4fe'},
{ id: 1, title: 'Kayseri', value: 20, color: '#3fc42d' },
{ id: 2, title: 'Muğla', value: 30, color: '#c33178' },
{ id: 3, title: 'Uşak', value: 40, color: '#423bce' },
{ id: 4, title: 'Sivas', value: 50, color: '#c8303b' },
{ id: 5, title: 'Konya', value: 60, color: '#2c2c2c' }
];
this.setState({ data });
}
render(){
return(
<div>
<ChartRace
data={this.state.data}
backgroundColor='#000'
width={760}
padding={12}
itemHeight={58}
gap={12}
titleStyle={{ font: 'normal 400 13px Arial', color: '#fff' }}
valueStyle={{ font: 'normal 400 11px Arial', color: 'rgba(255,255,255, 0.42)' }}
/>
</div>
);
}
}
export {Form}
I have made some changes in react-chart-race for label
Now, you do not need to use this dependency use below code in your project
Add this two files to your project
ChartRace.js
import React, { Component } from "react";
import "./chartstyle.css";
export default class ChartRace extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data.sort((a, b) => b.value - a.value),
temp: this.props.data,
maxValue: Math.max.apply(Math, this.props.data.map(item => item.value)),
valueLabel: this.props.valueLabel
};
}
static getDerivedStateFromProps(nextProps, prevState) {
let newProps = [...nextProps.data];
return {
data: nextProps.data,
temp: newProps.sort((a, b) => b.value - a.value),
maxValue: Math.max.apply(Math, nextProps.data.map(item => item.value))
};
}
draw(item, index) {
const indis = this.state.temp.findIndex(temp => temp.id === item.id);
const translateY =
indis === 0
? this.props.padding
: this.props.padding +
indis * this.props.itemHeight +
indis * this.props.gap;
return (
<div
key={index}
className="raceItem"
style={{
height: this.props.itemHeight,
transform:
"translateY(" +
translateY +
"px) translateX(" +
this.props.padding +
"px)"
}}
>
<b
style={{
backgroundColor: item.color,
width:
(item.value / this.state.maxValue) *
(this.props.width - 120 - 2 * this.props.padding)
}}
/>
<span>
<em style={this.props.titleStyle}>{item.title}</em>
<i style={this.props.valueStyle}>
{item.value} {this.state.valueLabel}{" "}
</i>
</span>
</div>
);
}
render() {
return (
<div
className="raceArea"
style={{
backgroundColor: this.props.backgroundColor,
paddingTop: this.props.padding,
paddingBottom: this.props.padding,
width: this.props.width,
height:
2 * this.props.padding +
this.state.temp.length * this.props.itemHeight +
(this.state.temp.length - 1) * this.props.gap
}}
>
{this.state.data.map((item, index) => this.draw(item, index))}
</div>
);
}
}
ChartRace.defaultProps = {
data: [],
backgroundColor: "#f9f9f9",
width: 680,
padding: 20,
itemHeight: 38,
gap: 4,
titleStyle: { font: "normal 400 13px Arial", color: "#212121" },
valueStyle: { font: "normal 400 11px Arial", color: "#777" },
valueLabel: ""
};
chartstyle.css
.raceArea {
box-sizing: border-box;
position: relative;
}
.raceItem {
display: flex;
align-items: center;
position: absolute;
top: 0px;
left: 0px;
transition: all 1200ms;
}
.raceItem b {
height: 100%;
border-radius: 4px;
transition: all 600ms;
background: #dadada;
}
.raceItem span {
width: 112px;
margin-left: 8px;
display: flex;
flex-direction: column;
}
.raceItem em {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.raceItem i {
margin-top: 2px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
Import it like
import ChartRace from "./ChartRace";
Use it like
<ChartRace
data={this.state.data}
backgroundColor="#000"
width={760}
padding={12}
itemHeight={58}
gap={12}
valueLabel="%" // pass your label here
titleStyle={{ font: "normal 400 13px Arial", color: "#fff" }}
valueStyle={{
font: "normal 400 11px Arial",
color: "rgba(255,255,255, 0.42)"
}}
/>

Resources