How to handle event bubbling in this code? - javascript

In the start, there will be a button on the top and on clicking that button it will make parent div for existing div and all the divs should be draggable inside its parent div.
Here is my code
import React, { useState } from "react";
import Draggable from "react-draggable";
function ParentDraggable(props) {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const handleMouseDown = (e) => {
let startX = e.clientX;
let startY = e.clientY;
const handleMouseMove = (e) => {
setX(x + e.clientX - startX);
setY(y + e.clientY - startY);
startX = e.clientX;
startY = e.clientY;
};
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", () => {
document.removeEventListener("mousemove", handleMouseMove);
});
};
return (
<Draggable bounds="parent" handle=".handle">
<div
style={{
position: "relative",
top: y,
left: x,
border: "1px solid black",
width: `${props.w}px`,
height: `${props.h}px`
}}
>
<div
style={{
height: 20,
backgroundColor: "gray"
}}
onMouseDown={handleMouseDown}
className="handle"
>
Title Bar
</div>
{props.children}
</div>
</Draggable>
);
}
function App() {
const [parents, setParents] = useState([]);
const [w, setW] = useState(200);
const [h, setH] = useState(200);
const handleAddParent = () => {
setParents([
<ParentDraggable w={w} h={h}>
{parents}
</ParentDraggable>
]);
setW((prev) => prev + 100);
setH((prev) => prev + 100);
};
return (
<>
<button onClick={handleAddParent}>AddParent</button>
<div style={{ height: "98vh" }}>{parents}</div>
</>
);
}
export default App;
When i try to drag any child component its parent div is also getting dragged and I dont that.
Expected - short video
my code demo - short video
check code on codesandbox
please help me to solve this issue.
Thanks in advance.

Related

Create nested divs which are draggable inside their parent div

Initially app will have only one thing that is a button AddParent on the top.
AddParent button will add a parent Draggable to this component i.e every time the user clicks the AddParent button, a parent draggable should envelope the draggable component. Keep in mind that each level of component should in itself be draggable inside its parents’ window i.e. each of the title bars can be used to move around the respective component inside its parents’ blank box.
Something like this (after 3 clicks of AddParent, there are 3 parents for the initial child) ->
(You don’t need to actually color the blank areas, it is just for ease of explanation...they can be white)
result after 3 clicks - image
Goal is to achive this
I have tried a lot but not able to come with expected solution. This is working but it is dragging its parent component too and top-most component is not draggble to bottom side.
import React, { useState } from 'react';
import Draggable from 'react-draggable';
function ParentDraggable(props) {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const handleMouseDown = (e) => {
let startX = e.clientX;
let startY = e.clientY;
const handleMouseMove = (e) => {
setX(x + e.clientX - startX);
setY(y + e.clientY - startY);
startX = e.clientX;
startY = e.clientY;
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', handleMouseMove);
});
};
return (
<Draggable bounds='parent' handle='.handle'>
<div
style={{
position: 'relative',
top: y,
left: x,
border: '1px solid black',
width: `${props.w}px`,
height: `${props.w}px`,
}}
>
<div
style={{
height: 20,
backgroundColor: 'gray',
}}
onMouseDown={handleMouseDown}
className='handle'
onClick={e=>e.preventDefault()}
>
Title Bar
</div>
{props.children}
</div>
</Draggable>
);
}
function App() {
const [parents, setParents] = useState([]);
const [w, setW] = useState(200)
const handleAddParent = () => {
setParents([ <ParentDraggable w = {w}>
{parents}
</ParentDraggable>]);
setW(prev => prev + 200)
};
return (
<div>
<button onClick={handleAddParent}>AddParent</button>
{parents}
</div>
);
}
export default App;
If you want to check code in codesandbox
Please help me to achieve my goal

html canvas not resizing properly after screen rotation on mobile website

I am working on an html canvas for the frist time I am using gatsby to develop my project
my problem is that my html canvas is cut when there is a rotation I seem to have a problem with the width and the height but I can't seem to understand what the problem is
here are some pictures of the problem :
without rotation
after the first rotation
after the second rotation
and here is the part of my code where I handle my canvas
Thank you for your help I am just a beginner so I am sorry if my code is not really clean
function MainScratch () {
let canvas = useRef(null)
function getSize() {
const isBrowser = typeof window !== 'undefined'
if (isBrowser) return {
width: window.innerWidth,
height: window.innerHeight,
}
else return {
width: 5,
height: 5,
}
}
const [windowSize, setWindowSize] = useState(getSize)
useEffect (() => {
function handleResize() {
setWindowSize(getSize())
}
window.addEventListener("resize", handleResize)
return () => window.removeEventListener("resize", handleResize)
},[]);
useEffect(() => {
let renderingElement = canvas.current
let drawingElement = renderingElement.cloneNode()
let drawingCtx = drawingElement.getContext('2d')
let renderingCtx = renderingElement.getContext('2d')
let lastX
let lastY
let moving = false
renderingCtx.globalCompositeOperation = "source-over"
renderingCtx.fillStyle = '#000000'
renderingCtx.fillRect(0, 0, windowSize.width, windowSize.height)
function reload()
{
renderingCtx.clearRect(0, 0, drawingElement.width, drawingElement.height);
drawingCtx.clearRect(0, 0, renderingElement.width, renderingElement.height);
renderingCtx.fillRect(0, 0, windowSize.width, windowSize.height)
}
window.addEventListener("resize", reload)
window.addEventListener("orientationchange", reload)
renderingElement.addEventListener('touchstart', e => {
moving = true
lastX = e.touches[0].pageX - renderingElement.offsetLeft
lastY = e.touches[0].pageY - renderingElement.offsetTop
e.preventDefault();
})
renderingElement.addEventListener('touchend', e => {
moving = false
lastX = e.lastX - renderingElement.offsetLeft
lastY = e.lastY - renderingElement.offsetTop
})
renderingElement.addEventListener('touchmove', e => {
moving = true
if (moving) {
drawingCtx.globalCompositeOperation = "source-over"
renderingCtx.globalCompositeOperation = "destination-out"
let currentX = e.touches[0].pageX - renderingElement.offsetLeft
let currentY = e.touches[0].pageY - renderingElement.offsetTop
drawingCtx.beginPath();
drawingCtx.lineJoin = "round"
drawingCtx.moveTo(lastX, lastY)
drawingCtx.lineTo(currentX, currentY)
drawingCtx.closePath()
drawingCtx.lineWidth = 80;
drawingCtx.stroke()
lastX = currentX
lastY = currentY
renderingCtx.drawImage(drawingElement, 0, 0)
}
e.preventDefault();
})
}, []);
return (
<Banner>
<Canvas
id="canvas"
height={windowSize.height}
width={windowSize.width}
ref={canvas}
/>
</Banner>
)

How I could modify a image by zooming in and out?

I have a gallery-like component, and the important part is:
<Gallery>
<Header>
<img src={galleryIcon} alt='Galley icon' />
<h1>My Gallery</h1>
</Header>
<Images isImage={custom.length > 0}>
{custom.length > 0 &&
custom.map((c, i) => (
<img
id={`custom${i}`}
key={`custom${i}`}
src={c.img}
alt='brick'
onClick={() => (setEdit((prev) => !prev), setActual(c))}
/>
))}
<div className='add'>
<button onClick={() => setGallery((prev) => !prev)}>
<img src={add} alt='Add icon' />
</button>
<p>Click to add a brick from our collections!</p>
</div>
</Images>
</Gallery>
Each image have this style:
img {
box-sizing: border-box;
height: 28vh;
width: 28vh;
margin-right: 1.5rem;
cursor: pointer;
}
Also, once the user input a new image I resize to no break the scale, with this function:
export function resizeImage(
file: File | Blob,
maxWidth: number,
maxHeight: number,
scale = 1
): Promise<Blob> {
return new Promise<Blob>((fulfill, reject) => {
const image = new Image();
image.src = URL.createObjectURL(file);
image.onload = () => {
URL.revokeObjectURL(image.src);
const width = image.width;
const height = image.height;
if (width <= maxWidth && height <= maxHeight) {
fulfill(file);
}
let newWidth = 0;
let newHeight = 0;
if (scale !== 1) {
newWidth = (width * scale) / maxWidth;
newHeight = (height * scale) / maxHeight;
} else if (width > height) {
newHeight = height * (maxWidth / width);
newWidth = maxWidth;
} else {
newWidth = width * (maxHeight / height);
newHeight = maxHeight;
}
console.log(newWidth, newHeight);
const canvas = document.createElement('canvas');
canvas.width = newWidth;
canvas.height = newHeight;
const context = canvas.getContext('2d');
context?.drawImage(image, 0, 0, newWidth, newHeight);
canvas.toBlob((blob) => blob && fulfill(blob), file.type);
};
image.onerror = reject;
});
}
And finally, the Resize component:
const Resize: React.FC<Props> = ({ actual, setResize, setCustom, custom }) => {
let stats: Stats = {} as Stats;
const getStats = useCallback((): Stats => stats, []);
const updateStats = useCallback(
(newStats: Stats): Stats => (stats = newStats),
[]
);
const getResizedCustom = useCallback(
async (copy: Custom): Promise<Custom> => {
const actualWidth = window.innerWidth;
const actualHeight = window.innerHeight;
const maxWidth = actualWidth < 1152 ? 180 : 360;
const maxHeight = actualHeight < 800 ? 180 : 360;
const newBlob = await resizeImage(
copy.blob,
maxWidth,
maxHeight,
getStats().scale
);
return {
blob: newBlob,
img: URL.createObjectURL(newBlob),
id: copy.id,
type: 'custom',
price: copy.price,
amount: copy.amount,
};
},
[stats]
);
const updateActual = useCallback(async () => {
// remove actual
const newCustomArr = [...custom];
const customCopy = newCustomArr.splice(newCustomArr.indexOf(actual), 1)[0];
const newCustom = await getResizedCustom(customCopy);
console.log(customCopy);
console.log(newCustom);
setCustom([...newCustomArr, newCustom]);
}, [actual, custom, setCustom, getResizedCustom]);
return (
<Container>
<Header>
<h1>ADJUST YOUR BRICK</h1>
<img
src={close}
alt='close icon'
onClick={() => setResize((prev) => !prev)}
/>
</Header>
<Main>
<h2>Pinch and zoom</h2>
<TransformWrapper onZoomChange={updateStats}>
<TransformComponent>
<img src={actual.img} alt='brick' />
</TransformComponent>
</TransformWrapper>
<button
onClick={async () => (
await updateActual(), setResize((prev) => !prev)
)}
>
DONE
</button>
</Main>
</Container>
);
};
I'm also using the react-zoom-pan-pinch to have the ability to zoom in and out the image. My question is: How I could resize the image on the DOM, based on the scale provided by the onZoomChange function from TransformWrapper component? Is that possible? There's a better and less "hacky" way to resize a image on DOM based on a zoom scale?
I'll try to provide a minimum repo soon, but for now, here's the full repo: https://github.com/Mdsp9070/brickart/tree/dev
It is for sure possible, but you need to be passing more props to drawImage. Check out the docs. It has four more optional arguments that you're not using. If you just use the first four then the whole image is included. When using all eight props, the first four refer to what portion of the original image to use (the cropping) while the next four specify where to put that selection of the the image on your canvas.
You need to do some math and some event tracking to calculate the source rectangle based on the center of the zoom and the scale.

Unable to draw bounding box on detected face in React-Native

I am trying to draw bounding boxes on a face but there is some issue with the CSS which shows the following error :
[269,"RCTView",281,{"position":"absolute","borderWidth":2,"borderColor":-65536,"left":"<<NaN>>","top":"<<NaN>>","width":"<<NaN>>","height":"<<NaN>>"}] is not usable as a native method argument
Style for bounding box where the error is coming from:
bbox: {
position: "absolute",
borderWidth: 2,
borderColor: "red"
},
Function to render the Bounding Box on face:
const previewLeft = 40;
const previewTop = 50;
const previewWidth = 350;
const previewHeight = 600;
const tensorDims = { height: 224, width: 224, depth: 3 };
const renderBoundingBoxes = () => {
const { faces } = modelFaces;
const tensorDims = { height: 300, width: 400 };
const scale = {
height: styles.camera.height / tensorDims.height,
width: styles.camera.width / tensorDims.width
};
const flipHorizontal = Platform.OS === "ios" ? false : true;
if (!isEmpty(faces)) {
return faces.map((face, i) => {
const { topLeft, bottomRight } = face;
const bbLeft = topLeft[0] * scale.width;
const boxStyle = Object.assign({}, styles.bbox, {
left: flipHorizontal
? previewWidth - bbLeft - previewLeft
: bbLeft + previewLeft,
top: topLeft[1] * scale.height + 20,
width: (bottomRight[0] - topLeft[0]) * scale.width,
height: (bottomRight[1] - topLeft[1]) * scale.height
});
return <View style={boxStyle} key={`face${i}`}></View>;
1;
});
}
};
RenderBoundingBoxes being called:
<View>
<TensorCamera
style={styles.camera}
type={type}
cameraTextureHeight={textureDims.height}
cameraTextureWidth={textureDims.width}
resizeHeight={tensorDims.height}
resizeWidth={tensorDims.width}
resizeDepth={tensorDims.depth}
onReady={images => handleCameraStream(images)}
autorender={AUTORENDER}
/>
{renderBoundingBoxes()}
</View>
I made a mistake, bottomRight and topLeft are not arrays but tensor objects instead.
I needed to download the tensors using tf.dataSync() first then access the arrays within them:
const renderBoundingBoxes = () => {
const { faces } = modelFaces;
const scale = {
height: styles.camera.height / tensorDims.height,
width: styles.camera.width / tensorDims.width
};
const flipHorizontal = Platform.OS === "ios" ? false : true;
if (!isEmpty(faces)) {
return faces.map((face, i) => {
const { topLeft, bottomRight } = face;
const bbLeft = topLeft.dataSync()[0] * scale.width;
const boxStyle = Object.assign({}, styles.bbox, {
left: flipHorizontal
? previewWidth - bbLeft - previewLeft
: bbLeft + previewLeft,
top: topLeft.dataSync()[1] * scale.height + 20,
width:
(bottomRight.dataSync()[0] - topLeft.dataSync()[0]) * scale.width,
height:
(bottomRight.dataSync()[1] - topLeft.dataSync()[1]) * scale.height
});
return <View style={boxStyle}></View>;
1;
});
}
};

Not able to move div using mousemove in react

I am trying to move a div with the help of mousemove event.
Here is the code for the same.
https://codepen.io/anurag92/pen/VEoQOZ
class ImageMarker extends React.Component {
constructor(props) {
super(props);
this.mouseDown = this.mouseDown.bind(this);
this.mouseUp = this.mouseUp.bind(this);
this.mouseMove = this.mouseMove.bind(this);
this.paint = this.paint.bind(this);
}
mouseDown(e) {
const position = {
left: this.marker.offsetLeft,
top: this.marker.offsetTop
};
this.hitOffset = {
x: e.pageX - position.left,
y: e.pageY - position.top,
diameter: this.diameter(),
markerRadius: 10
};
this.marker.addEventListener('mousemove', this.mouseMove);
this.marker.addEventListener('mouseup', this.mouseUp);
this.marker.addEventListener('mouseleave', this.mouseUp);
e.preventDefault();
}
mouseMove(e) {
this.position = {
x: e.pageX - this.hitOffset.x,
y: e.pageY - this.hitOffset.y
};
this.position.x = Math.round(this.position.x);
this.position.y = Math.round(this.position.y);
this.position.x = Math.min(700 - 1, Math.max(0, this.position.x));
this.position.y = Math.min(700 - 1, Math.max(0, this.position.y));
this.paint();
}
mouseUp(e) {
this.marker.removeEventListener('mousemove', this.mouseMove);
this.marker.removeEventListener('mouseup', this.mouseUp);
this.marker.removeEventListener('mouseleave', this.mouseUp);
}
diameter() {
return 1;
}
paint() {
if (JSON.stringify(this.paintedPosition) !== JSON.stringify(this.position)) {
this.paintedPosition = Object.assign({}, this.position);
}
if (this.position) {
this.marker.style.left = `${100 * this.position.x / 700}%`;
this.marker.style.top = `${100 * this.position.y / 700}%`;
}
return this;
}
render() {
this.position = this.position || {
x: 5,
y: 5
};
this.offset = 0;
return <div className='outer'
ref = {ref => {
this.canvasRef = ref;
}}
>
<div className = 'marker'
onMouseDown = {event => this.mouseDown(event)}
ref = {ref => {
this.marker = ref;
}} >
</div>
</div>;
}
}
// export default ImageMarker;
ReactDOM.render(<ImageMarker />,
document.getElementById('root')
);
When i move my cursor slowly its working fine, but on fast movement mouseleave gets triggered and as a result div is not able to keep up with the cursor.
Can someone please tell me a potential fix for this.
You could resolve that by attaching the mouseMove (and mouseUp)to the whole document This way they will be fired no matter if the mouse gets out of the element you want to drag.
Just remember to detach the event during componentWillUnmount to avoid leaks.
Further more if you want you site to work on mobile you need to attach touch, pointer or drag events. See the code of the kendo-draggable abstraction for a reference. We are using it in our react components. Bind to the element on ComponentDidMount and detach on ComponentWillUnmount

Categories

Resources