I'm trying to create a React Component that zooms an image and the zoomed image follows the mouse.
I've created a pen to show what I've done so far: React Zoom
And here is the code:
class ProductGallery extends React.Component {
constructor (props) {
super(props)
this.state = {
imgZoomed: false,
mouseX: undefined,
mouseY: undefined
}
this.zoomImage = this.zoomImage.bind(this)
this.unZoomImage = this.unZoomImage.bind(this)
this.moveMouseImg = this.moveMouseImg.bind(this)
}
zoomImage () {
this.setState({
imgZoomed: true
})
}
unZoomImage () {
this.setState({
imgZoomed: false
})
}
moveMouseImg (e) {
const {
top: offsetTop,
left: offsetLeft
} = e.target.getBoundingClientRect()
const x = ((e.pageX - offsetLeft) / e.target.width) * 100
const y = ((e.pageY - offsetTop) / e.target.height) * 100
this.setState({
mouseX: x,
mouseY: y
})
}
render () {
const transform = {
transformOrigin: `${this.state.mouseX}% ${this.state.mouseY}%`
}
const classes = Object.assign({}, transform, {
transform: this.state.imgZoomed ? 'scale(2.0)' : 'scale(1.0)',
maxHeight: '615px',
maxWidth: '100%',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
backgroundSize: 'cover',
transition: 'transform .1s ease-out'
})
const divStyles = {
height: '615px',
width: '615px',
float: 'left',
marginLeft: '10px',
overflow: 'hidden',
borderStyle: 'solid'
}
return (
<div style={divStyles}>
<img
src="http://www.planwallpaper.com/static/images/maxresdefault_8yZPhSS.jpg"
style={classes}
alt="dmsak"
onMouseOver={this.zoomImage}
onMouseOut={this.unZoomImage}
onMouseMove={this.moveMouseImg}
/>
</div>
)
}
}
React.render(<ProductGallery />, document.getElementById('app'));
The problem is that when I move the mouse, the component just starts to "shake"! I am using a scale of 2.0, but if I change it to 1.5 (transform: scale(1.5)), the problem only happens in the edges of the image.
How can I solve it?
Related
I want my component to have an animation that if my mouse enter,it will be fully displayed in 0.3s,and if my mouse leave,it will disappear in 0.1s.But useSpring can just define one duration just like the code below,which cause that the component will be displayed and disappear all in 0.3s.How can I define different duration for from->to and to->from?Thanks for anyone who can help.
const animationStyle = useSpring({
bottom: show ? 0 : -71,
from: {
bottom: -71
},
config: { duration: 300 }
})
Like this
import import React, { useState } from 'react';
import { animated, useSpring } from '#react-spring/web';
const SuspendedComponent: React.FC = ({ children }) => {
const [isMouseEnter, setMouseEnter] = useState<boolean>(false);
const _style = useSpring({
bottom: isMouseEnter ? 0 : -71,
from: {
bottom: -71,
},
config: { duration: 300 },
});
function onMouseHandler(isEnter: boolean) {
setMouseEnter(isEnter);
}
return (
<div
style={{ display: 'block', width: '100px', height: '100px' }}
onMouseEnter={() => onMouseHandler(true)}
onMouseLeave={() => onMouseHandler(false)}
>
{isMouseEnter && <animated.div style={_style}>{children}</animated.div>}
</div>
);
};
export default SuspendedComponent;
You can control animated.div element display by onMouseEnter and onMouseLeave event.
I want to have a resizeable modal only on height so I did write some code but while trying to grow it to the bottom because it's going fast and out of the element it doesn't have any impact, also I have seen codes like this but they work properly like this I don't know what I'm missing.
also, I want to ask; is it the right way of doing resizeable components in react? I did try to write it with states but I faced some problems like it was growing unexpectedly.
import React, { FC, useCallback, useMemo, useRef } from "react";
import { PrimitivesT } from "../Table/Table";
interface ModalProps {
children: JSX.Element | PrimitivesT;
display: boolean;
width: string;
height: string;
x?: number;
y?: number;
boxShadow?: boolean;
}
const Modal: FC<ModalProps> = ({
children,
display = false,
// initial height
height = "0",
width = "0",
x,
y,
boxShadow = true,
}) => {
const ref = useRef<HTMLDivElement>(null);
const styles = useMemo<React.CSSProperties>(
() => ({
display: display ? "block" : "none",
height: height,
width,
minHeight: "15px",
position: "absolute",
left: x,
top: y,
boxShadow: boxShadow ? "1px 1px 10px 5px var(--gray)" : undefined,
borderRadius: "5px",
backgroundColor: "white",
zIndex: 900,
}),
[display, height, width, x, y, boxShadow]
);
const bottomStyle = useMemo<React.CSSProperties>(
() => ({
cursor: "row-resize",
width: "100%",
position: "absolute",
bottom: "0",
left: "0",
height: "5px",
}),
[]
);
const onMouseDown =
useCallback((): React.MouseEventHandler<HTMLDivElement> => {
let y = 0;
let h = 60;
const onMouseMove = (e: MouseEvent) => {
const YDir = e.clientY - y;
if (ref.current) ref.current.style.height = `${h + YDir}px`;
};
const onMouseUp = () => {
try {
ref.current?.removeEventListener("mousemove", onMouseMove);
ref.current?.removeEventListener("mouseup", onMouseUp);
} catch (err) {
console.error(err);
}
};
return e => {
e.stopPropagation();
const bounding = ref.current?.getBoundingClientRect();
if (bounding?.height) h = bounding?.height;
y = e.clientY;
ref.current?.addEventListener("mousemove", onMouseMove);
ref.current?.addEventListener("mouseup", onMouseUp);
};
}, []);
return (
<div
ref={ref}
style={styles}
data-testid="Modal"
onMouseDown={e => e.stopPropagation()}>
{children}
<div style={bottomStyle} onMouseDown={onMouseDown()}></div>
</div>
);
};
export default Modal;
I think it didn't work that way because it's modal and it has to be fixed or absolute so I change the element that I was attaching event listeners instead of the resizeable target I used document object.
const onMouseDown =
useCallback((): React.MouseEventHandler<HTMLDivElement> => {
let y = 0;
let h = 60;
const onMouseMove = (e: MouseEvent) => {
const YDir = e.clientY - y;
if (ref.current) ref.current.style.height = `${h + YDir}px`;
};
const onMouseUp = () => {
try {
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
} catch (err) {
console.error(err);
}
};
return e => {
e.stopPropagation();
const bounding = ref.current?.getBoundingClientRect();
if (bounding?.height) h = bounding?.height;
y = e.clientY;
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
};
}, []);
I am developing an app, which provides the design of different shapes like Square , I have used the following Reanimated 2 API . I want to get its coordinates (pageX and PageY) every time I move the object.
my research
So i read this article about measure and i decided to try with that.
Then i created ref to my animated view like that const aref = useAnimatedRef();.
After that i read again in Reanimated docs that i should use useDerivedValue combined with measure. But unfortunately I didn't get what I needed in the end. My app crashed with following error -
Tried to synchronously call usederivedvalue from a different thread
My code so far i tried to use runOnJs - no luck again! And my question is -
import React from "react";
import { StyleSheet, View } from "react-native";
import {
GestureHandlerRootView,
PanGestureHandler,
} from "react-native-gesture-handler";
import Animated, {
measure,
runOnJS,
useAnimatedGestureHandler,
useAnimatedRef,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
withSpring,
} from "react-native-reanimated";
const SIZE = 100.0;
const CIRCLE_RADIUS = SIZE * 2;
export default function App() {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const aref = useAnimatedRef();
const panGestureEvent = useAnimatedGestureHandler({
onStart: (event, context) => {
context.translateX = translateX.value;
context.translateY = translateY.value;
},
onActive: (event, context) => {
translateX.value = event.translationX + context.translateX;
translateY.value = event.translationY + context.translateY;
const wrap = () => {
try {
const pageX = measure(aref).pageX;
console.log({ pageX });
} catch {}
};
useDerivedValue(() => {
runOnJS(wrap)(true);
});
},
onEnd: () => {
const distance = Math.sqrt(translateX.value ** 2 + translateY.value ** 2);
if (distance < CIRCLE_RADIUS + SIZE / 2) {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
}
},
});
const rStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: translateX.value,
},
{
translateY: translateY.value,
},
],
};
});
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<View style={styles.container}>
<View style={styles.circle}>
<PanGestureHandler onGestureEvent={panGestureEvent}>
<Animated.View style={[styles.square, rStyle]} ref={aref} />
</PanGestureHandler>
</View>
</View>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
square: {
width: SIZE,
height: SIZE,
backgroundColor: "rgba(0, 0, 256, 0.5)",
borderRadius: 20,
},
circle: {
width: CIRCLE_RADIUS * 2,
height: CIRCLE_RADIUS * 2,
alignItems: "center",
justifyContent: "center",
borderRadius: CIRCLE_RADIUS,
borderWidth: 5,
borderColor: "rgba(0, 0, 256, 0.5)",
},
});
My error - every time when i move my square my app crash with error above.
How can i get coordinates (pageX and PageY) every time I move the object.
What is causing the problem?
Of course this is the use of
const wrap = () => {
try {
const pageX = measure(aref).pageX;
console.log({ pageX });
} catch {}
};
useDerivedValue(() => {
runOnJS(wrap)(true);
});
in onActive event but i don't know how to fix that ?
Sorry for my bad English.
I was using the reanimated v2 for creating animations for an app. I was creating a splash (loading) screen with three dots that jump up and down. The 3 dots are supposed to have certain constant delay (interval) between them. Like the animation when the other person is typing in facebook's messenger.
The animation looks fine in the beginning but after a while the 2 dots or even 3 dots depending on the delays and duration sync up and I am left with 2 or 3 dots absolutely in sync with each other. Here is the video of the problem animation video
I am very new to react-native and reanimated. So I am assuming the problem is in my code. I have no idea if this is the correct way to do this. The code examples I see in reanimated v1 have "startClock" and custom "runTiming" functions but I couldn't find them in the docs for v2. reanimated docs
import React, { useEffect } from "react";
import { View, StyleSheet } from "react-native";
import Animated, {
useSharedValue,
useAnimatedStyle,
withRepeat,
withTiming,
withDelay,
} from "react-native-reanimated";
import { themeColor } from "../../assets/ThemeColor";
const Loading = () => {
const y1 = useSharedValue(0);
const y2 = useSharedValue(0);
const y3 = useSharedValue(0);
const animatedStyles1 = useAnimatedStyle(() => {
return {
transform: [
{
translateY: withDelay(
0,
withRepeat(withTiming(y1.value, { duration: 200 }), -1, true)
),
},
],
};
});
const animatedStyles2 = useAnimatedStyle(() => {
return {
transform: [
{
translateY: withDelay(
100,
withRepeat(withTiming(y2.value, { duration: 200 }), -1, true)
),
},
],
};
});
const animatedStyles3 = useAnimatedStyle(() => {
return {
transform: [
{
translateY: withDelay(
200,
withRepeat(withTiming(y3.value, { duration: 200 }), -1, true)
),
},
],
};
});
/**
*
*
*
*
*/
useEffect(() => {
y1.value = -10;
y2.value = -10;
y3.value = -10;
}, []);
/**
*
*
*
*
*
*/
return (
<View style={styles.loadingContainer}>
<Animated.View
style={[styles.ballStyle, animatedStyles1]}
></Animated.View>
<Animated.View
style={[styles.ballStyle, animatedStyles2]}
></Animated.View>
<Animated.View
style={[styles.ballStyle, animatedStyles3]}
></Animated.View>
</View>
);
};
const styles = StyleSheet.create({
loadingContainer: {
flex: 1,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
},
ballStyle: {
width: 13,
height: 13,
backgroundColor: themeColor,
borderRadius: 13,
margin: 10,
},
});
export default Loading;
Can someone please tell me why the animations sync up eventually and what is the correct way to animate three elements with the same animation but some constant delay. Thank you.
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