I need help to draw a polygon and snap to a grid. I also would like to be able to change the dimensions after it has been drawn. Does anyone know where I can find information on how to do this in konva js.
I have googled it but can't seem to get anywhere. the support docs from konva.js are good but can't find exactly what im looking for.
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@8.4.3/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Simple Window drawing</title>
<style>
body {
padding: 0;
margin: 0;
overflow: hidden;
}
#container {
border: 1px solid grey;
background-color: lightgrey;
}
#controls {
position: absolute;
top: 4px;
left: 4px;
}
</style>
</head>
<body>
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<div id='container1'
style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;">
</div>
<script>
// Set up the canvas / stage
var s1 = new Konva.Stage({container: 'container1', width: 600, height: 300});
// Add a layer for line
var lineLayer = new Konva.Layer({draggable: false});
s1.add(lineLayer);
// Add a layer for drag points
var pointLayer = new Konva.Layer({draggable: false});
s1.add(pointLayer);
// Add a rectangle to layer to catch events. Make it semi-transparent
var r = new Konva.Rect({x:0, y: 0, width: 600, height: 300, fill: 'black', opacity: 0.1})
pointLayer.add(r)
// Everything is ready so draw the canvas objects set up so far.
s1.draw()
// generic canvas end
// Class for the draggable point
// Params: path = the parent object, opts = position info, doPush = should we just make it or make it AND store it
var DragPoint = function(path, opts, doPush){
var path = path;
this.x = opts.x;
this.y = opts.y;
this.fixed = opts.fixed;
this.id = randId(); // random id.
if (doPush){ // in some cases we want to create the pt then insert it in the run of the array and not always at the end
path.pts.push(this);
}
// random id generator
function randId() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10);
}
// mark the pt as fixed - important state, shown by filled point
this.makeFixed = function(){
this.fixed = true;
s1.find('#' + this.id)
.fill(path.fillColor);
}
this.kill = function(){
s1.find('#' + this.id)
.remove();
}
this.draw = function(){
// Add point & pt
var circleId = this.id;
var pt = new Konva.Circle({
id: circleId,
x: this.x,
y: this.y,
radius: 3,
opacity: path.pointOpacity,
strokeWidth: 3,
stroke: path.strokeColor,
fill: 'transparent',
draggable: 'true'
})
pt.on('dragstart', function(){
path.drawState = 'dragging';
})
pt.on('dragmove', function(){
var pos = this.getPosition();
path.updatePt(this.id(), pos)
path.calc(this.id());
path.draw();
})
pt.on('dragend', function(){
path.drawState = 'drawing';
var pos = this.getPosition();
path.updatePt(this.getId(), pos);
path.splitPts(this.getId());
path.draw();
})
if (this.fixed){
this.makeFixed();
}
path.ptLayer.add(pt);
path.draw();
}
}
var path = function() {
this.lineLayer = null;
this.ptLayer = null;
this.drawState = '';
this.pts = []; // array of dragging points.
this.startPt = null;
this.endPt = null;
// Add a point to the path.
this.addPt = function(pos, isFixed){
if (this.drawState === 'dragging'){ // do not add a new point because we were just dragging another
return null;
}
this.startPt = this.startPt || pos;
this.endPt = pos;
// create this new pt
var pt = new DragPoint(this, {x: this.endPt.x, y: this.endPt.y, fixed: isFixed}, true, "A");
pt.draw();
pt.makeFixed(); // always fixed for manual points
// if first point ignore the splitter process
if (this.pts.length > 0){
this.splitPts(pt.id, true);
}
this.startPt = this.endPt; // remember the last point
this.calc(); // calculate the line points from the array
this.draw(); // draw the line
}
// Position the points.
this.calc = function (draggingId){
draggingId = (typeof draggingId === 'undefined' ? '---' : draggingId); // when dragging an unfilled point we have to override its automatic positioning.
for (var i = 1; i < this.pts.length - 1; i = i + 1){
var d2 = this.pts[i];
if (!d2.fixed && d2.id !== draggingId){ // points that have been split are fixed, points that have not been split are repositioned mid way along their line segment.
var d1 = this.pts[i - 1];
var d3 = this.pts[i + 1];
var pos = this.getHalfwayPt(d1, d3);
d2.x = pos.x;
d2.y = pos.y;
}
s1.find('#' + d2.id).position({x: d2.x, y: d2.y}); // tell the shape where to go
}
}
// draw the line
this.draw = function (){
if (this.drawingLine){
this.drawingLine.remove();
}
this.drawingLine = this.newLine(); // initial line point
for (var i = 0; i < this.pts.length; i = i + 1){
this.drawingLine.points(this.drawingLine.points().concat([this.pts[i].x, this.pts[i].y]))
}
this.ptLayer.draw();
this.lineLayer.draw();
}
// When dragging we need to update the position of the point
this.updatePt = function(id, pos){
for (var i = 0; i < this.pts.length; i = i + 1){
if (this.pts[i].id === id){
this.pts[i].x = pos.x;
this.pts[i].y = pos.y;
break;
}
}
}
// Function to add and return a line object. We will extend this line to give the appearance of drawing.
this.newLine = function(){
var line = new Konva.Line({
stroke: this.color,
strokeWidth: this.width,
lineCap: 'round',
lineJoin: 'round',
tension : .1
});
this.lineLayer.add(line)
return line;
}
// make pts either side of the split
this.splitPts = function(id, force){
var idx = -1;
// find the pt in the array
for (var i = 0; i < this.pts.length; i = i + 1){
if (this.pts[i].id === id){
idx = i;
if (this.pts[i].fixed && !force){
return null; // we only split once.
}
//break;
}
}
// If idx is -1 we did not find the pt id !
if ( idx === -1){
return null
}
else if (idx === 0 ) {
return null
}
else { // pt not = 0 or max
// We are now going to insert a new pt either side of the one we just dragged
var d1 = this.pts[idx - 1]; // previous pt to the dragged pt
var d2 = this.pts[idx ]; // the pt pt
var d3 = this.pts[idx + 1]; // the next pt after the dragged pt
d2.makeFixed()// flag this pt as no longer splittable
// get point midway from prev pt and dragged pt
var pos = this.getHalfwayPt(d1, d2);
var pt = new DragPoint(this, {x: pos.x, y: pos.y, foxed: false}, false, "C");
pt.draw();
this.pts.splice(idx, 0, pt);
if (d3){
// get point midway from dragged pt to next
pos = this.getHalfwayPt(d2, d3);
var pt = new DragPoint(this, {x: pos.x, y: pos.y, foxed: false}, false, "D");
pt.draw();
this.pts.splice(idx + 2, 0, pt); // note idx + 2 !
}
}
}
// convert last point array entry to handy x,y object.
this.getPoint = function(pts){
return {x: pts[pts.length - 2], y: pts[pts.length - 1]};
}
this.getHalfwayPt = function(d1, d2){
var pos = {
x: d1.x + (d2.x - d1.x)/2,
y: d1.y + (d2.y - d1.y)/2
}
return pos;
}
this.exportPoints = function(){
var list = [], pt;
console.log('pts=' + this.pts.length)
for (var i = 0; i < this.pts.length; i = i + 1){
pt = this.pts[i]
if (pt.fixed){
console.log('push ' + i)
list.push({x: pt.x, y: pt.y});
}
}
return list;
}
}
var path = new path();
path.lineLayer = lineLayer;
path.ptLayer = pointLayer;
path.fillColor = 'AliceBlue';
path.strokeColor = 'blue';
path.pointOpacity = 1;
path.pointRadius = 3;
path.color = '#666666'
// Listen for mouse up on the stage to know when to draw points
s1.on('mouseup touchend', function () {
path.addPt(s1.getPointerPosition(), true);
});
// jquery is used here simply as a quick means to make the buttons work.
// Controls for points export
$('#export').on('click', function(){
if ($(this).html() === "Hide"){
$(this).html('Export');
$('#points').hide();
}
else {
$(this).html('Hide');
$('#points')
.css('display', 'block')
.val(JSON.stringify(path.exportPoints()));
}
})
// reset button
$('#reset').on('click', function(){
path.reset();
})
</script>
</body>
</html>