I looked for solutions to this problem for a long time, found some and I thought I understand but apparently I don't. I'd like to make a canvas that ppl can use to sign to confirm a booking. But I can't get the line to follow the user's cursor. My code looks like this:
HTML:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Signature Test</title>
<link rel="stylesheet" href="../css/test.css">
<script src="../js/jquery-3.2.0.js"></script>
</head>
<body>
<header id="header">
</header>
<section id="content">
<canvas id="signaturePad">DSorry your browser is rubbish</canvas>
</section>
<footer>
</footer>
<script src="../js/test.js"></script>
</body>
</html>
CSS:
body
{
margin: 0;
padding: 0;
}
header
{
border: 1px solid black;
width: 100%;
height: 50px;
}
#content
{
border: 1px solid blue;
width: 100%;
height: 300px;
position: relative;
}
#content #signaturePad
{
border: 1px solid red;
width: 200px;
height: 200px;
position: absolute;
left: calc(50% - 100px);
top: calc(50% - 100px);
}
footer
{
border: 1px solid black;
width: 100%;
height: 50px;
}
And finally the JS:
var canvas = document.getElementById("signaturePad");
var context = canvas.getContext("2d");
var radius = 1;
var dragging = false;
context.lineWidth = 2*radius;
function displayMousePosition(mouseX, mouseY) {
var header = document.getElementById("header");
header.innerHTML = "X : " + mouseX + "<br />Y : " + mouseY;
}
function getMousePosition(e) {
var mouseX = 0,
mouseY = 0,
elmX = 0,
elmY = 0,
obj = this;
//get mouse position on document crossbrowser
if (!e){e = window.event;}
if (e.pageX || e.pageY){
mouseX= e.pageX;
mouseY = e.pageY;
} else if (e.clientX || e.clientY){
mouseX = e.clientX + document.body.scrollLeft
+ document.documentElement.scrollLeft;
mouseY = e.clientY + document.body.scrollTop
+ document.documentElement.scrollTop;
}
//get parent element position in document
if (obj.offsetParent){
do{
elmX += obj.offsetLeft;
elmY += obj.offsetTop;
} while (obj = obj.offsetParent);
}
displayMousePosition(mouseX, mouseY);
return [mouseX, mouseY];
}
var putPoint = function(e) {
if(dragging) {
var offset = $("#signaturePad").offset();
var mouseX = getMousePosition(e)[0];
var mouseY = getMousePosition(e)[1];
context.lineTo(mouseX, mouseY);
context.stroke();
context.beginPath();
context.arc(mouseX, mouseY, radius, 0, Math.PI*2);
context.fill();
context.beginPath();
context.moveTo(mouseX, mouseY);
}
}
$(document).on("mousedown", "#signaturePad", function(e) {
dragging = true;
putPoint(e);
});
$(document).on("mouseup", "#signaturePad", function() {
dragging = false;
context.beginPath();
});
$(document).on("mousemove", "#signaturePad", function(e) {
putPoint(e);
});
And the fiddle if you wanna see it live: https://jsfiddle.net/Cellendhyll/yxguemf0/7/
Related
const canvas = document.getElementById('drawing-board');
const toolbar = document.getElementById('toolbar');
const ctx = canvas.getContext('2d');
const canvasOffsetX = canvas.offsetLeft;
const canvasOffsetY = canvas.offsetTop;
canvas.width = window.innerWidth - canvasOffsetX;
canvas.height = window.innerHeight - canvasOffsetY;
let isPainting = false;
let lineWidth = 5;
let startX;
let startY;
toolbar.addEventListener('click', e => {
if (e.target.id === 'clear') {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
toolbar.addEventListener('change', e => {
if(e.target.id === 'stroke') {
ctx.strokeStyle = e.target.value;
}
if(e.target.id === 'lineWidth') {
lineWidth = e.target.value;
}
});
const draw = (e) => {
if(!isPainting) {
return;
}
ctx.lineWidth = lineWidth;
ctx.lineCap = 'round';
ctx.lineTo(e.clientX - canvasOffsetX, e.clientY);
ctx.stroke();
}
canvas.addEventListener('mousedown', (e) => {
isPainting = true;
startX = e.clientX;
startY = e.clientY;
});
canvas.addEventListener('mouseup', e => {
isPainting = false;
ctx.stroke();
ctx.beginPath();
});
canvas.addEventListener('mousemove', draw);
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
color: white;
}
h1 {
background: #7F7FD5;
background: -webkit-linear-gradient(to right, #91EAE4, #86A8E7, #7F7FD5);
background: linear-gradient(to right, #91EAE4, #86A8E7, #7F7FD5);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.container {
height: 100%;
display: flex;
}
#toolbar {
display: flex;
flex-direction: column;
padding: 5px;
width: 70px;
background-color: #202020;
}
#toolbar * {
margin-bottom: 6px;
}
#toolbar label {
font-size: 12px;
}
#toolbar input {
width: 100%;
}
#toolbar button {
background-color: #1565c0;
border: none;
border-radius: 4px;
color:white;
padding: 2px;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Drawing app</title>
</head>
<body>
<section class="container">
<div id="toolbar">
<h1>Draw.</h1>
<label for="stroke">Stroke</label>
<input id="stroke" name='stroke' type="color">
<label for="lineWidth">Line Width</label>
<input id="lineWidth" name='lineWidth' type="number" value="5">
<button id="clear">Clear</button>
</div>
<div class="drawing-board">
<canvas id="drawing-board"></canvas>
</div>
</section>
<script src="./index.js"></script>
</body>
</html>
I am making a simple line drawer and this is what I have so far. One of the main features i need it to have is solid straight lines as this is for a bigger project in which this is meant to represent pipes. So the lines cannot bend or curve but must be how the code shows it to be. The issue i am having now is if i want to have the lines to stay on the canvas after another mouse down and click then the lines aren't straight and is almost a paint application. I have provided the code down below.
const canvasEle = document.getElementById('drawContainer');
const context = canvasEle.getContext('2d');
let startPosition = {x: 0, y: 0};
let lineCoordinates = {x: 0, y: 0};
let isDrawStart = false;
const getClientOffset = (event) => {
const {pageX, pageY} = event.touches ? event.touches[0] : event;
const x = pageX - canvasEle.offsetLeft;
const y = pageY - canvasEle.offsetTop;
return {
x,
y
}
}
const drawLine = () => {
if(!isDrawStart) {
return;
}
context.beginPath();
context.moveTo(startPosition.x, startPosition.y);
context.lineTo(lineCoordinates.x, lineCoordinates.y);
context.stroke();
}
const mouseDownListener = (event) => {
startPosition = getClientOffset(event);
isDrawStart = true;
}
const mouseMoveListener = (event) => {
if(!isDrawStart) return;
lineCoordinates = getClientOffset(event);
clearCanvas();
drawLine();
}
const mouseupListener = (event) => {
isDrawStart = false;
}
const clearCanvas = () => {
context.clearRect(0, 0, canvasEle.width, canvasEle.height);
}
canvasEle.addEventListener('mousedown', mouseDownListener);
canvasEle.addEventListener('mousemove', mouseMoveListener);
canvasEle.addEventListener('mouseup', mouseupListener);
canvasEle.addEventListener('touchstart', mouseDownListener);
canvasEle.addEventListener('touchmove', mouseMoveListener);
canvasEle.addEventListener('touchend', mouseupListener);
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
color: white;
}
h1 {
background: #7F7FD5;
background: -webkit-linear-gradient(to right, #91EAE4, #86A8E7, #7F7FD5);
background: linear-gradient(to right, #91EAE4, #86A8E7, #7F7FD5);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.container {
height: 100%;
display: flex;
}
#toolbar {
display: flex;
flex-direction: column;
padding: 5px;
width: 70px;
background-color: #202020;
}
#toolbar * {
margin-bottom: 6px;
}
#toolbar label {
font-size: 12px;
}
#toolbar input {
width: 100%;
}
#toolbar button {
background-color: #1565c0;
border: none;
border-radius: 4px;
color:white;
padding: 2px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<canvas id = "drawContainer" width = "500" height = "500" style = "border: 1px solid #333;"></canvas>
<script src="index.js"></script>
</body>
</html>
Adding line segments
I am guessing this is what you are after. A set of line segments added as the use clicks and drags (mouse or touch)
The simplest solution is to create an array of lines, adding lines to the array at the end of each click drag interaction.
Example
The example adds some functions to create points and lines, draw a line and lines from an array of lines.
const ctx = canvas.getContext('2d'), lines = [];
const Point = (x = 0, y = 0) => ({x,y});
const Line = (p1, p2) => ({p1, p2});
var currentLine = Line(Point(), Point()), addingLine = false;
["mousedown", "mousemove", "mouseup", "touchstart", "touchmove", "touchend"].forEach(name => canvas.addEventListener(name, mouseEvent));
function getClientOffset(event, point) {
event = event.touches ? event.touches[0] : event;
point.x = event.pageX - canvas.offsetLeft;
point.y = event.pageY - canvas.offsetTop;
}
function drawLine(line) {
ctx.moveTo(line.p1.x, line.p1.y);
ctx.lineTo(line.p2.x, line.p2.y);
}
function UpdateDisplay() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
for (const l of lines) { drawLine(l) }
addingLine && drawLine(currentLine);
ctx.stroke();
}
function mouseEvent(event) {
if (event.type === "mousedown" || event.type === "touchstart") {
getClientOffset(event, currentLine.p1);
getClientOffset(event, currentLine.p2);
addingLine = true;
UpdateDisplay();
} else if (event.type === "mouseup" || event.type === "touchend") {
lines.push(currentLine);
currentLine = Line(Point(), Point());
addingLine = false;
UpdateDisplay();
} else if(addingLine) {
getClientOffset(event, currentLine.p2);
UpdateDisplay();
}
}
<canvas id = "canvas" width = "500" height = "500" style = "border: 1px solid #333;"></canvas>
I am writing my own Drag'n'drop functionality for one of my projects and I am running into an issue. All my "draggable" elements are inside a container with display:flex. On mousedown event on one of this elements I set the position to absolute so I would be able to set the left and top properties of the element when I am dragging. Here is what I am doing:
let container = document.querySelector("#big-container")
var dragging = false;
var draggedObject;
let shiftX=0;
let shiftY=0;
document.querySelectorAll(".draggable").forEach((draggable,index) => {
draggable.style.order = index;
draggable.draggable =false;
draggable.ondragstart = ()=>{return false}
draggable.addEventListener("mousedown",ev =>{
draggedObject = draggable;
shiftX = ev.offsetX+5;
shiftY = ev.offsetY+5;
draggable.style.position = "absolute";
draggable.style.left = (ev.clientX - shiftX) + 'px';
draggable.style.top = (ev.clientY - shiftY) + 'px';
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
})
})
document.addEventListener("mousemove", ev =>{
if(dragging){
draggedObject.style.left = ev.clientX - shiftX + 'px';
draggedObject.style.top = ev.clientY - shiftY + 'px';
}
})
document.addEventListener("mouseup",ev =>{
if(dragging){
draggedObject.style.position = 'static'
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false
}
})
/* the :not(:last-of-type(div)) is there so the console doesn't get affected */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div id = "big-container" style ="display: flex; background-color: rgb(76, 104, 95); width: 500px; height: 500px;">
<div style="background-color: rgb(204, 125, 111);" class="draggable"></div>
<div style="background-color: rgb(170, 214, 120);" class="draggable"></div>
<div style="background-color: rgb(129, 212, 167);" class="draggable"></div>
<div style="background-color: rgb(162, 137, 196);" class="draggable"></div>
</div>
</body>
What I am trying to achieve is that on mousedown the element should stay where it was and after that when I move my mouse to move the element as well.(the anchor point should be where I clicked the element)
I am doing shiftX = ev.offsetX+5; because I need to account for the element's margin.
The issue is when I click on a element(and don't move my mouse at all), you can see a little shift in the element's position. It is very minor(maybe 1 or 2px) and is not happening in all places(some zones in the element do not introduce this position shift)
Do you guys have any idea what might be causing it?
You can use getBoundingClientRect() to get the actual position.
let container = document.querySelector("#big-container");
var dragging = false;
var draggedObject;
let shiftX = 0;
let shiftY = 0;
document.querySelectorAll(".draggable").forEach((draggable, index) => {
draggable.style.order = index;
draggable.draggable = false;
draggable.ondragstart = () => {
return false;
};
draggable.addEventListener("mousedown", (ev) => {
draggedObject = draggable;
var x = draggable.getBoundingClientRect().top - 5;
var y = draggable.getBoundingClientRect().left - 5;
shiftX = ev.offsetX + 5;
shiftY = ev.offsetY + 5;
draggable.style.position = "absolute";
draggable.style.left = y + "px";
draggable.style.top = x + "px";
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
});
});
document.addEventListener("mousemove", (ev) => {
if (dragging) {
draggedObject.style.left = ev.clientX - shiftX + "px";
draggedObject.style.top = ev.clientY - shiftY + "px";
}
});
document.addEventListener("mouseup", (ev) => {
if (dragging) {
draggedObject.style.position = "static";
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false;
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
#big-container {
width: 500px;
height: 500px;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div
id="big-container"
style="display: flex; background-color: rgb(76, 104, 95);"
>
<div
style="background-color: rgb(204, 125, 111);"
class="draggable"
></div>
<div
style="background-color: rgb(170, 214, 120);"
class="draggable"
></div>
<div
style="background-color: rgb(129, 212, 167);"
class="draggable"
></div>
<div
style="background-color: rgb(162, 137, 196);"
class="draggable"
></div>
</div>
</body>
I have some code that's not working on my site but works when I test it elsewhere. In addition, on my site elements are duplicating twice, in testing it doesn't. I've tried implementing through a text widget, then placing code on the actual page. The problem is
Duplicate elements
The words should be draggable but aren't.
Malfunctioning widget area
desktop
https://codepen.io/adsler/pen/bGEbzxP
var dragItem;
var container = document.querySelector("#container");
var active = false;
var currentX;
var currentY;
var initialX;
var initialY;
var xOffset = 0;
var yOffset = 0;
container.addEventListener("touchstart", dragStart, false);
container.addEventListener("touchend", dragEnd, false);
container.addEventListener("touchmove", drag, false);
container.addEventListener("mousedown", dragStart, false);
container.addEventListener("mouseup", dragEnd, false);
container.addEventListener("mousemove", drag, false);
function dragStart(e) {
dragItem = e.target;
if (dragItem.xOffset == undefined) {
dragItem.xOffset = 0;
}
if (dragItem.yOffset == undefined) {
dragItem.yOffset = 0;
}
if (e.type === "touchstart") {
initialX = e.touches[0].clientX - dragItem.xOffset;
initialY = e.touches[0].clientY - dragItem.yOffset;
} else {
initialX = e.clientX - (dragItem.xOffset || 0);
initialY = e.clientY - (dragItem.xOffset || 0);
}
console.log(initialX);
console.log(initialY);
// if (e.target === dragItem) {
active = true;
// }
}
function dragEnd(e) {
active = false;
}
function drag(e) {
if (active) {
e.preventDefault();
if (e.type === "touchmove") {
currentX = e.touches[0].clientX - initialX;
currentY = e.touches[0].clientY - initialY;
} else {
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
}
dragItem.xOffset = currentX;
dragItem.yOffset = currentY;
setTranslate(currentX, currentY, dragItem);
}
}
function setTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
#container {
width: 100%;
height: 400px;
background- color: white;
display: flex;
align- items: center;
justify- content: center;
overflow: hidden;
border- radius: 7px;
touch- action: none;
}
.container div {
width: 100px;
height: 0px;
background- color: transparent;
border: 0px solid rgba(136, 136, 136, 0.5);
border- radius: 50%;
touch- action: none;
user- select: none;
text-align: center;
}
.container div:active {
background- color: white;
}
.container div:hover {
cursor: pointer;
border- width: 0px;
}
#gd {
background-color: yellow;
}
#websites {
background-color: blue;
}
#Identity {
background-color: #ff0af3;
}
<div id="outerContainer">
<div id="container">
<div>
<div id="gd">Hello and</div>
<div>
<div id="websites">welcome</div>
<div>
<div id="Identity">to 4309</div>
</div>
</div>
</div>
</div>
</div>
Finally decided to implement the code on the home page (it's a WordPress site) and the widget area appears to be malfunctioning, but it's not showing up at all now. I have several containers so changed the container to container6. Didn't work. Tried removing DOCTYPE and html tags, as the page would already have DOCTYPE and I'd be calling it twice. Didn't work. Normally i implement home page changes through a text widget, but homepage code implementation should work also.
I'm starting to create a custom draggable, everything seems to be fine but the shapes still doesn't move on mousemove, I'm confused on what I could have done wrongly, any hint to the solution would be helpful.
I guess the problem occurs on the dragElement function, every numbers shows correctly without an error but still nothing moves, why ?
function startDrag(event) {
var move = $(this);
$(this).addClass('dragged-item');
position = move.position();
var lastOffset = move.data('lastTransform');
var lastOffsetX = lastOffset ? lastOffset.dx : position.left;
var lastOffsetY = lastOffset ? lastOffset.dy : position.top;
var startX = event.pageX - lastOffsetX;
var startY = event.pageY - lastOffsetY;
this.addEventListener('mousemove', dragElement(startX, startY, event, move));
}
function dragElement(startX, startY, event, move) {
var newDx = event.pageX - startX;
var newDy = event.pageY - startY;
move.css('transform', 'translate3d(' + newDx + 'px, ' + newDy + 'px, 0px)');
// we need to save last made offset
move.data('lastTransform', {
dx: newDx,
dy: newDy
});
}
var elements = document.getElementsByClassName('element');
for(var i = 0; i < elements.length; i++) {
var element= elements[i];
element.addEventListener('mousedown', startDrag);
}
.elements {
position: absolute;
overflow: hidden;
border: 3px solid #ebeced;
background-color: #fff;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
width: 300px;
height: 300px;
border: 1px solid blue;
}
.elements .element:hover {
cursor: pointer;
}
.elements .element {
position: absolute;
width: 50px;
height: 50px;
background: #4dadc9;
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<p>Not draggable yet... 0 error on console.</p>
<div class="elements">
<div class="element" style="transform:translate3d(0px, 0px, 0px);">1</div>
<div class="element" style="transform:translate3d(50px, 100px, 0px);">2</div>
<div class="element" style="transform:translate3d(110px, 150px, 0px);">3</div>
<div class="element" style="transform:translate3d(150px, 240px, 0px);">4</div>
</div>
Any suggestion on what is going wrong ?
I'm trying to drag a div when I click on it but when I do it the div blinks and moves to the left, if I remove offset and put position instead it works but the cursor goes to the left top of the div.
var selected = 0,
x = 0,
y = 0;
$.fn.isDraggable = function() {
$(this).on('mousedown', function(e) {
selected = $(this);
$(selected).css({
position: 'absolute',
left: e.pageX - $(selected).position().left,
top: e.pageY - $(selected).position().top
});
});
$(document).on('mouseup', function() {
if (selected !== 0) {
selected = 0;
}
});
$(document).bind('mousemove', function(e) {
$(selected).css({
position: 'absolute',
left: e.pageX - $(selected).offset().left,
top: e.pageY - $(selected).offset().top
});
});
return true;
};
$('#card').isDraggable();
#card {
position: fixed;
width: 100px;
height: 150px;
top: calc(50% - 75px);
left: calc(50% - 50px);
border: 1px solid #D3D3D3;
}
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<title>freeMarketRocks</title>
</head>
<body>
<div>
<div id="card">
</div>
</div>
</body>
</html>
You have 2 problems here. First your event handler logic might result in a performance waste as you are asking your browser to constantly check for mouse movement, even if its not necessary.
Second, the calculation of the box coordiante is wrong, it must take the initial position in account. That's the purpose of my deltaX and deltaY variables in the fiddle
Here's a working fiddle https://jsfiddle.net/TCHdevlp/t2bapq5y/
Or Here:
var selected = 0,
x = 0,
y = 0,
boxX = 0,
boxY = 0;
$.fn.isDraggable = function() {
$(this).on('mousedown', function(e) {
selected = $(this);
//get initial positions
x = e.pageX;
y = e.pageY;
BoxX = $(selected).offset().left;
BoxY = $(selected).offset().top;
//bind mousemove
$(document).bind('mousemove', function(e) {
//compute new coordinate
deltaX = e.pageX - x;
deltaY = e.pageY - y;
$(selected).css({
position: 'absolute',
left: (BoxX + deltaX),
top: (BoxY + deltaY)
});
});
});
//unbind when finished
$(document).on('mouseup', function() {
if (selected !== 0) {
$(document).unbind("mousemove");
selected = 0;
}
});
return true;
};
$('#card').isDraggable();
#card {
position: fixed;
width: 100px;
height: 150px;
top: 10x;
left: 10px;
border: 1px solid #D3D3D3;
}
<div>
<div id="card">
</div>
</div>
var selected = 0,
x = 0,
y = 0;
$.fn.isDraggable = function() {
var moveFrame, comStartX, comStartY, startMousePosX, startMousePosY;
$(this).on('mousedown', function(e) {
selected = $(this);
moveFrame = true;
comStartX = $(this).position().left;
comStartY = $(this).position().top;
startMousePosX = e.pageX;
startMousePosY = e.pageY;
});
$(document).on('mouseup', function() {
moveFrame = false;
});
$(document).bind('mousemove', function(e) {
if (moveFrame){
currPosX = comStartX + (e.pageX - startMousePosX);
currPosY = comStartY + (e.pageY - startMousePosY);
$(selected).css({position: 'absolute', 'left': currPosX + 'px', 'top': currPosY + 'px'});
}
});
return true;
};
$('#card').isDraggable();
#card {
position: fixed;
width: 100px;
height: 150px;
top: calc(50% - 75px);
left: calc(50% - 50px);
border: 1px solid #D3D3D3;
}
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
<title>freeMarketRocks</title>
</head>
<body>
<div>
<div id="card">
</div>
</div>
</body>
</html>