How to keep drawing on canvas when scrolling? - javascript

I'm wanting to implement canvas as background for my website so users can use their cursors to paint on the webpage like this codepen: https://codepen.io/cocotx/pen/PoGRdxQ?editors=1010
(this is an example code from http://www.dgp.toronto.edu/~clwen/test/canvas-paint-tutorial/)
if(window.addEventListener) {
window.addEventListener('load', function () {
var canvas, context;
// Initialization sequence.
function init () {
// Find the canvas element.
canvas = document.getElementById('imageView');
if (!canvas) {
alert('Error: I cannot find the canvas element!');
return;
}
if (!canvas.getContext) {
alert('Error: no canvas.getContext!');
return;
}
// Get the 2D canvas context.
context = canvas.getContext('2d');
if (!context) {
alert('Error: failed to getContext!');
return;
}
// Attach the mousemove event handler.
canvas.addEventListener('mousemove', ev_mousemove, false);
}
// The mousemove event handler.
var started = false;
function ev_mousemove (ev) {
var x, y;
// Get the mouse position relative to the canvas element.
if (ev.layerX || ev.layerX == 0) { // Firefox
x = ev.layerX;
y = ev.layerY;
} else if (ev.offsetX || ev.offsetX == 0) { // Opera
x = ev.offsetX;
y = ev.offsetY;
}
// The event handler works like a drawing pencil which tracks the mouse
// movements. We start drawing a path made up of lines.
if (!started) {
context.beginPath();
context.moveTo(x, y);
started = true;
} else {
context.lineTo(x, y);
context.stroke();
}
}
init();
}, false); }
The problem is that the cursor stops painting when I scroll until I move my mouse again. Any idea on how to keep cursor painting even when I scroll?
Thanks in advance! Much appreciated!

You will have to store the last mouse event and fire a new fake one in the scroll event.
Luckily, the MouseEvent constructor accepts an mouseEventInit object on which we can set the clientX and clientY values of our new event, so we just need to store these values from the previous event and dispatch it in the scroll event.
Now, I couldn't help but rewrite almost everything from your code.
It had a lot of checks for old browsers (like very old ones which should never face the web again anyway), you may want to add it again if you wish.
It wasn't clearing the context, meaning that every time it drew a new line, it also did draw again the previous lines over themselves, leading in fatter lines, with a lot of noise at the beginning, and smoother lines at the end.
This could be fixed in many ways, the less intrusive one was to just clear the context at each frame.
To get the relative mouse position, it now uses the clientX and clientY properties of the event.
And the rest of the changes is commented in the snippet.
window.addEventListener('load', function () {
const canvas = document.getElementById('imageView');
context = canvas.getContext("2d");
let last_event; // we will store our mouseevents here
// we now listen to the mousemove event on the document,
// not only on the canvas
document.addEventListener('mousemove', ev_mousemove);
document.addEventListener('scroll', fireLastMouseEvent, { capture: true } );
// to get the initial position of the cursor
// even if the mouse never moves
// we listen to a single mouseenter event on the document's root element
// unfortunately this seems to not work in Chrome
document.documentElement.addEventListener( "mouseenter", ev_mousemove, { once: true } );
// called in scroll event
function fireLastMouseEvent() {
if( last_event ) {
// fire a new event on the document using the same clientX and clientY values
document.dispatchEvent( new MouseEvent( "mousemove", last_event ) );
}
}
// mousemove event handler.
function ev_mousemove (ev) {
const previous_evt = last_event || {};
const was_offscreen = previous_evt.offscreen;
// only for "true" mouse event
if( ev.isTrusted ) {
// store the clientX and clientY props in an object
const { clientX, clientY } = ev;
last_event = { clientX, clientY };
}
// get the relative x and y positions from the mouse event
const point = getRelativePointFromEvent( ev, canvas );
// check if we are out of the canvas viewPort
if( point.x < 0 || point.y < 0 || point.x > canvas.width || point.y > canvas.height ) {
// remember we were
last_event.offscreen = true;
// if we were already, don't draw
if( was_offscreen ) { return; }
}
// we come from out-of-screen to in-screen
else if( was_offscreen ) {
// move to the previous point recorded as out-of-screen
const previous_point = getRelativePointFromEvent( previous_evt, canvas );
context.moveTo( previous_point.x, previous_point.y );
}
// add the new point to the context's sub-path definition
context.lineTo( point.x, point.y );
// clear the previous drawings
context.clearRect( 0, 0, canvas.width, canvas.height );
// draw everything again
context.stroke();
}
function getRelativePointFromEvent( ev, elem ) {
// first find the bounding rect of the element
const bbox = elem.getBoundingClientRect();
// subtract the bounding rect from the client coords
const x = ev.clientX - bbox.left;
const y = ev.clientY - bbox.top;
return { x, y };
}
});
#container {
width: 400px;
height: 200px;
overflow: auto;
border: 1px solid;
}
#imageView { border: 1px solid #000; }
canvas {
margin: 100px;
}
<div id="container">
<canvas id="imageView" width="400" height="300"></canvas>
</div>

Related

How to detect collisions between DOM-draggables and FabricJS shapes

I would like to find object that is already present in the canvas as soon as I drag external image over canvas and it intersects with that canvas object. This is the code I am using for drag and drop:
if (Modernizr.draganddrop) {
// Browser supports HTML5 DnD.
// Bind the event listeners for the image elements
var images = document.querySelectorAll('#images img');
[].forEach.call(images, function (img) {
img.addEventListener('dragstart', handleDragStart, false);
img.addEventListener('dragend', handleDragEnd, false);
});
// Bind the event listeners for the canvas
var canvasContainer = document.getElementById('canvas-container');
canvasContainer.addEventListener('dragenter', handleDragEnter, false);
canvasContainer.addEventListener('dragover', handleDragOver, false);
canvasContainer.addEventListener('dragleave', handleDragLeave, false);
canvasContainer.addEventListener('drop', handleDrop, false);
} else {
// Replace with a fallback to a library solution.
alert("This browser doesn't support the HTML5 Drag and Drop API.");
}
above mentioned callback functions are defined accordingly.
What I already know is that we can find out the intersection between two objects when they are present within the canvas. Link: http://fabricjs.com/intersection/
But my problem is I need to catch the object while I am dragging image from outside the canvas onto the canvas area and it intersects with the canvas object.
Any help would be much appreciated. Thanks.
About testing for collisions
Basic shapes tend to come in 2 flavors: rectangular & circular.
You'll need these tests to see if any of these basic shapes are colliding:
function rectsColliding(r1,r2){
return(!(
r1.x > r2.x+r2.width ||
r1.x+r1.width < r2.x ||
r1.y > r2.y+r2.height ||
r1.y+r1.height < r2.y
));
}
function circlesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var sumR=c1.radius+c2.radius;
return( dx*dx+dy*dy <= sumR*sumR );
}
function rectCircleColliding(circle,rect){
var distX = Math.abs(circle.x - rect.x-rect.w/2);
var distY = Math.abs(circle.y - rect.y-rect.h/2);
if (distX > (rect.w/2 + circle.r)) { return false; }
if (distY > (rect.h/2 + circle.r)) { return false; }
if (distX <= (rect.w/2)) { return true; }
if (distY <= (rect.h/2)) { return true; }
var dx=distX-rect.w/2;
var dy=distY-rect.h/2;
return (dx*dx+dy*dy<=(circle.r*circle.r));
}
An important notice about draggable
The native html5 draggable system still has some cross-browser inconsistencies. It's difficult to get an accurate [x,y] position when the draggable is being dragged. Instead, you can use the jQueryUI draggable system which has smoothed out the inconsistencies between browsers.
A plan to test if dragged DOM elements collide with FabricJS shapes
listen for the dragmove event on the draggable,
Create a bounding box of the draggable at its current [x,y] position,
Create a bounding box of the Fabric.Rect. This bounding box must be offset by the position of the FabricJS canvas in the window,
Call the rectsColliding function to test if the 2 bounding boxes are colliding,
Do whatever you need depending whether the DOM-draggable & Fabric.Rect are colliding.
Here's example code and a Demo:
// attach the canvas's bounding box to itself
var canvasElement=document.getElementById("c");
canvasElement.bb=canvasElement.getBoundingClientRect();
// create a wrapper around native canvas element (with id="c")
var canvas = new fabric.Canvas('c');
var rect;
// load an image and begin...
var image1=new Image();
image1.onload=function(){
createFabrics();
createDraggables();
}
image1.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png";
// create a fabric rect and add it to the stage
function createFabrics(){
// create a rectangle object
rect = new fabric.Rect({
left: 100,
top: 20,
fill: 'green',
width: 100,
height: 75,
lockMovementX:true,
lockMovementY:true,
lockScalingX:true,
lockScalingY:true,
lockRotation:true,
hasControls:false,
});
rect.bb=rect.getBoundingRect();
// "add" rectangle onto canvas
canvas.add(rect);
canvas.renderAll();
}
// Make the previously created image draggable
// Detect collisions between the image and the FabricJS rect
function createDraggables(){
var $house=$("#house");
var $canvas=$("#c");
// make the canvas element a dropzone
$canvas.droppable({ drop:dragDrop, hoverClass:'drop-hover' });
// make the house draggable
$house.draggable({
helper:'clone',
// optional event handlers are...
start:dragstart,
drag:dragmove,
stop:dragend,
});
// set the data payload
$house.data("image",image1); // key-value pair
function dragstart(e,ui){}
function dragend(e,ui){}
function dragDrop(e,ui){}
function dragmove(e,ui){
var target=e.target;
var tbb={
left:ui.offset.left-canvasElement.bb.left,
top:ui.offset.top-canvasElement.bb.top,
width:target.width,
height:target.height
}
if( rectsColliding(tbb,rect.bb) ){
rect.fill='red';
canvas.renderAll();
}else{
rect.fill='green';
canvas.renderAll();
}
}
function rectsColliding(r1,r2){
return(!(
r1.left > r2.left+r2.width ||
r1.left+r1.width < r2.left ||
r1.top > r2.top+r2.height ||
r1.top+r1.height < r2.top
));
}
} // end createDraggables
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<h4 id=hit>Drag house over green FabricJS rect.<br>Rect will turn red during collisions.</h4>
<img id="house" width=32 height=32 src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png"><br>
<canvas id='c' width=300 height=150></canvas>

How to drag and drop between canvases in Fabric.js

I understand Fabric.js have built-in support on drag-n-drop within the same canvas.
How can we make it work for multiple canvas?
Or from an non-canvas html element e.g. an image from a table?
Drag and drop between canvases is possible in Fabric.js, but involves some manipulation of private properties. For this reason it is not guaranteed to be functional with future versions of Fabric.js.
Working demo: https://jsfiddle.net/mmalex/kdbu9f3y/
Video capture: https://youtu.be/nXZgCmIrpqQ
Key features:
✓ can drag and drop between any number of canvases (not only two),
✓ can drag back and forth between canvases without interruption,
✓ can transform (mirror) dropped image without interruption of manipulation.
Step 1 – prepare canvases, load images, arrange everything for demo:
//create two canvases
var canvas0El = document.getElementById("c0");
canvas0El.width = canvas0El.offsetWidth;
canvas0El.height = canvas0El.parentElement.offsetHeight;
var canvas1El = document.getElementById("c1");
canvas1El.width = canvas1El.offsetWidth;
canvas1El.height = canvas1El.parentElement.offsetHeight;
var canvas0 = new fabric.Canvas('c0');
canvas0.setBackgroundColor('rgba(19, 19, 19, 0.25)');
canvas0.renderAll();
var canvas1 = new fabric.Canvas('c1');
canvas1.setBackgroundColor('rgba(92, 18, 18, 0.25)');
canvas1.renderAll();
// add loaded image on left canvas
var onImageLoaded = function(oImg) {
oImg.originX = "center";
oImg.originY = "center";
oImg.left = this.x;
oImg.top = this.y;
canvas0.add(oImg);
oImg.canvas = canvas0;
imgArrow = oImg;
};
var config = { crossOrigin: 'anonymous' };
var baseUrl = "http://mbnsay.com/rayys/images";
var url0 = baseUrl + "/arrow-right-green.png";
var url1 = baseUrl + "/arrow-right-icon.png";
var url2 = baseUrl + "/arrow-right-blue.png";
// load some images
fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 56, y: 96 }), config);
fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 156, y: 96 }), config);
fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 56, y: 2*96 }), config);
fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 156, y: 2*96 }), config);
fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 56, y: 3*96 }), config);
fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 156, y: 3*96 }), config);
Step 2 – subscribe object:moving events on both canvases, and watch when object center crossing the canvas border. When object crosses the border, it has to be
remove from source canvas,
paste into destination canvas,
migrate internal canvas transformations (will be explained separately)
var onObjectMoving = function(p) {
var viewport = p.target.canvas.calcViewportBoundaries();
if (p.target.canvas === canvas0) {
if (p.target.left > viewport.br.x) {
console.log("Migrate: left -> center");
migrateItem(canvas0, canvas1, p.target);
return;
}
}
if (p.target.canvas === canvas1) {
if (p.target.left < viewport.tl.x) {
console.log("Migrate: center -> left");
migrateItem(canvas1, canvas0, p.target);
return;
}
}
};
canvas0.on("object:moving", onObjectMoving);
canvas1.on("object:moving", onObjectMoving);
Step 3 – The core of the solution, migrate object between canvases not interrupting mouse manipulations. Hard to explain, just follow the comments in code.
var migrateItem = function(fromCanvas, toCanvas, pendingImage) {
// Just drop image from old canvas
fromCanvas.remove(pendingImage);
// We're going to trick fabric.js,
// so we keep internal transforms of the source canvas,
// in order to inject it into destination canvas.
var pendingTransform = fromCanvas._currentTransform;
fromCanvas._currentTransform = null;
// Make shortcuts for fabric.util.removeListener and fabric.util.addListener
var removeListener = fabric.util.removeListener;
var addListener = fabric.util.addListener;
// Re-arrange subscriptions for source canvas
{
removeListener(fabric.document, 'mouseup', fromCanvas._onMouseUp);
removeListener(fabric.document, 'touchend', fromCanvas._onMouseUp);
removeListener(fabric.document, 'mousemove', fromCanvas._onMouseMove);
removeListener(fabric.document, 'touchmove', fromCanvas._onMouseMove);
addListener(fromCanvas.upperCanvasEl, 'mousemove', fromCanvas._onMouseMove);
addListener(fromCanvas.upperCanvasEl, 'touchmove', fromCanvas._onMouseMove, {
passive: false
});
if (isTouchDevice) {
// Wait 500ms before rebinding mousedown to prevent double triggers
// from touch devices
var _this = fromCanvas;
setTimeout(function() {
addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
}, 500);
}
}
// Re-arrange subscriptions for destination canvas
{
addListener(fabric.document, 'touchend', toCanvas._onMouseUp, {
passive: false
});
addListener(fabric.document, 'touchmove', toCanvas._onMouseMove, {
passive: false
});
removeListener(toCanvas.upperCanvasEl, 'mousemove', toCanvas._onMouseMove);
removeListener(toCanvas.upperCanvasEl, 'touchmove', toCanvas._onMouseMove);
if (isTouchDevice) {
// Unbind mousedown to prevent double triggers from touch devices
removeListener(toCanvas.upperCanvasEl, 'mousedown', toCanvas._onMouseDown);
} else {
addListener(fabric.document, 'mouseup', toCanvas._onMouseUp);
addListener(fabric.document, 'mousemove', toCanvas._onMouseMove);
}
}
// We need this timer, because we want Fabric.js to complete pending render
// before we inject, because it causes some unpleasant image jumping.
setTimeout(function() {
// Add image to destination canvas,
pendingImage.scaleX *= -1;
pendingImage.canvas = toCanvas;
pendingImage.migrated = true;
toCanvas.add(pendingImage);
// and inject transforms from source canvas
toCanvas._currentTransform = pendingTransform;
// as we have mirrored the image, we mirror transforms too
toCanvas._currentTransform.scaleX *= -1;
toCanvas._currentTransform.original.scaleX *= -1;
// finally don't forget to make pasted object selected
toCanvas.setActiveObject(pendingImage);
}, 10);
};
Have fun!
Use
canvas.observe("object:moving", function (event) {});
If event.e.clientY and event.e.clientX are outside the canvas, then:
var activeObject = canvas.getActiveObject();
Store in global dragImage var
activeObject.clone(function (c) { dragImage = c; });
canvas.remove(activeObject);
Then in window mouse move event you can place an img with src = dragImage.src and follow the cursor.
function mousemove(e){
if (dragImage != null) {
$("#dragimage").show();
$("#dragimage").css("left", e.clientX);
$("#dragimage").css("top", e.clientY);
return;
}else{
$("#dragimage").hide();
}
}
On a window event mouseup, if dragImage != null and new coordinates are inside a fabric.js canvas, just newcanvas.add(dragImage).
mouseup event:
if (dragImage != null) {
$([canvas, canvas2]).each(function (i, v) {
if (Intersect([event.clientX, event.clientY],$(v.wrapperEl))) {
dragImage.left = event.clientX - $(v.wrapperEl).offset().left;
dragImage.top = event.clientY - $(v.wrapperEl).offset().top;
v.add(dragImage);
}
});
dragImage = null;
}
Helper Intersect function:
function Intersect(point, element) {
return ( point[0] > element.offset().left
&& point[0] < element.offset().left + element.width()
&& point[1] < element.offset().top + element.height()
&& point[1] > element.offset().top
);
}
css for #dragimage:
#dragimage
{
opacity:0.5;
max-width:100px;
max-height:200px;
position:fixed;
top:0px;
left:0px;
z-index:90000;
}
I can't do a fiddle but i implemented this on our mega huge photo album editor in less than 30 minutes. Works for text too but for text you must use dragImage=getActiveObject().clone()
Any questions feel free to ask.

Mouse input not working, only touch

I have a problem with my canvas drawing.
case1:
My PC can be used as touch screen as well as having a mouse. However, I can only draw using the touch screen. The mouse doesn't work.
case2:
My friend's PC only has a mouse and the canvas works fine.
Please help. I can see where the problem, but I'm not good enough to make changes.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Desktops and Tablets</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
initialize();
});
// works out the X, Y position of the click inside the canvas from the X, Y position on the page
function getPosition(mouseEvent, sigCanvas) {
var x, y;
if (mouseEvent.pageX != undefined && mouseEvent.pageY != undefined) {
x = mouseEvent.pageX;
y = mouseEvent.pageY;
} else {
x = mouseEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = mouseEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { X: x - sigCanvas.offsetLeft, Y: y - sigCanvas.offsetTop };
}
function initialize() {
// get references to the canvas element as well as the 2D drawing context
var sigCanvas = document.getElementById("canvas1");
var context = sigCanvas.getContext("2d");
context.strokeStyle = 'Black';
// This will be defined on a TOUCH device such as iPad or Android, etc.
var is_touch_device = 'ontouchstart' in document.documentElement;
if (is_touch_device) {
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function (coors) {
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function (coors) {
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function (coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event) {
// get the touch coordinates. Using the first touch in case of multi-touch
var coors = {
x: event.targetTouches[0].pageX,
y: event.targetTouches[0].pageY
};
// Now we need to get the offset of the canvas location
var obj = sigCanvas;
if (obj.offsetParent) {
// Every time we find a new object, we add its offsetLeft and offsetTop to curleft and curtop.
do {
coors.x -= obj.offsetLeft;
coors.y -= obj.offsetTop;
}
// The while loop can be "while (obj = obj.offsetParent)" only, which does return null
// when null is passed back, but that creates a warning in some editors (i.e. VS2010).
while ((obj = obj.offsetParent) != null);
}
// pass the coordinates to the appropriate handler
drawer[event.type](coors);
}
// attach the touchstart, touchmove, touchend event listeners.
sigCanvas.addEventListener('touchstart', draw, false);
sigCanvas.addEventListener('touchmove', draw, false);
sigCanvas.addEventListener('touchend', draw, false);
// prevent elastic scrolling
sigCanvas.addEventListener('touchmove', function (event) {
event.preventDefault();
}, false);
}
else {
// start drawing when the mousedown event fires, and attach handlers to
// draw a line to wherever the mouse moves to
$("#canvas1").mousedown(function (mouseEvent) {
var position = getPosition(mouseEvent, sigCanvas);
context.moveTo(position.X, position.Y);
context.beginPath();
// attach event handlers
$(this).mousemove(function (mouseEvent) {
drawLine(mouseEvent, sigCanvas, context);
}).mouseup(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
}).mouseout(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
});
});
}
}
// draws a line to the x and y coordinates of the mouse event inside
// the specified element using the specified context
function drawLine(mouseEvent, sigCanvas, context) {
var position = getPosition(mouseEvent, sigCanvas);
context.lineTo(position.X, position.Y);
context.stroke();
}
// draws a line from the last coordiantes in the path to the finishing
// coordinates and unbind any event handlers which need to be preceded
// by the mouse down event
function finishDrawing(mouseEvent, sigCanvas, context) {
// draw the line to the finishing coordinates
drawLine(mouseEvent, sigCanvas, context);
context.closePath();
// unbind any events which could draw
$(sigCanvas).unbind("mousemove")
.unbind("mouseup")
.unbind("mouseout");
}
</script>
</head>
<body>
<h1>Canvas test</h1>
<div id="canvasDiv">
<!-- It's bad practice (to me) to put your CSS here. I'd recommend the use of a CSS file! -->
<canvas id="canvas1" width="500px" height="500px" style="border:2px solid #000000; margin-left: 400px;
margin-top: 100px; "></canvas>
</div>
</body>
</html>
The code below is causing your trouble -- it exclusively binds to only touch events or only mouse events.
if (is_touch_device) {
....
}
else {
....
}
Maybe you could use jQuery's vmouse? Or try binding to both touch and mouse events...

Syncrhonous scrolling of 2 separate canvases on a single page

I have 2 canvases:-
<canvas id="myCanvas" width="915" height="650" style="border: 2px double #000000;"></canvas>
<canvas id="pharmacy" width="915" height ="320" style ="border:2px double #000000;"></canvas>
They are separate canvases, on the same aspx page.
The idea is to have them both represent a timeline. i.e they are both scrollable left to right and vice-versa.
I had created the first canvas a while ago, and recently decided to implement the 2nd canvas.
When I copy pasted the original code in the 2nd canvas to emulate the scrolling part, I figured out that in practice, when I scrolled , only the original canvas would scroll and the new canvas doesn't.
If I comment out the code for the original canvas then the new canvas scrolls.
Which led me to think, that the event based scrolling I was trying to achieve has some form of flaw in its naming convention. (since a mouse click is represented as an event and then dragging is allowed; my code was only registering dragging event of the original canvas and not the new canvas).
http://jsfiddle.net/przBL/3/
I would greatly appreciate if someone could take a look at the code and let me know where I am going wrong??
The goal:- is to click on either of the canvases, drag the mouse, and both canvases scroll at the same time.
1st canvas:-
// when mouse is clicked on canvas
window.onmousedown = function (e) {
var evt = e || event;
// dragging is set to true.
dragging = true;
lastX = evt.offsetX;
}
// when mouse is clicked again and the canvas is deselected
window.onmouseup = function () {
// dragging is set to false.
dragging = false;
}
// when mouse is dragging the canvas sideways
can.onmousemove = function (e) {
var evt = e || event;
if (dragging) {
var delta = evt.offsetX - lastX;
translated += delta;
//console.log(translated);
ctx.restore();
ctx.clearRect(0, 0, 930, 900);
ctx.save();
ctx.translate(translated, 0);
lastX = evt.offsetX;
timeline();
}
}
2nd canvas:-
// when mouse is clicked on canvas
window.onmousedown = function (e) {
var evt = e || event;
// dragging is set to true.
dragging = true;
lastX = evt.offsetX;
}
// when mouse is clicked again and the canvas is deselected
window.onmouseup = function () {
// dragging is set to false.
dragging = false;
}
// when mouse is dragging the canvas sideways
can1.onmousemove = function (e) {
var evt = e || event;
if (dragging) {
var delta = evt.offsetX - lastX;
translated += delta;
//console.log(translated);
ctx1.restore();
ctx1.clearRect(0, 0, 915, 600);
ctx1.save();
ctx1.translate(translated, 0);
lastX = evt.offsetX;
pharm_line();
}
}
now I want both can and can1 to move synchronously on any mousedown and mousemove event and stop when mouse is up.
You can use common mouse-handlers to identically scroll both canvas's.
See window.onmousemove below which keeps both canvas's in translated sync.
// when mouse is clicked on canvas
window.onmousedown = function (e) {
var evt = e || event;
// dragging is set to true.
dragging = true;
lastX = evt.offsetX;
}
// when mouse is clicked again and the canvas is deselected
window.onmouseup = function (e) {
// dragging is set to false.
dragging = false;
}
window.onmousemove = function(e) {
var evt = e || event;
if (dragging) {
var delta = evt.offsetX - lastX;
translated += delta;
move(ctx,930,900);
move(ctx1,915,600);
lastX = evt.offsetX;
timeline();
pharm_line();
}
}
// common code used to service either canvas
function move(context,width,height){
context.restore();
context.clearRect(0, 0, width, height);
context.save();
context.translate(translated, 0);
}

How To Turn A (Touch) Pencil Tool Into A Straight Line Tool?

So far, it's mobile touch pencil tool, which can draw perfectly, but I'm wondering... how would I make it so that it draws a completely straight line instead of a basic pencil tool which can draw outside the line?
Here's the code so far:
// "Draw Line" Button
$(document).ready(function () {
initialize();
});
// works out the X, Y position of the click inside the canvas from the X, Y position on the page
function getPosition(mouseEvent, sigCanvas) {
var x, y;
if (mouseEvent.pageX != undefined && mouseEvent.pageY != undefined) {
x = mouseEvent.pageX;
y = mouseEvent.pageY;
} else {
x = mouseEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = mouseEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { X: x - sigCanvas.offsetLeft, Y: y - sigCanvas.offsetTop };
}
function initialize() {
// get references to the canvas element as well as the 2D drawing context
var sigCanvas = document.getElementById("canvasSignature");
var context = sigCanvas.getContext("2d");
context.strokeStyle = 'Black';
// This will be defined on a TOUCH device such as iPad or Android, etc.
var is_touch_device = 'ontouchstart' in document.documentElement;
if (is_touch_device) {
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function (coors) {
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function (coors) {
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function (coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event) {
// get the touch coordinates. Using the first touch in case of multi-touch
var coors = {
x: event.targetTouches[0].pageX,
y: event.targetTouches[0].pageY
};
// Now we need to get the offset of the canvas location
var obj = sigCanvas;
if (obj.offsetParent) {
// Every time we find a new object, we add its offsetLeft and offsetTop to curleft and curtop.
do {
coors.x -= obj.offsetLeft;
coors.y -= obj.offsetTop;
}
// The while loop can be "while (obj = obj.offsetParent)" only, which does return null
// when null is passed back, but that creates a warning in some editors (i.e. VS2010).
while ((obj = obj.offsetParent) != null);
}
// pass the coordinates to the appropriate handler
drawer[event.type](coors);
}
// attach the touchstart, touchmove, touchend event listeners.
sigCanvas.addEventListener('touchstart', draw, false);
sigCanvas.addEventListener('touchmove', draw, false);
sigCanvas.addEventListener('touchend', draw, false);
// prevent elastic scrolling
sigCanvas.addEventListener('touchmove', function (event) {
event.preventDefault();
}, false);
}
else {
// start drawing when the mousedown event fires, and attach handlers to
// draw a line to wherever the mouse moves to
$("#canvasSignature").mousedown(function (mouseEvent) {
var position = getPosition(mouseEvent, sigCanvas);
context.moveTo(position.X, position.Y);
context.beginPath();
// attach event handlers
$(this).mousemove(function (mouseEvent) {
drawLine(mouseEvent, sigCanvas, context);
}).mouseup(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
}).mouseout(function (mouseEvent) {
finishDrawing(mouseEvent, sigCanvas, context);
});
});
}
}
// draws a line to the x and y coordinates of the mouse event inside
// the specified element using the specified context
function drawLine(mouseEvent, sigCanvas, context) {
var position = getPosition(mouseEvent, sigCanvas);
context.lineTo(position.X, position.Y);
context.stroke();
}
// draws a line from the last coordiantes in the path to the finishing
// coordinates and unbind any event handlers which need to be preceded
// by the mouse down event
function finishDrawing(mouseEvent, sigCanvas, context) {
// draw the line to the finishing coordinates
drawLine(mouseEvent, sigCanvas, context);
context.closePath();
// unbind any events which could draw
$(sigCanvas).unbind("mousemove")
.unbind("mouseup")
.unbind("mouseout");
}
Thanks,
Wardenclyffe
Anchor the pen down point at first touch and then wherever the next touch is draw a straight line from the first touch to the new touch. Have a floating check mark above the new touch to accept the new line. If the check mark isn't selected and there is another touch then remove the previous line, draw a new line, and show another check mark to save the new line. Provide an (x) above the original pen down point to cancel the line drawing tool.
The difference between a straight line tool and a pencil tool is that the pencil draws as many points as possible, and connects them with (possibly smoothed) line segments. A straight line tool connects a small number of lines with relatively large line segments.
One option would be for the pencil tool to lay down points as long as the finger touches the screen. And then the line tool draws from tap to tap. Alternately, if you tap and slide, the line tool could rubber-band the line from the touch point to the drag point, and then keep the last point before the touch lifts.

Categories

Resources