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
Related
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.
I am trying to copy an open curtain animation like in the following where the side divs expand horizontally on the background image on scroll.
I have a working example with the following code
import { useState, useEffect, useRef, useCallback } from "react";
import "./styles.css";
export default function App() {
// check window scroll direction
const [y, setY] = useState(null);
const [scrollDirection, setScrollDirection] = useState("");
const boxTwo = useRef(null);
const boxTwoLeft = useRef(null);
const boxTwoRight = useRef(null);
const countRefTranslateX = useRef(0);
// check window scroll direction https://stackoverflow.com/questions/62497110/detect-scroll-direction-in-react-js
const handleScrollDirection = useCallback(
(e) => {
const window = e.currentTarget;
if (y > window.scrollY) {
setScrollDirection("up");
} else if (y < window.scrollY) {
setScrollDirection("down");
}
setY(window.scrollY);
},
[y]
);
const handleScroll = useCallback(() => {
if (boxTwo.current) {
let position = boxTwo.current.getBoundingClientRect();
// checking for partial visibility and if 50 pixels of the element is visible in viewport
if (
position.top + 50 < window.innerHeight &&
position.bottom >= 0 &&
scrollDirection === "down"
) {
countRefTranslateX.current = countRefTranslateX.current + 3;
boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
} else if (
position.top + 50 < window.innerHeight &&
position.bottom >= 0 &&
scrollDirection === "up"
) {
countRefTranslateX.current = countRefTranslateX.current - 3;
boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
} else {
countRefTranslateX.current = 0;
boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
}
}
}, [scrollDirection]);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [handleScroll]);
useEffect(() => {
setY(window.scrollY);
window.addEventListener("scroll", handleScrollDirection);
return () => {
window.removeEventListener("scroll", handleScrollDirection);
};
}, [handleScrollDirection]);
return (
<div className="App">
<div className="boxOne"></div>
<div ref={boxTwo} className="boxTwo">
<div ref={boxTwoLeft} className="boxTwoLeft"></div>
<div ref={boxTwoRight} className="boxTwoRight"></div>
</div>
<div className="boxThree"></div>
</div>
);
}
I have two issues.
My right white div keeps going to the left while I scroll up
I cannot get a fixed vertical window breakpoint. My animation continues after the window has scrolled after the point I want to start/stop moving the divs
How can I resolve these issues?
My codesandbox
I've looked over what you've done but it's way too complicated.
All you want is to place two curtains (panels) on top of your content.
The container should have position:relative and the curtains should have position: absolute.
You then declare the scrollLimits in between which they should move. In the example below, 0 is the starting point and window.innerHeight the end point. Replace those values with whatever makes sense for your section, considering its vertical position in the page. You could use the section's current offsetTop and clientHeight to set the limits dynamically, based on current window size.
You then get the current scroll position and calculate the scroll percentage relative to the limits.
You then apply the percentage/2 + 50% to each curtain's transform: translateX() the left one with negative value, the right one with positive value.
Done.
document.addEventListener('scroll', revealSection);
/* function revealSection() {
const scrollLimits = [0, window.innerHeight];
const currentScroll = window.scrollY;
const percent = Math.min(currentScroll * 100 / (scrollLimits[1] - scrollLimits[0]), 100);
document.querySelector('.l-panel').style.transform = `translateX(-${percent/2 + 50}%)`;
document.querySelector('.r-panel').style.transform = `translateX(${percent/2 + 50}%)`;
} */
function revealSection() {
const top = document.querySelector('section').getBoundingClientRect().top;
const percent = Math.min(Math.max(0, (window.innerHeight - top) / window.innerHeight * 100), 100);
document.querySelector('.l-panel').style.transform = `translateX(-${percent/2 + 50}%)`;
document.querySelector('.r-panel').style.transform = `translateX(${percent/2 + 50}%)`;
}
body {
margin: 0;
}
section {
background-image: url('https://static.toss.im/assets/homepage/new tossim/section2_4_big.jpg');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
width: 100%;
height: 100vh;
margin: 100vh 0;
position: relative;
overflow: hidden;
}
.l-panel, .r-panel{
position: absolute;
height: 100%;
width: 100%;
content: '';
top: 0;
background-color: white;
left: 0;
}
<section>
<div class="l-panel"></div>
<div class="r-panel"></div>
</section>
Note: change the CSS selectors so the styles apply to your section only.
Edit: I've come up with a more generic way to set the scroll interval, using the section's getBoundingClientRect().top and window.innerHeight. It's probably more useful, as you no longer have to worry about the section's position in page.
I've left the previous version in, for reference and/or for anyone who prefers it.
I want to draw rectangles over texts or paragraphs in a HTML file. I want to achieve this using javascript without the use of canvas. Multiple rectangles can also exist for selecting multiple paragraphs. Is there any library for achieving this kind of functionality.
Click here to see the intended result
page Ruler link here https://chrome.google.com/webstore/detail/page-ruler-redux/giejhjebcalaheckengmchjekofhhmal?hl=en-US
You can use the absolute positioned div element to draw a rectangle over the web page. You need to listen for events when the user presses the mouse button (mousedown), when the user moves the mouse (mousemove), and when the user releases the button (mouseup). Here is a simple example:
const rectangle = document.createElement("div");
rectangle.style.position = "absolute";
rectangle.style.backgroundColor = "rgba(204,230,255, 0.7)";
rectangle.style.border = "1px dashed black";
document.body.appendChild(rectangle);
let isDragged = false;
let rectangleCoords = [];
const clearRectangleCoords = () => {
rectangleCoords = [];
};
const addFirstRectangleCoords = coords => {
rectangleCoords[0] = coords;
};
const addSecondRectangleCoords = coords => {
rectangleCoords[1] = coords;
};
const redrawRectangle = () => {
const top = Math.min(rectangleCoords[0].y, rectangleCoords[1].y);
const height = Math.max(rectangleCoords[0].y, rectangleCoords[1].y) - top;
const left = Math.min(rectangleCoords[0].x, rectangleCoords[1].x);
const width = Math.max(rectangleCoords[0].x, rectangleCoords[1].x) - left;
rectangle.style.top = top + "px";
rectangle.style.height = height + "px";
rectangle.style.left = left + "px";
rectangle.style.width = width + "px";
};
window.addEventListener("mousedown", e => {
isDragged = true;
clearRectangleCoords();
addFirstRectangleCoords({x: e.pageX, y: e.pageY});
addSecondRectangleCoords({x: e.pageX, y: e.pageY});
redrawRectangle();
});
window.addEventListener("mousemove", e => {
if (isDragged) {
addSecondRectangleCoords({x: e.pageX, y: e.pageY});
redrawRectangle();
}
});
window.addEventListener("mouseup", e => {
if (isDragged) {
addSecondRectangleCoords({x: e.pageX, y: e.pageY});
redrawRectangle();
isDragged = false;
}
});
You can play with the code on JSFiddle
If you want to highlight pure p elements, you can manipulate the backgroundColor property of the paragraphs, but you'd have to make sure they're displayed inline-block.
document.getElementById('bttn').addEventListener('click', highlight);
function highlight() {
var p = document.getElementById('target');
p.style.backgroundColor = '#ff0000'; //change background color
}
<!--make sure the paragraph is displayed inline-block, or else the entire line will be highlighted. -->
<p id="target" style="display:inline-block">This is a paragraph.</p>
<p>This is another paragraph.</p>
<button id="bttn">Click here to highlight</button>
I am implementing Draw function, to draw a line in canvas. The Draw function is activated on the first double click and on mouse move the line is being drawn and on the second double click, the draw function is deactivated.
When I double click the third time the previously drawn line is disappearing. My requirement is I want to retain all of the lines. How do I do that?
Below is my draw function:
handleMouseMove(event){
if(this.state.isDouble)
{
this.Draw(event)
}
else{
}
}
Draw(event){
x2=event.offsetX
y2=event.offsetY
const canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth = 3;
//Deleting The State
ctx.canvas.width = ctx.canvas.width;
console.log("First" + this.state.previousPointX,this.state.previousPointY)
console.log("Second" + x2,y2)
xtemp=x2
ytemp=this.state.previousPointY
console.log("Temp" + xtemp,ytemp)
ctx.beginPath()
ctx.moveTo(xtemp,ytemp);
ctx.lineTo(x2,y2);
ctx.stroke();
ctx.moveTo(xtemp,ytemp);
ctx.lineTo(this.state.previousPointX,this.state.previousPointY);
ctx.strokeStyle = "black";
ctx.stroke();
}
I have read here that you can save the canvas by converting it to json
for Redux store, I have tested using state for my Fabric canvas instance, its a 3rd party library that is a wrapper on a HTML canvas DOM element.
import React, { useEffect, useState } from 'react';
import './WallToDecorate.css';
import { fabric } from 'fabric';
const WallToDecorate = ({templateList=[]}) => {
const [wallCanvas, setCanvas] = useState();
useEffect(() => {
const canvas = new fabric.Canvas('fabric-canvas-to-decorate', {
width: rect.width,
height: rect.height
});
canvas.setBackgroundImage(
originalWallImage.src,
canvas.renderAll.bind(canvas),
{
backgroundImageOpacity: 1,
backgroundImageStretch: false,
top: top,
left: left,
}
);
setCanvas(canvas);
}, []);
const addImageToCanvas = (event) => {
fabric.util.loadImage(
event.target.src,
function (img) {
var fab_image = new fabric.Image(img, {
});
wallCanvas.add(fab_image);
wallCanvas.renderAll();
},
{ left: 100, top: 100, angle: 0, opacity: 1 }
);
};
return (
<>
<div id="select-image-dimensions" className="placeholder canvas-size">
<canvas id="fabric-canvas-to-decorate" />
</div>
</>
);
}
export default WallToDecorate;
In my case the wallCanvas.renderAll() refreshes the canvas element.
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