I'm creating my portfolio website, and the concept is to create a cube that people can interact with. It is possible to move the cube by clicking and dragging the mouse on the cube/screen. However, I'ld like to be able to "lock" the cube, by using a useState (true/false).
Literally, if the cube "isLocked", then we shouldnt be able to use the function within the useEffect hook. Here is a part of my Cube component so far:
export default function Cube(props) {
const [isLocked, setIsLocked] = useState(false);
useEffect(() => {
if (!isLocked) {
rotateCube(document.getElementById("cube"));
} else {
return;
}
}, [isLocked]);
// Rotate function :
function rotateCube(cube) {
var mouseX0 = 0,
mouseY0 = 0,
mouseX1 = 0,
mouseY1 = 0;
cube.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
mouseX0 = e.clientX;
mouseY0 = e.clientY;
document.onmouseup = closeDragElement;
...
}
Can put check inside dragMouseDown function
export default function Cube(props) {
const [isLocked, setIsLocked] = useState(false);
// executed once on load
useEffect(() => {
rotateCube(document.getElementById("cube"));
}, []);
// Rotate function :
function rotateCube(cube) {
var mouseX0 = 0,
mouseY0 = 0,
mouseX1 = 0,
mouseY1 = 0;
cube.onmousedown = dragMouseDown;
function dragMouseDown(e) {
// here the check
if (isLocked) { return false }
e = e || window.event;
e.preventDefault();
mouseX0 = e.clientX;
mouseY0 = e.clientY;
document.onmouseup = closeDragElement;
...
...
...
}
I wonder why you don't use JSX for handling events e.g. <Cube onMouseDown={onMouseDown} /> ?
Thank you for your answers !
I could actually find out what was happening. As you guys told me, I had to implement the isLocked within the rotateCube function, and place this entire function in the useEffect. Also, I forgot the place isLocked in the dependency array.
Here is the code :
useEffect(() => {
rotateCube(document.getElementById("cube"));
// Rotate function :
function rotateCube(cube) {
var mouseX0 = 0,
mouseY0 = 0,
mouseX1 = 0,
mouseY1 = 0;
if (isLocked === true) {
cube.onmousedown = closeDragElement;
}
if (isLocked === false) {
cube.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
mouseX0 = e.clientX;
mouseY0 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// Get new cursor position :
mouseX1 = e.clientX - mouseX0;
mouseY1 = (e.clientY - mouseY0) * -1;
//Set the new cube position :
cube.style.transform =
"rotateX(" + mouseY1 / 2 + "deg) rotateY(" + mouseX1 / 2 + "deg)";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
}, [isLocked]);
Related
I have built out basic drag and drop feature that allows us to drag randomly placed absolute positioned elements within a container. Ideally I would want to push other elements that possibly intersect the dragged elements boundaries away, but I am presently unable find a solution.
I have this for the Drag and Drop Solution:
var draggableElements = document.getElementsByClassName("team_member");
for(var i = 0; i < draggableElements.length; i++){
dragElement(draggableElements[i]);
}
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
pos3 = parseInt(e.clientX);
pos4 = parseInt(e.clientY);
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
return false;
}
function elementDrag(e) {
e = e || window.event;
pos1 = pos3 - parseInt(e.clientX);
pos2 = pos4 - parseInt(e.clientY);
pos3 = parseInt(e.clientX);
pos4 = parseInt(e.clientY);
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
elmnt.classList.add("drag");
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
elmnt.classList.remove("drag");
}
}
This is a cool task to implement, I played a bit and came up with something like this, but for some reason it's a big buggy, my idea was to use the API : document.elementFromPoint() to get the intersecting element during the drag. It seems to work but it's a bit clumsy and sometimes it fails.
Probably it would work better with an IntersectionObserver, that could also make sure to handle multiple boundary drag ( for example if you have 3+ objects, object one will push object 2 that will push object 3, object 3 has to react ).
const draggableElements = document.querySelectorAll(".team_member");
draggableElements.forEach((el, i) => makeDraggable(el, i))
function makeDraggable(elem, idx) {
elem.onmousedown = dragMouseDown;
elem.position = {
x: 0,
y: 0,
prevX: 0,
prevY: 0
}
elem.style.left = `calc(35% + ${(idx* 60) + "px"})`
}
// ----- //
function dragMouseDown(e) {
e = e || window.event;
this.position.prevX = +(e.clientX);
this.position.prevY = +(e.clientY);
document.onmouseup = closeDragElement;
document.onmousemove = (e) => elementDrag(e, this);
return false;
}
function elementDrag(e, t) {
t.position.x = t.position.prevX - +(e.clientX);
t.position.y = t.position.prevY - +(e.clientY);
t.position.prevX = +(e.clientX);
t.position.prevY = +(e.clientY);
t.style.top = (t.offsetTop - t.position.y) + "px";
t.style.left = (t.offsetLeft - t.position.x) + "px";
t.classList.add("drag");
// Check intersection
const width = t.getBoundingClientRect().width
const height = t.getBoundingClientRect().height
const intersectionElemTop = document.elementFromPoint(e.clientX, e.clientY - height / 2)
const intersectionElemRight = document.elementFromPoint(e.clientX + width / 2, e.clientY)
const intersectionElemBottom = document.elementFromPoint(e.clientX, e.clientY + height / 2)
const intersectionElemLeft = document.elementFromPoint(e.clientX - width / 2, e.clientY)
const intersections = [{
type: "top",
node: intersectionElemTop
}, {
type: "right",
node: intersectionElemRight
}, {
type: "bottom",
node: intersectionElemBottom
}, {
type: "left",
node: intersectionElemLeft
}].filter(i => [...draggableElements].some(d => i.node === d && d !== t))
if (intersections[0]) {
const i = intersections[0]
switch (i.type) {
case "top":
{
i.node.style.top = t.getBoundingClientRect().y - height + "px";
}
break;
case "right":
{
i.node.style.left = t.getBoundingClientRect().x + width + "px";
}
break;
case "bottom":
{
i.node.style.top = t.getBoundingClientRect().y + height + "px";
}
break;
case "left":
{
i.node.style.left = t.getBoundingClientRect().x - width + "px";
}
break;
}
i.node.classList.add("dragged");
clearTimeout(i.timeout)
i.timeout = setTimeout(() => i.node.classList.remove("dragged"), 500);
}
}
function closeDragElement(e) {
document.onmouseup = null;
document.onmousemove = null;
e.target.classList.remove("drag");
}
.team_member {
position: fixed;
width: 50px;
height: 50px;
border: 2px solid red;
display: flex;
justify-content: center;
align-items: center;
cursor: grab;
top: calc(50% - 25px);
}
.drag {
background: blue;
}
.dragged {
background: red;
}
<div class="team_member">
One
</div>
<div class="team_member">
Two
</div>
I want to create one image each time I click on a button and I will need to be able to drag this each image to move it where I want.
At this moment my code works only for one image.
I think I need to create my div in JS but this doesn't work..
const CreerPerso = document.getElementById('createPerso');
CreerPerso.onclick = CreationPersonnage;
async function CreationPersonnage() {
/*var divImg = document.createElement('div');
divImg.setAttribute("id", "imgPerso");
document.getElementById('body').appendChild(divImg);*/
document.getElementById("imgPerso").innerHTML = "<img src='images/circle.png' />";
//drag images Personnage
dragElement(document.getElementById("imgPerso"));
function dragElement(elmnt) {
var pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
if (document.getElementById(elmnt.id)) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id).onmousedown = dragMouseDown;
} else {
// otherwise, move the DIV from anywhere inside the DIV:
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
}
}
<body id="body">
<button id="createPerso" class="btn btn-primary">Créer Personnage</button>
<div id="imgPerso">
</div>
</body>
Id's in HTML need to be unique, so you should add a counter or something to the div you create and keep that id for future reference.
var personImageIdCount = 0;
async function CreationPersonnage() {
var personImageId = "imgPerso_" + (personImageIdCount++).toString();
var divImg = document.createElement('div');
divImg.setAttribute("id", personImageId);
document.getElementById('body').appendChild(divImg);
No need for async
No need to give body an ID
You pass the element, no need to get the ID to get the element
IDs need to be unique
This is simpler
function dragElement(elmnt) {
elmnt.dataset.pos1 = 0,
elmnt.dataset.pos2 = 0,
elmnt.dataset.pos3 = 0,
elmnt.dataset.pos4 = 0;
// if present, the header is where you move the DIV from:
elmnt.addEventListener("mousedown", dragMouseDown);
}
function dragMouseDown(e) {
e.preventDefault();
const elemt = e.target;
// get the mouse cursor position at startup:
elemt.dataset.pos3 = e.clientX;
elemt.dataset.pos4 = e.clientY;
}
function elementDrag(e) {
e.preventDefault();
const elemt = e.target;
if (elemt) {
// calculate the new cursor position:
let pos1 = elemt.dataset.pos3 - elemt.clientX;
let pos2 = elemt.dataset.pos4 - elemt.clientY;
let = elemt.clientX;
let = elemt.clientY;
// set the element's new position:
elemt.style.top = (elemt.offsetTop - pos2) + "px";
elemt.style.left = (elemt.offsetLeft - pos1) + "px";
}
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
const CreerPerso = document.getElementById('createPerso');
const body = document.querySelector('body');
CreerPerso.addEventListener("click", CreationPersonnage);
function CreationPersonnage() {
const divImg = document.createElement('div');
divImg.classList.add("imgPerso");
divImg.innerHTML = "<img src='images/circle.png' />";
body.appendChild(divImg);
//drag images Personnage
dragElement(divImg);
}
document.addEventListener("mouseup", closeDragElement);
document.addEventListener("mousemove", elementDrag);
.imgPerso {
position: absolute
}
<button id="createPerso" class="btn btn-primary">Créer Personnage</button>
Hello I am tring to make a svg editor where I can create and drag the rectangles I create. Each time the mouseDown event gets fired I add 2 eventListeners to the svg element (mouseMove and mouseUp) and each time mouseUp gets called I try to remove these events, but because the remove doesn't work the element has multiple mouseMove and mouseUp listeners attached to it. I can't figure out what's wrong. MouseDown does 2 things, first it can draw the rectangles and then if createMode is false it let me drag them
this.domElement.addEventListener("mousedown", (event) => {
if (event.button === 0 && this.createMode === true) {
document.body.style.cursor = 'crosshair'
const rect = document.createElementNS(this.svgns, 'rect');
//rect.setAttribute('tabindex', "1");
let first_mouseX = event.clientX;
let first_mouseY = event.clientY;
const drawRect = (event) => {
let mouseX = event.clientX;
let mouseY = event.clientY;
const width = Math.abs(mouseX - first_mouseX);
const height = Math.abs(mouseY - first_mouseY);
if (mouseX > first_mouseX) {
mouseX = first_mouseX;
}
if (mouseY > first_mouseY) {
mouseY = first_mouseY;
}
rect.setAttribute('x', mouseX - this.domElement.getBoundingClientRect().left)
rect.setAttribute('y', mouseY - this.domElement.getBoundingClientRect().top);
rect.setAttribute('width', width);
rect.setAttribute('height', height);
rect.setAttribute('stroke', "black");
rect.setAttribute('fill', 'white');
rect.setAttribute('stroke-width', 2);
this.rect = rect;
this.domElement.appendChild(rect);
}
const endDraw = () => {
this.domElement.removeEventListener("mousemove", drawRect);
this.domElement.removeEventListener("mouseup", endDraw);
if (this.rect !== null) {
this.rect.addEventListener('click', () => {
this.createMode = false;
document.body.style.cursor = "auto"
event.target.focus()
})
this.rect.addEventListener('blur', () => {
this.stergeClick();
})
this.rect.addEventListener("focus", (event) => {
this.stergeClick();
this.adaugaClick(event.target)
})
this.rect.focus();
this.rect = null;
}
}
this.domElement.addEventListener("mouseup", endDraw)
this.domElement.addEventListener("mousemove", drawRect)
} else if (this.createMode === false) {
const startDrag = (event) => {
this.selectedItem = {}
this.selectedItem.item = event.target
this.selectedItem.offsetX = event.clientX - this.selectedItem.item.getAttribute('x');
this.selectedItem.offsetY = event.clientY - this.selectedItem.item.getAttribute('y');
}
startDrag(event)
const drag = (event) => {
if (Object.keys(this.selectedItem).length > 0 && this.createMode === false && this.selectedItem.item.nodeName !== "svg") {
this.stergeClick();
this.adaugaClick(this.selectedItem.item);
this.selectedItem.item.setAttribute('x', event.clientX - this.selectedItem.offsetX);
this.selectedItem.item.setAttribute('y', event.clientY - this.selectedItem.offsetY);
} else {
return false
}
}
const endDrag = (ev) => {
if (Object.keys(this.selectedItem).length > 0 && this.selectedItem.item.nodeName !== "svg") {
this.stergeClick();
this.adaugaClick(this.selectedItem.item);
ev.target.removeEventListener('mouseup', drag);
ev.target.removeEventListener('mousemove', endDrag);
this.domElement.removeEventListener('mouseup', drag);
this.domElement.removeEventListener('mousemove', endDrag);
this.selectedItem = {};
}
}
this.domElement.addEventListener("mousemove", drag);
this.domElement.addEventListener("mouseup", endDrag);
}
})
}
Is there a way to stop users on dragging the mouse down so they only can click instead of click and drag. I want htem to be only to draw dots on the canvas or any pattern i define similar to a stamp tool.
function Sketcher( canvasID, brushImage ) {
this.renderFunction = (brushImage == null || brushImage == undefined) ? this.updateCanvasByLine : this.updateCanvasByBrush;
this.brush = brushImage;
this.touchSupported = Modernizr.touch;
this.canvasID = canvasID;
this.canvas = $("#"+canvasID);
this.context = this.canvas.get(0).getContext("2d");
this.context.strokeStyle = "#000000";
this.context.lineWidth = 3;
this.lastMousePoint = {x:0, y:0};
if (this.touchSupported) {
this.mouseDownEvent = "touchstart";
this.mouseMoveEvent = "touchmove";
this.mouseUpEvent = "touchend";
}
else {
this.mouseDownEvent = "mousedown";
this.mouseMoveEvent = "mousemove";
this.mouseUpEvent = "mouseup";
}
this.canvas.bind( this.mouseDownEvent, this.onCanvasMouseDown() );
}
Sketcher.prototype.onCanvasMouseDown = function () {
var self = this;
return function(event) {
self.mouseMoveHandler = self.onCanvasMouseMove()
self.mouseUpHandler = self.onCanvasMouseUp()
$(document).bind( self.mouseMoveEvent, self.mouseMoveHandler );
$(document).bind( self.mouseUpEvent, self.mouseUpHandler );
self.updateMousePosition( event );
self.renderFunction( event );
}
}
Sketcher.prototype.onCanvasMouseMove = function () {
var self = this;
return function(event) {
self.renderFunction( event );
event.preventDefault();
return false;
}
}
Sketcher.prototype.onCanvasMouseUp = function (event) {
var self = this;
return function(event) {
$(document).unbind( self.mouseMoveEvent, self.mouseMoveHandler );
$(document).unbind( self.mouseUpEvent, self.mouseUpHandler );
self.mouseMoveHandler = null;
self.mouseUpHandler = null;
}
}
Sketcher.prototype.updateMousePosition = function (event) {
var target;
if (this.touchSupported) {
target = event.originalEvent.touches[0]
}
else {
target = event;
}
var offset = this.canvas.offset();
this.lastMousePoint.x = target.pageX - offset.left;
this.lastMousePoint.y = target.pageY - offset.top;
}
Sketcher.prototype.updateCanvasByLine = function (event) {
this.context.beginPath();
this.context.moveTo( this.lastMousePoint.x, this.lastMousePoint.y );
this.updateMousePosition( event );
this.context.lineTo( this.lastMousePoint.x, this.lastMousePoint.y );
this.context.stroke();
}
Sketcher.prototype.updateCanvasByBrush = function (event) {
var halfBrushW = this.brush.width/2;
var halfBrushH = this.brush.height/2;
var start = { x:this.lastMousePoint.x, y: this.lastMousePoint.y };
this.updateMousePosition( event );
var end = { x:this.lastMousePoint.x, y: this.lastMousePoint.y };
var distance = parseInt( Trig.distanceBetween2Points( start, end ) );
var angle = Trig.angleBetween2Points( start, end );
var x,y;
for ( var z=0; (z<=distance || z==0); z++ )
{
x = start.x + (Math.sin(angle) * z) - halfBrushW;
y = start.y + (Math.cos(angle) * z) - halfBrushH;
//console.log( x, y, angle, z );
this.context.drawImage(this.brush, x, y);
}
}
Sketcher.prototype.toString = function () {
var dataString = this.canvas.get(0).toDataURL("image/png");
var index = dataString.indexOf( "," )+1;
dataString = dataString.substring( index );
return dataString;
}
Sketcher.prototype.toDataURL = function () {
var dataString = this.canvas.get(0).toDataURL("image/png");
return dataString;
}
Sketcher.prototype.clear = function () {
var c = this.canvas[0];
this.context.clearRect( 0, 0, c.width, c.height );
}
You can achieve this by simply ignoring the mousemove and mouseup event handlers by either not initializing them or using flags to indicate that you are in stamp mode.
All you need is:
canvas.onmousedown = function(e) {
var pos = getMousePos(e);
render(pos.x, pos.y);
}
That's it, and it works similar for touch events.
To use flag (I would not recommend adding and removing handlers all the time):
var isStampMode = true;
canvas.onmousemove = function(e) {
if (isStampMode) return;
...
}
and similar for mouse up (but not really necessary unless it depends on what you do in mousedown).
Live example here
Hope this helps!
i had a problem on confusing how to put ontouch inside my coding because i want it function on ipad, below is my code
<script language="javascript">
function sig1()
{
if(sig == null)
{
xclear('myCanvas');
}
}
code for clear the line in canvas
<body id="main_body" class="no_guidelines" onload="sig1()" onmouseup="bodyonmouseup">
to let the canvas had fillstyle color and function
<canvas border=1 id="myCanvas" height=200 width=400 onmousedown="onmousedown()" onmouseup="onmouseup()" onmousemove="onmousemove()">Your browser does not support the application</canvas>
canvas that can draw line like signature
var sig = null;
var ele;
function bodyonmouseup(e)
{
var ele;
ele = document.getElementById('myCanvas');
ele.isDown = false;
}
function onmousedown(e)
{
var ele,p;
if (!e) e = window.event;
ele = (e.srcElement);
if (! ele) ele = e.target;
ele.isDown = true;
ele.context = ele.getContext("2d");
ele.context.lineWidth = 1;
ele.context.beginPath();
ele.context.moveTo(e.offsetX,e.offsetY);
sig.line = new Array();
p = new Object();
p.x = e.offsetX;
p.y = e.offsetY;
sig.p = p;
sig.line[sig.line.length] = p;
sig.lines[sig.lines.length] = sig.line;
}
function onmousemove(e)
{
var ele,dx,dy,p;
if (!e) e = window.event;
ele = (e.srcElement);
if (! ele) ele = e.target;
if (! ele.isDown) return;
ele.context.lineTo(e.offsetX,e.offsetY);
ele.context.stroke();
dx = e.offsetX - sig.p.x;
dy = e.offsetY - sig.p.y;
if (dx == 0 && dy == 0) return;
sig.p = new Object();
sig.p.x = e.offsetX;
sig.p.y = e.offsetY;
sig.line[sig.line.length] = sig.p;
}
So i want to know if i want keep both onMouse and ontouch, how i put the ontouch so that the canvas can be draw on ipad?? please help, it is urgent
Use this js. Your finger will be your mouse. They are converting all touch events to mouse events.
jquery-ui-touch-punch