d3 plot as card content material UI - reactjs

I am having some trouble in understanding how can I include a data visualization made with D3.js inside a <CardContent>. I am using Material UI. I created the card as follow
<Card className="shape-curve-container" sx={{ width: 300, height: 300 }}>
<CardHeader
title={feat}
action={
<IconButton aria-label="close" onClick={onRemoveFeat}>
<CloseIcon />
</IconButton>
}
/>
<CardContent>
add visualization implementation here
</CardContent>
</Card>
I have created the visualization before as follow and know I would like to put it inside the card.
function renderCurvePlot(
root: SVGElement | SVGGElement,
data: LineArray,
props: ChartStyle
) {
const margin = getMargin(props.margin)
const height = props.height - margin.top - margin.bottom
const width = props.width - margin.left - margin.right
const visRoot = d3.select(root)
const base = getChildOrAppend<SVGGElement, SVGElement>(visRoot, "g", "base")
.attr("transform", `translate(${margin.left}, ${margin.top})`)
const xValues = data.map(d => d.XX)
const x = d3.scaleLinear().domain([d3.min(xValues) || 0, d3.max(xValues) || 1]).range([0, width])
const yValues = data.map(d => d.pdep_XX)
const y = d3.scaleLinear().domain([-105000, 105000]).range([height, 0])
const colors = d3.scaleOrdinal(["1", "2", "3"], ["black", "green", "red"])
base.selectAll("circle.dot")
.data(data)
.join<SVGCircleElement>(
enter => enter.append("circle")
.attr("class", "dot"),
update => update,
exit => exit.remove()
)
.attr("cx", d => x(d.XX))
.attr("cy", d => y(d.pdep_XX))
.attr("r", 1)
.style("fill", d => colors('black') || "#fff")
Any tips?

Related

How can I get Framer Motion 3.3.0 scrollYprogress to work in react?

Hi I cant get scrollYProgress to work (xLeft). If I console log it I get nothing. Where as scrollY works fine. I am on a project using an older version of framer (nothing I can do about that). I know someone else had a similar problem but I can figure out how to implement it. I am using Mui - if that has any relevance. Would love some help here.
export const TwelveTwelveAni: React.FC<TwelveTwelveAniProps> = (props) => {
const { headerOne, headerTwo, registerRef } = props;
const classes = useStyles(props);
const { scrollY } = useViewportScroll();
const { scrollYProgress } = useViewportScroll();
scrollY.onChange((value) => console.log(value));
scrollYProgress.onChange((value) => console.log(value));
const xLeft = useTransform(scrollYProgress, [0, 1], [-1100, 1000]);
const xRight = useTransform(scrollY, [0, 2500], [1000, -1100]);
return (
<Grid xs={12} md={12}>
<Box className={classes.mainWrapperColCenter}>
<motion.div style={{ x: xLeft }}>
<Typography variant="h1" className={classes.jumbo}>
{headerOne}
</Typography>
</motion.div>
<motion.div style={{ x: xRight }}>
<Typography variant="h1" className={classes.jumbo}>
{headerTwo}
</Typography>
</motion.div>
</Box>
</Grid>
);
};
For me, your code looks alright, i would advice you upgrade the framerMotion Library to the latest package, talk to the owner of the project and see if you can upgrade the package
the latest api for scrollY and scrollYProgress is useScroll()
what will i change from this code?
const {scrollY, scrollYProgress} = useScroll();
const xLeft = useTransform(scrollYProgress, [0, 1], [-1100, 1000]);
const xRight = useTransform(scrollY, [0, 2500], [1000, -1100]);
useEffect(() => {
const removeA = scrollY.onChange((value) => console.log(value));
const removeB = scrollYProgress.onChange((value) => console.log(value));
return () => {
removeA()
removeB()
}
})

Ract Native Animated API : I can't move the element correctly and leave a space between the elements while using the react native Animated API

I am trying to implement this carousel in React Native
and use React Native Animated to implement it.
I wrote the code as follows
const itemSize = AppDevice.width / 4;
const marginValue = 5;
const HomeScreen = ({navigation}) => {
let videoRef = useRef();
const scrollX = useRef(new Animated.Value(0)).current;
const marginX = useRef(new Animated.Value(0)).current;
let carouselRef = useRef();
let [loading, setLoading] = useState(true);
let [MainCategory, setMainCategory] = useState();
let [currentIndex, setcurrentIndex] = useState();
useEffect(() => {
const renderCarousel = () => {
return (
<Animated.FlatList
data={MainCategory}
horizontal
showsHorizontalScrollIndicator={false}
snapToInterval={itemSize + marginValue * 2}
bounces={false}
decelerationRate={0}
style={{backgroundColor: 'red'}}
scrollEventThrottle={16}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: scrollX}}}],
{useNativeDriver: true},
)}
onMomentumScrollEnd={e =>
setcurrentIndex(
Math.ceil(
e.nativeEvent.contentOffset.x /(itemSize + marginValue * 2),//to find active item
),
)
}
renderItem={({item, index}) => {
const inputRange = [
(index - 2) * (itemSize + marginValue * 2), //Before 2 items of the active item
(index - 1) * (itemSize + marginValue * 2), //Before 1 items of the active item
index * (itemSize + marginValue * 2), //active item
(index + 1) * (itemSize + marginValue * 2),//after 1 items of the active item
(index + 2) * (itemSize + marginValue * 2),//after 2 items of the active item
];
const translateY = scrollX.interpolate({ //To change the size of items
inputRange,
outputRange: [0.6, 0.9, 1.25, 0.9, 0.6],
});
const inputRange2 = [
(index - 2) * (itemSize + marginValue),
(index - 1) * (itemSize + marginValue),
index * (itemSize + marginValue),
(index + 1) * (itemSize + marginValue),
(index + 2) * (itemSize + marginValue),
];
const margin = marginX.interpolate({ //to add margine to items ((Here it does
not work as expectedcorrectly
inputRange,
outputRange: [-20, 10, 0, 10, -20], //
});
return (
<Animated.View
style={{
marginTop: 50,
width: itemSize,
height: itemSize,
justifyContent: 'space-between',
marginHorizontal:
index != 0 && index + 1 != MainCategory.length //to center first item
? marginValue
: 0,
marginStart:
index == 0
? itemSize * 2 - itemSize / 2 + marginValue
: marginValue,
marginEnd: //to center last item
index + 1 == MainCategory.length
? itemSize + itemSize / 2 - marginValue
: marginValue,
padding: 20,
borderRadius: 20,
backgroundColor: '#FFF',
transform: [{scale: translateY}, {translateX: margin}],
}}>
<Image
style={{width: '100%', height: '100%', resizeMode: 'contain'}}
source={require('../assets/images/open-box.png')}
/>
</Animated.View>
);
}}
/>
);
};
return (
<View style={{flex: 1}}>
{renderCarousel()}
</View>
);
};
The size changes as expected, but the problem is that I want to leave spaces between the elements, but it does not work for me as expected
Is there anyone who can help me, thanks in advance
I finally found I just replaced the ref, from marginX to scrollX

Is there a `useSpring()` alternative for framer-motion?

After some research, i found a good bottom sheet for my remix application on codesandbox. The problem is, that the bottom sheet is powered by react-spring and #use-gestures but i want to use framer-motion for the animation part. I don't want to use both.
so the question: is there an alternative to useSpring() hook in framer-motion, so i can use the following code? Can i create a very similar animation in framer-motion, or should I better use react-spring for this kind of animation, what do you think?
import React from 'react'
import { useDrag } from '#use-gesture/react'
import { a, useSpring, config } from '#react-spring/web'
import styles from './styles.module.css'
const items = ['save item', 'open item', 'share item', 'delete item', 'cancel']
const height = items.length * 60 + 80
export default function App() {
const [{ y }, api] = useSpring(() => ({ y: height }))
const open = ({ canceled }) => {
// when cancel is true, it means that the user passed the upwards threshold
// so we change the spring config to create a nice wobbly effect
api.start({ y: 0, immediate: false, config: canceled ? config.wobbly : config.stiff })
}
const close = (velocity = 0) => {
api.start({ y: height, immediate: false, config: { ...config.stiff, velocity } })
}
const bind = useDrag(
({ last, velocity: [, vy], direction: [, dy], movement: [, my], cancel, canceled }) => {
// if the user drags up passed a threshold, then we cancel
// the drag so that the sheet resets to its open position
if (my < -70) cancel()
// when the user releases the sheet, we check whether it passed
// the threshold for it to close, or if we reset it to its open positino
if (last) {
my > height * 0.5 || (vy > 0.5 && dy > 0) ? close(vy) : open({ canceled })
}
// when the user keeps dragging, we just move the sheet according to
// the cursor position
else api.start({ y: my, immediate: true })
},
{ from: () => [0, y.get()], filterTaps: true, bounds: { top: 0 }, rubberband: true }
)
const display = y.to((py) => (py < height ? 'block' : 'none'))
const bgStyle = {
transform: y.to([0, height], ['translateY(-8%) scale(1.16)', 'translateY(0px) scale(1.05)']),
opacity: y.to([0, height], [0.4, 1], 'clamp')
}
return (
<div className="flex" style={{ overflow: 'hidden' }}>
<a.div className={styles.bg} onClick={() => close()} style={bgStyle}>
<img
src="https://images.pexels.com/photos/1239387/pexels-photo-1239387.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
alt=""
/>
<img
src="https://images.pexels.com/photos/5181179/pexels-photo-5181179.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
alt=""
/>
</a.div>
<div className={styles.actionBtn} onClick={open} />
<a.div className={styles.sheet} {...bind()} style={{ display, bottom: `calc(-100vh + ${height - 100}px)`, y }}>
{items.map((entry, i) => (
<div
key={entry}
onClick={() => (i < items.length - 1 ? alert('clicked on ' + entry) : close())}
children={entry}
/>
))}
</a.div>
</div>
)
}
Link to Codesandbox:
https://codesandbox.io/s/github/pmndrs/use-gesture/tree/main/demo/src/sandboxes/action-sheet?file=/src/App.jsx:0-2734

react konva keep one layer fixed while zoom on stage

I'm developing a map (green rectangle ) and a path drawn over it ( black lines ) in Konva:
The map has a fixed dimensions while the path is get from an API call.
I want to be able to zoom the map either with the mousescroll or by clicking the buttons + o - that I created.
I follow this example how to react-konva zooming on scroll in order to implement the zoom, but when I scroll, also the buttons change their positions, see image below.
I would like to keep the buttons fixed at the bottom side of the map.
This is a piece of my code, I defined some custom components as Map, Ground, WholePath, GroundButtons and Button:
export const Map = ( props ) => {
const [mouseScale, setMouseScale] = useState(1);
const [x0, setX0] = useState(0);
const [y0, setY0] = useState(0);
const scaleBy = 1.02;
const handleWheel = (e) => {
const group = e.target.getStage();
e.evt.preventDefault();
const mousePointTo = {
x: group.getPointerPosition().x / mouseScale - group.x() / mouseScale,
y: group.getPointerPosition().y / mouseScale - group.y() / mouseScale
};
const newScale = e.evt.deltaY < 0 ? mouseScale * scaleBy : mouseScale / scaleBy;
setMouseScale(newScale);
setX0(-(mousePointTo.x - group.getPointerPosition().x / newScale) * newScale );
setY0(-(mousePointTo.y - group.getPointerPosition().y / newScale) * newScale );
};
const SimulateMouseWheel = (e, BtnType, mScale = scaleBy) => {
const newScale = BtnType > 0 ? mouseScale * mScale : mouseScale / mScale;
setMouseScale(newScale);
};
const onClickPlus = (e) => {
SimulateMouseWheel(e, +1, 1.08);
};
const onClickMinus = (e) => {
SimulateMouseWheel(e, -1, 1.08);
};
return (
<Stage
width={800}
height={400}
draggable={true}
onWheel={handleWheel}
scaleX={mouseScale}
scaleY={mouseScale}
x={x0}
y={y0}
>
<Layer>
<Ground pitch={props.pitch}/>
<WholePath path={props.path}/>
</Layer>
<Layer>
<GroundButtons reference={props.reference} onClickMinus={onClickMinus} onClickPlus={onClickPlus} />
</Layer>
</Stage>
)
}
const GroundButtons = ( props ) => {
return (
<Group>
<Button xText={10} yText={5} text={"+"} x={790} y={360} side={30} onClick={props.onClickPlus}/>
<Button xText={12} yText={5} text={"-"} x={790} y={360} side={30} onClick={props.onClickMinus}/>
</Group>
)
}
const Button = props => {
return (
<Group x={props.x} y={props.y} onClick={props.onClick}>
<Rect
width={props.side}
height={props.side}
fill='#f2f1f0'
stroke='#777'
strokeWidth={1}
/>
<Text
x={props.xText}
y={props.yText}
fontSize={20}
text={props.text}
stroke='#777'
align="center"
strokeWidth={1}
/>
</Group>
)
}
This is the solution I implemented:
Thanks to Ondolin's answer.
export const Map = ( props ) => {
const [mouseScale, setMouseScale] = useState(1);
const [x0, setX0] = useState(0);
const [y0, setY0] = useState(0);
const scaleBy = 1.02;
const handleWheel = (e) => {
const group = e.target.getStage();
e.evt.preventDefault();
const mousePointTo = {
x: group.getPointerPosition().x / mouseScale - group.x() / mouseScale,
y: group.getPointerPosition().y / mouseScale - group.y() / mouseScale
};
const newScale = e.evt.deltaY < 0 ? mouseScale * scaleBy : mouseScale / scaleBy;
setMouseScale(newScale);
setX0(-(mousePointTo.x - group.getPointerPosition().x / newScale) * newScale );
setY0(-(mousePointTo.y - group.getPointerPosition().y / newScale) * newScale );
};
const SimulateMouseWheel = (e, BtnType, mScale = scaleBy) => {
const newScale = BtnType > 0 ? mouseScale * mScale : mouseScale / mScale;
setMouseScale(newScale);
};
const onClickPlus = (e) => {
SimulateMouseWheel(e, +1, 1.08);
};
const onClickMinus = (e) => {
SimulateMouseWheel(e, -1, 1.08);
};
return (
<div>
<Stage
width={800}
height={400}
draggable={true}
onWheel={handleWheel}
scaleX={mouseScale}
scaleY={mouseScale}
x={x0}
y={y0}
>
<Layer>
<Ground pitch={props.pitch}/>
<WholePath path={props.path}/>
</Layer>
<Layer>
<GroundButtons reference={props.reference} onClickMinus={onClickMinus} onClickPlus={onClickPlus} />
</Layer>
</Stage>
<button className="ground_controls" onClick={onClickPlus}><i className="fa fa-plus" aria-hidden="true"></i></button>
<button className="ground_controls" onClick={onClickMinus}><i className="fa fa-minus" aria-hidden="true"></i></button>
</div>
)
}
I would strongly recommend you not to use Buttons drawn on the canvas (stage). You should use standard HTML buttons. You can position them on top of the stage with css (Positon absolute).
If you do it this way, there are many benefits to you. E.g. you can design it easier and better. In addition, it is more efficient and also solves the problem from this question.

change position landscape or portrait in react

i have a landscape and portrait image. how can i pop up it correctly ?
my pop up:
<Modal
visible={this.state.visible}
width="600"
height="400"
effect="fadeInUp"
onClickAway={() => this.closeModal()}
>
<div>
<img
src={"/img/" + this.state.LinkName}
width="600"
height="400"
alt="Houston We have pb"
/>
my idea, i will add "land" and "portrait" string to each image.
so i can test this.state.LinkName.contains("Land") then
width = 600 and height = 400
else width = 400 and height = 600
how can i do that in react ?
i am newbie in react technologie.
i modifie the code :
handleButtonClick = (e, row) => {
var bild = new Image();
bild.src = "/img/" + row.original.Cert;
//Image.getSize(bild.src, (width, height) => {this.setState({width, height})});
//const {width, height} = Image.resolveAssetSource(bild.src);
var Pos
bild.onload = function()
{
Pos = bild.width > bild.height ? "Land" : "Port";
}
this.setState({ visible: true, LinkName: row.original.Cert, ThePos: Pos });
};
Now i get the real width and height.
the problem now, the variable ThePos is always undifined.
You can just test on this.state.LinkName.contains("Land")
like this:
render(){
return(
<Modal
visible={this.state.visible}
width={this.state.LinkName.contains("Land")? "600" : "400"}
height={this.state.LinkName.contains("Land")? "400" : "600"}
effect="fadeInUp"
onClickAway={() => this.closeModal()}
>
<div>
<img
src={"/img/" + this.state.LinkName}
width={this.state.LinkName.contains("Land")? "600" : "400"}
height={this.state.LinkName.contains("Land")? "400" : "600"}
alt="Houston We have pb"
/>
</Modal>
)
}
i found it:
handleButtonClick = (e, row) => {
var bild = new Image();
bild.src = "/img/" + row.original.Cert;
bild.onload = () =>
this.setState({ visible: true, LinkName: row.original.Cert, ThePos: bild.width > bild.height ? "Land" : "Port" });
};

Resources