I want to finish drawing when releasing mouse button and not on double click. How can I achieve that?
Right now I use this code to activate freehand drawing on Openlayers drawing feature by default:
draw = new ol.interaction.Draw({
source: drawLayerSource,
type: 'LineString',
condition: ol.events.condition.singleClick,
freehandCondition: ol.events.condition.noModifierKeys
});
map.addInteraction(draw);
But I don't know how to avoid double click to finish the line string and use mouse release instead.
UPDATE - https://jsfiddle.net/jonataswalker/frwfuzzn/
To enable both freehand and straight lines, I'm calculating the elapsed time between the drawing start and pointerup with this function:
var isStraightLine = function(){
var now = new Date();
// `start_time` was set at the drawing beginning
var diff = now - start_time;
// strip the ms
diff /= 1000;
already_checked = true;
// an average time I found here
// not sure if this is completely reliable
return diff < 0.07;
};
If I find this is a freehand drawing finish with draw.finishDrawing(). Better check the code for the sake of understanding.
I could but only if you stay with freehandCondition default condition - ol.events.condition.shiftKeyOnly.
Listen to pointerup on ol.Map and if you are drawing, finish with ol.interaction.Draw#finishDrawing.
var start_drawing = false;
var draw = new ol.interaction.Draw({
source: vectorSource,
type: 'LineString',
condition: ol.events.condition.singleClick
});
map.addInteraction(draw);
draw.on('drawstart', function(evt){
start_drawing = true;
});
draw.on('drawend', function(evt){
start_drawing = false;
});
map.on('pointerup', function(evt){
if(start_drawing) {
draw.finishDrawing();
}
});
http://jsfiddle.net/jonataswalker/ewv0mo3c/
Related
I am trying to add a draw interaction to my map. The user should click a button, add one point and then add attribute information about the point. I'm still stuck in the drawing part: After I draw a point in the map, the cursor position remains the same and mouse movements change the map in the background (like when panning). When I click again this stops. No idea why it's doing this. In the console in Chrome I get an error in the draw.js file from openlayers (this.source_.addFeature is not a function).
Grateful for any help!
Here's my code:
function addFeature() {
var btn_cancel = document.getElementById('buttonCancel');
btn_cancel.style.display = 'block';
var btn_add = document.getElementById('buttonAdd');
btn_add.classList.add("button_clicked");
var draw;
function addInteraction() {
draw = new ol.interaction.Draw({
source: lunchvec,
type: "Point",
});
map.addInteraction(draw);
draw.on('drawend', function(evt) {
console.log(evt.feature.getGeometry().getCoordinates());
map.removeInteraction(draw);
});
};
addInteraction();
}
I have an application that allows the user to draw in a fabric canvas for a limited period of time. After the time ends, I would like to end the drawing mode and save the drawing made as an image.
My problem is that if the time ends while the user is still drawing (dragging the mouse), the drawing will disappear (after clicking on the canvas again).
The below fiddle example shows the application I made, and the steps to produce the problem are:
Run the fiddle, and start drawing immediately.
After 3 seconds the timeout event will occur and the free drawing mode will be ended. And the drawing will stop.
Click on the canvas again and drag the mouse.
The drawing will disappear.
Code example:
var canvas = new fabric.Canvas("c",{backgroundColor : "#fff"});
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "green";
canvas.freeDrawingBrush.width = 4;
setTimeout(stop_drawing, 3000);
function stop_drawing() {
canvas.isDrawingMode = false;
}
Fiddle example:
https://jsfiddle.net/usaadi/808x5d20/1/
You could emulate what happens in the library:
_onMouseUpInDrawingMode: function(e) {
this._isCurrentlyDrawing = false;
if (this.clipTo) {
this.contextTop.restore();
}
this.freeDrawingBrush.onMouseUp();
this._handleEvent(e, 'up');
},
In your case, you have just to fire onMouseUp event, so your function stop_drawing will be:
function stop_drawing() {
canvas.isDrawingMode = false;
canvas.freeDrawingBrush.onMouseUp();
}
in our case onMouseUp will be:
/**
* Invoked on mouse up
*/
onMouseUp: function() {
this._finalizeAndAddPath();
},
let's see how it works _finalizeAndAddPath:
/**
* On mouseup after drawing the path on contextTop canvas
* we use the points captured to create an new fabric path object
* and add it to the fabric canvas.
*/
_finalizeAndAddPath: function() {
var ctx = this.canvas.contextTop;
ctx.closePath();
var pathData = this.convertPointsToSVGPath(this._points).join('');
if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
// do not create 0 width/height paths, as they are
// rendered inconsistently across browsers
// Firefox 4, for example, renders a dot,
// whereas Chrome 10 renders nothing
this.canvas.renderAll();
return;
}
var path = this.createPath(pathData);
this.canvas.add(path);
path.setCoords();
this.canvas.clearContext(this.canvas.contextTop);
this._resetShadow();
this.canvas.renderAll();
// fire event 'path' created
this.canvas.fire('path:created', { path: path });
}
at this point the drawn path has already been added to the canvas.
I have updated your snippet here
if you want to take a look at the code below...
all the best
var canvas = new fabric.Canvas("c", {
backgroundColor: "#fff"
});
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "green";
canvas.freeDrawingBrush.width = 4;
setTimeout(stop_drawing, 3000);
function stop_drawing() {
canvas.isDrawingMode = false;
canvas.freeDrawingBrush.onMouseUp();
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
i have added the draw interaction to draw free hand polygon by default freehandCondition is SHIFT key but how can we draw if the map is opened in Mobile and Tablets.
drawOptions.type = 'Polygon';
this.draw = new ol.interaction.Draw(drawOptions);
this.draw.on('drawend', lang.hitch(this, "drawEnd"));
How can we draw? is their any other condition i can give?
There are several ways to suspend drag pan and enable free-hand drawing in OL3. Here is one way to set the freeHandCondition (where the variable shapeGeom is Point, LineString or Polygon):
function drawInteraction() {
if (shapeGeom == 'Point') {
draw = new ol.interaction.Draw({
features: drawfeature,
type: shapeGeom,
})
} else {
draw = new ol.interaction.Draw({
features: drawfeature,
type: shapeGeom,
freehandCondition: ol.events.condition.always,
condition: ol.events.condition.never,
})
}
map.addInteraction(draw);
}
When you start the draw action, suspend DragPan.
map.getInteractions().forEach(function(interaction) {
if (interaction instanceof ol.interaction.DragPan) {
interaction.setActive(false);
}
}, this);
Then, restore DragPan when the feature has been drawn.
draw.on('drawend', function(event){
map.addInteraction(new ol.interaction.DragPan)});
OL3's API docs have information for both the freeHandCondition and DragPan elements with these links.
The app works fine so far except if the line is drawn really fast and leaves the edge of the canvas, the line is then not drawn to the edge of the canvas. There is a part missing from it.
I'm trying to fix the issue with:
canvasVar.addEventListener ('mouseout', clearPathIfMouseCursorLeavesCanvasFunc);
and
function clearPathIfMouseCursorLeavesCanvasFunc(e){
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
mouseButtonHeld = false;
I've tried some things like adding a settimeout(); but nothing worked so far. I don't know what causes this and I've been searching if someone else had this problem and a fix for it, but every canvas drawing app I've come across has the same issues.
It's very important that the line is drawn to the edge and that the users mouse motion is recognized, not just a line to the last coordinates where the mouse left the canvas.
It's been days now that I'm stuck with this problem. Help is really appreciated!
Whole Code:
// Varibale declaration
var canvasVar = document.getElementById('canvasHtmlElement');
var contextVar = canvasVar.getContext('2d');
var pointRadiusVar = 0.5;
var mouseButtonHeld = false;
var pointsArrPosition = 0;
//Arrays
var pointsArr = [];
// Varibale declration end
//canvas setup
canvasVar.width = window.innerWidth;
canvasVar.height = window.innerHeight;
//canvas setup end
//resize fix
window.onresize = function() {
var tempImageVar = contextVar.getImageData(0, 0, canvasVar.width, canvasVar.height);
canvasVar.width = window.innerWidth;
canvasVar.height = window.innerHeight;
contextVar.putImageData(tempImageVar, 0, 0);
}
//resize fix end
//functions
// Objects
function pointObject() {
this.x = 0;
this.y = 0;
this.fill = '#444444';
}
function addFilledCircleFunc(x, y) {
//alert('works1');
var filledCircle = new pointObject;
filledCircle.x = x;
filledCircle.y = y;
pointsArr.push(filledCircle);
contextVar.lineWidth = 10; //pointRadiusVar * 2; // Line Width
contextVar.lineTo(pointsArr[pointsArrPosition].x, pointsArr[pointsArrPosition].y);
contextVar.stroke();
//contextVar.beginPath();
contextVar.fillRect(filledCircle.x, filledCircle.y, 1, 1);
//contextVar.arc(filledCircle.x, filledCircle.y, pointRadiusVar, 0, Math.PI * 2);
//contextVar.fill();
//contextVar.lineWidth = 0.5;
//contextVar.stroke();
//contextVar.beginPath();
pointsArrPosition++;
//contextVar.moveTo(pointsArr[pointsArrPosition].x, pointsArr[pointsArrPosition].y);
//alert(pointsArr[0].x);
}
//Objects end
// create circle on mouse clicked point while mousebutton is held
var addPointToCanvasVar = function(e) {
if (mouseButtonHeld) {
//alert('addpointfunc');
addFilledCircleFunc(e.clientX, e.clientY);
}
};
// MAKE SURE that lines work when drawn over the edge of the canvas
function clearPathIfMouseCursorLeavesCanvasFunc(e) {
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
mouseButtonHeld = false;
}
// end
// mouse Up/Down functions
var mouseDownVar = function(e) {
//alert("mouseDown");
addPointToCanvasVar(e); // add point on first click, not just when mousebutton is held
mouseButtonHeld = true;
}
var mouseUpVar = function() {
//alert("mouseUp");
mouseButtonHeld = false;
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
}
// mouse Up/Down Switch end
//functions end
//listeners
canvasVar.addEventListener('mousemove', addPointToCanvasVar);
canvasVar.addEventListener('mouseup', mouseUpVar);
canvasVar.addEventListener('mousedown', mouseDownVar);
canvasVar.addEventListener('mouseout', clearPathIfMouseCursorLeavesCanvasFunc);
//listeners end
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Confident Drawing</title>
</head>
<body style="margin: 0">
<canvas id="canvasHtmlElement" style="display: block;">
Your Browser does not support Canvas! Please update to a newer version.
</canvas>
<script src="main_0.06.js"></script>
</body>
</html>
If you don't get what I mean: Run the snippet and draw a line as fast as you can while exiting the canvas.
The reason the line ends prematurely near the edge when you quickly draw a line across the edge is because the last mousemove event fired when the mouse was still in the canvas and just short of the edge, and the very next mousemove event fired after your mouse left the canvas. To fix that problem, simply draw your line from the last recorded mouse position in the canvas to the one outside of the canvas as soon as the mouseout event fires.
You can add a new global variable mousePosition and initialize it to {x:0,y:0}. Every time mousemove fires (whenever you call addPointToCanvasVar), record the e.clientX and e.clientY to your mousePosition. Then when mouseout fires (whenever you call clearPathIfMouseCursorLeavesCanvasFunc), draw the rest of the line from mousePosition to the current e.clientX and e.clientY position. This will complete the line to the end of the canvas edge.
I'm building a drawing board app in JavaScript using the Kinetic.js library. I'm having an issue with performance in the code I implemented for freehand drawing. Basically, I create an invisible rectangle that is the size of the stage, then attach event handlers to it to determine where to place the drawn line. Each time the mouse moves with the left-click button held down, I add the mouse coordinated to an array and use the points in that array to map my line. There is about a one second delay between the mouse movement and the line actually being rendered. I'm not sure if this delay is caused by an error in my own code or limitations in the Kinetic library. Here is the code:
Whiteboard.prototype.initializeDrawings = function() {
// Create an invisible shape that will act as an event listener
var background = new Kinetic.Rect({
x: 0,
y: 0,
width: this.stage.getWidth(),
height: this.stage.getHeight(),
});
this.mouseDrag = false;
// Attach handlers
background.on('mousedown touchstart', function(e) {
this.mouseDrag = true;
this.currentLine = [];
});
// Save a copy of whiteboard instance
var wb = this;
background.on('mousemove touchmove', function(e) {
if(this.mouseDrag === true) {
this.currentLine.push([e.clientX, e.clientY]);
wb.userDrawings.clear();
wb.userDrawings.add(new Kinetic.Line({
points: this.currentLine,
stroke: 'red',
strokeWidth: 4,
lineCap: 'round',
lineJoin: 'round'
}));
wb.stage.add(wb.userDrawings);
}
});
background.on('mouseup touchstop', function(e) {
this.mouseDrag = false;
});
this.stage.add(new Kinetic.Layer().add(background));
};
Overall, the code works, but because of the requirements for this application, I need to significantly minimize the delay between moving the mouse and rendering the path.
You don't want to be creating a new Kinetic.Line with every mousemove...
To gain better performance:
Instead of creating a new Kinetic.Line with every mousemove, create a single new Line in the mousedown handler and add points to that existing line in mousemove.
// a var which will eventually hold a Kinetic.Line (in your class or in global scope)
var myExistingLine;
// in mousedown
myExistingLine=new Kinetic.Line({ ...
// in mousemove
currentLine.push([mouseX,mouseY]);
myExistingLine.setPoints(currentLine);
myExistingLine.draw(); // or layer.drawScene();
To squeeze maximum performance:
Create a Kinetic.Shape which gives you access to a wrapped canvas context. Let the user draw their polyline on that context. After the user has created their polyline you can put those points in a new Kinetic.Line to get the benefits of a "managed" polyline--and remove the Kinetic.Shape.