# A Scalable, Draggable, Anchored Triangle in kinetic.js 10:31 PM Javascript , Kinetic 5 Comments

I don't do a whole bunch on the front end, which is probably my only (weak) explanation as to why I am just starting to play with HTML5 canvas.  However, for the past three nights, I've found myself playing with it and creating stuff that would have blown my (tiny) mind just a few years back. After years of producing rigid web pages of columns and rows, there is nothing quite as liberating as seeing a scalene triangle glide across a web page.  So, you can imagine my glee when I found out that accomplishing that was a simple as:
```<html>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v3.10.0.js"></script>
<script type="text/javascript">
var stage = new Kinetic.Stage({
container : "container",
height : 200
});

var layer = new Kinetic.Layer();

var triangle = new Kinetic.Polygon({
stroke : "red",
strokeWidth : 4,
points : [ 60, 100, 90, 100, 90, 140 ],
draggable : true
});

}
</script>
<style>
#container {
border: 1px solid black;
}
</style>
<body onmousedown="return false;">
<div id="container">
</div>
</body>
</html>
```
So, after about 60 seconds of sliding my triangle across the page (and unsuccessfully trying to get my seven-year-old to appreciate its utter awesomeness), I decided that maybe it wouldn't be so hard to create a much more dynamic triangle that could be resized and reshaped as well.

### Draggable Vertices

Turns out that there was a simple tutorial online for reshapable polygons. I simply adapted that code for my triangle. The basic approach is to create three draggable anchor points that are placed in the same layer as the polygon and then override the draw function for that layer. Each time there is a redraw, the vertices of the triangle are reassessed in accordance with the position of the three anchor points. The challenge here was making it draggable as well as anchorable. With movable anchors as vertices, the shape of the triangle is dependent on the position of the anchors. Once it is draggable, the dependency is reversed, and the anchors are dependent on the position of the triangle. To get this right we need to do three things. First, we need to be able to differentiate the two events. KineticJs provides support for dragstart, dragend, and dragmove as bindable events, but for now, it is easy enough for us to differentiate between draggable and non-draggable paint scenarios with the isDragging method:
```if ( polygon.isDragging() ) {
// move the anchor points
moveAnchorPoints();
} else {
// move the triangle to match the anchor points
moveTriangle();
}
```
Easy enough. Second, we need to implement moveTriangle. KineticJs gives us the setPoints method, so the api answers the call again:
```function moveTriangle(triangle, points) {
// voila!
triangle.setPoints(points);
}
```
Third, we need to implement moveAnchorPoints(). This is where life gets trickier. One might think that calling triangle.getPoints and setting the anchors to those would work, but this is not so. While I don't yet understand the reasons, getPoints does not get updated before the drawTriangle execution on a drag event. This means that using getPoints as a reference will simply leave the points in the dust as the triangle is dragged out from underneath them. It turns out that getX and getY are updated before drawTriangle is drawn. Sweet. The problem is that this is a single point in the upper-left-hand corner of the polygon, so it is not representative of all three points. It turns out that this is enough, though, since we can use that point to detect how much the triangle has moved. Dragging affects all the vertices equally, so if we keep track of where the polygon's (x,y) before the drag event began and where it is at the time of the draw method, then we can just change the anchor points relative to that change. The calculations are simple. I add a custom property to my polygon that tracks the last known position of the triangle prior to the event:
```triangle.was = { x : 0, y : 0 };
```
and then account for the movement in moveAnchorPoints():
```function moveAnchorPoints(layer) {
var anchors = layer.get(".anchor");
var triangle = layer.get(".triangle")[0];

for ( var i = 0; i < anchors.length; i ++ ) {
// the difference between the current position of triangle and the last known position is also the same amount that each anchor needs to move
anchors[i].setX(anchors[i].getX() + (triangle.getX() - triangle.was.x));
anchors[i].setY(anchors[i].getY() + (triangle.getY() - triangle.was.y));
}

// record the new known location
triangle.was.x = triangle.getX();
triangle.was.y = triangle.getY();
}
```
Now, we have a draggable and adjustable triangle:
```<html>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v3.10.0.js"></script>
<script type="text/javascript">
// the circle anchor points
function buildAnchor(layer, x, y, name) {
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: "#666",
fill: "#ddd",
strokeWidth: 2,
draggable: true,
name : name
});

anchor.on("mouseover", function() {
document.body.style.cursor = "pointer";
this.setStrokeWidth(4);
layer.draw();
});
anchor.on("mouseout", function() {
document.body.style.cursor = "default";
this.setStrokeWidth(2);
layer.draw();
});

return anchor;
}

function buildTriangle(layer, points, name) {
var triangle = new Kinetic.Polygon({
stroke : "red",
strokeWidth : 4,
name : name,
draggable : true
});

triangle.a = buildAnchor(layer, points[0], points[1], "anchor");
triangle.b = buildAnchor(layer, points[2], points[3], "anchor");
triangle.c = buildAnchor(layer, points[4], points[5], "anchor");
triangle.was = { x : 0, y : 0 };

return triangle;
}

function drawTriangle() {
var triangle = this.get(".triangle")[0];

if ( !triangle.isDragging() ) {
triangle.setPoints([ triangle.a.attrs.x - triangle.was.x,
triangle.a.attrs.y - triangle.was.y,
triangle.b.attrs.x - triangle.was.x,
triangle.b.attrs.y - triangle.was.y,
triangle.c.attrs.x - triangle.was.x,
triangle.c.attrs.y - triangle.was.y ]);
} else {
var anchors = this.get(".anchor");

for ( var i = 0; i < anchors.length; i ++ ) {
anchors[i].setX(anchors[i].getX() + (triangle.getX() - triangle.was.x));
anchors[i].setY(anchors[i].getY() + (triangle.getY() - triangle.was.y));
}

triangle.was.x = triangle.getX();
triangle.was.y = triangle.getY();
}
}

var stage = new Kinetic.Stage({
container: "container",
height: 200
});

var layer = new Kinetic.Layer({
drawFunc : drawTriangle
});

var triangle = buildTriangle(layer, [60, 100, 90, 100, 90, 140], "triangle");
triangle.moveToBottom();

// add the layer to the stage
}
</script>
<style>
#container {
border: 1px solid black;
}
</style>
<body onmousedown="return false;">
<div id="container">
</div>
</body>
</html>
```

### Scalable Polygons

Last is making it scalable. I wanted to make this work with the scroll wheel, but haven't had the time, yet, to figure that out. So, I am controlling it via the 'q' and 'w' keys. Again, the calculations are pretty simple. Scaling a convex polygon is about finding the in-center of the polygon and moving each vertex out by the appropriate factor away from the in-center. Moving it away amounts to simply moving it along the line defined between the vertex and the in-center (think y = mx + b). Calculating the in-center is just taking the average x and average y of the three vertices:
```triangle.calculateCenter = function() {
this.center.x = ( this.a.attrs.x + this.b.attrs.x + this.c.attrs.x ) / 3;
this.center.y = ( this.a.attrs.y + this.b.attrs.y + this.c.attrs.y ) / 3;
};
```
And moving it along the line is pretty easy, too. If we treat the center as an offset from (0,0), than we can just multiply the point less the offset:
```triangle.scaleAnchor = function(anchor, factor) {
anchor.attrs.x = ( anchor.attrs.x - this.center.x ) * factor + this.center.x;
anchor.attrs.y = ( anchor.attrs.y - this.center.y ) * factor + this.center.y;
};
```
The rest is pretty basic. I tie the keypress event to the document and call scaleAnchor accordingly. In the draw handler, I recalculate the center point whenever a drag event or an anchor adjustment occurs:
```<html>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v3.10.0.js"></script>
<script type="text/javascript">
function buildAnchor(layer, x, y, name) {
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: "#666",
fill: "#ddd",
strokeWidth: 2,
draggable: true,
name : name
});

anchor.on("mouseover", function() {
document.body.style.cursor = "pointer";
this.setStrokeWidth(4);
layer.draw();
});
anchor.on("mouseout", function() {
document.body.style.cursor = "default";
this.setStrokeWidth(2);
layer.draw();
});

return anchor;
}

function buildTriangle(layer, points, name) {
var triangle = new Kinetic.Polygon({
stroke : "red",
strokeWidth : 4,
name : name,
draggable : true
});

triangle.scaleAnchor = function(anchor, factor) {
anchor.attrs.x = ( anchor.attrs.x - this.center.x ) * factor + this.center.x;
anchor.attrs.y = ( anchor.attrs.y - this.center.y ) * factor + this.center.y;
};
triangle.calculateCenter = function() {
this.center.x = ( this.a.attrs.x + this.b.attrs.x + this.c.attrs.x ) / 3;
this.center.y = ( this.a.attrs.y + this.b.attrs.y + this.c.attrs.y ) / 3;
};

triangle.a = buildAnchor(layer, points[0], points[1], "anchor");
triangle.b = buildAnchor(layer, points[2], points[3], "anchor");
triangle.c = buildAnchor(layer, points[4], points[5], "anchor");
triangle.was = { x : 0, y : 0 };
triangle.center = { x : 0, y : 0 };

return triangle;
}

function drawTriangle() {
var triangle = this.get(".triangle")[0];

if ( !triangle.isDragging() ) {
triangle.setPoints([ triangle.a.attrs.x - triangle.was.x,
triangle.a.attrs.y - triangle.was.y,
triangle.b.attrs.x - triangle.was.x,
triangle.b.attrs.y - triangle.was.y,
triangle.c.attrs.x - triangle.was.x,
triangle.c.attrs.y - triangle.was.y ]);
} else {
var anchors = this.get(".anchor");

for ( var i = 0; i < anchors.length; i ++ ) {
anchors[i].setX(anchors[i].getX() + (triangle.getX() - triangle.was.x));
anchors[i].setY(anchors[i].getY() + (triangle.getY() - triangle.was.y));
}

triangle.was.x = triangle.getX();
triangle.was.y = triangle.getY();
}

triangle.calculateCenter.apply(triangle);
}

var stage = new Kinetic.Stage({
container: "container",
width: 578,
height: 200
});

var layer = new Kinetic.Layer({
drawFunc : drawTriangle
});

var triangle = buildTriangle(layer, [60, 100, 90, 100, 90, 140], "triangle");
triangle.moveToBottom();

document.onkeypress = function(e) {
if ( e.keyCode == 113 ) {
triangle.scaleAnchor.apply(triangle, [triangle.a, 1.05]);
triangle.scaleAnchor.apply(triangle, [triangle.b, 1.05]);
triangle.scaleAnchor.apply(triangle, [triangle.c, 1.05]);
layer.draw();
} else if ( e.keyCode == 119 ) {
triangle.scaleAnchor.apply(triangle, [triangle.a, .95]);
triangle.scaleAnchor.apply(triangle, [triangle.b, .95]);
triangle.scaleAnchor.apply(triangle, [triangle.c, .95]);
layer.draw();
}
};

// add the layer to the stage
}
</script>
<style>
#container {
border: 1px solid black;
}
</stylegt;