How to Apply Marquee on KineticJS Shapes?

KineticJS is a JavaScript library to interact with HTML5 Canvas. Using KineticJS with less line of codes you can do high performance based animations, layering, drag n drop, filtering etc. KineticJS supports desktop & high end mobile devices. Shapes like Rectangle, Circle, Ellipse, Line & Polygon you can easily draw using KineticJS. After draw the shape to re-size or to rotate the shapes you need marquee.

Marquee is an identity to selected shapes. In this session let us share the marquee class to apply on any shape using KineticJS. Here marquee.js is the class file where all the related functions are added to select a shape. Copy the index.html & marquee.js file to a folder. Open the index file. In this example marquee.js is a JavaScript class. You can refer this in your program. Create an instance of marquee class & using show method apply marquee on the shapes. Using hide method you can remove marquee from the applied shapes.

Index.html

<!DOCTYPE HTML>

<html>

<head>

<title>How to apply marquee on KineticJS Shapes?</title>

<script src=”http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.4.3.min.js” type=”text/javascript”></script>

<script src=”http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js”></script>

<script src=”marquee.js” type=”text/javascript”></script>

</head>

<body>

<div id=”stagetodraw”></div>

<script type=”text/javascript”>

/* Drawing Stage */

var stage = new Kinetic.Stage({

container: ‘stagetodraw’,

width: 640,

height: 380,

draggable: false

});

/* Drawing Rectangle over Stage */

var bgRectangle = new Kinetic.Rect({

x: 0,

y: 0,

width: stage.getWidth(),

height: stage.getHeight(),

fill: ‘#E5E4E2’,

draggable: false

});

bgRectangle.on(‘click’, function() {

mar.hide();

});

/* Creating a Layer */

var layer = new Kinetic.Layer();

/* Adding base Rectangle to Layer */

layer.add(bgRectangle);

var mar = null;

/* Rectangle for Marquee */

var marqueeRectangle = new Kinetic.Rect({

x: 100,

y: 100,

width: 400,

height: 150,

fill: ‘lightblue’,

draggable: true

});

marqueeRectangle.on(‘click’, function() {

/* Creating instance for marquee class */

mar = new Marquee(marqueeRectangle);

/* Using show method showing marquee for rectangle */

mar.show();

});

/* Adding shape to Layer */

layer.add(marqueeRectangle);

/* Adding Layer to Stage */

stage.add(layer);

</script>

</body>

</html>

Marquee.js

var Marquee = function (shape) {

/* Declaration of marquee settings */

var RESIZE_RECT_FILL_COLOR = “green”;

var RESIZE_CIRCLE_FILL_COLOR = “blue”;

var RESIZE_LINE_STROKE_COLOR = “darkgray”;

var RESIZE_LINE_SHORT_WIDTH = 20;

var RESIZE_RECT_WIDTH_FACTOR = 2;

var RESIZE_CIRCLE_WIDTH_FACTOR = 1;

var STROKE_WIDTH = 5;

var ROT_DEG = 10;

/* Declaring the points for marquee */

var CP_ACTION = {

NONE: 0,

CP_LEFT: 1,

CP_TOP: 2,

CP_RIGHT: 3,

CP_BOTTOM: 4,

CP_TOP_LEFT: 5,

CP_TOP_RIGHT: 6,

CP_BOTTOM_LEFT: 7,

CP_BOTTOM_RIGHT: 8,

CP_ROTATE: 9

};

var exports = {};

var _shape = null;

var _group = null;

var _layer = null;

var _stage = null;

var _bSelected = false;

var _cpAction = null;

var _marX = null;

var _marY = null;

var _marWidth = null;

var _marHeight = null;

function init() {

var stroke = STROKE_WIDTH;

getMarDimensions(stroke);

_group = new Kinetic.Group({

id: “marquee”,

name: “marqueeGroup”,

x: _marX, //0,

y: _marY, //0,

width: _marWidth,

height: _marHeight,

draggable: true

});

// move shape to this group

_shape.setX(0);

_shape.setY(0);

_shape.moveTo(_group);

// resizing border lines

createBorderLines(stroke / 2, 0, 0, _marHeight, _marWidth, false);

// control points

createControlPoints(stroke, 0, 0, _marHeight, _marWidth, false);

_layer.add(_group);

_stage.draw();

}

/* Show method to display marquee */

exports.show = function () {

_bSelected = true;

for (i = 0; i < _group.children.length; i++) {

_group.children[i].show();

}

_group.setDraggable(true);

_shape.setDraggable(true);

_stage.draw();

};

/* Hide method to hide marquee */

exports.hide = function () {

_bSelected = false;

for (i = 0; i < _group.children.length; i++) {

if (_group.children[i]._id != _shape._id) {

_group.children[i].hide();

}

}

_group.setDraggable(false);

_shape.setDraggable(false);

_stage.draw();

};

exports.refresh = function (prevX, prevY) {

var stroke = STROKE_WIDTH;

getMarDimensions(stroke);

createBorderLines(stroke, 0, 0, _marHeight, _marWidth, true);

createControlPoints(stroke, 0, 0, _marHeight, _marWidth, true);

_stage.draw();

}

function getMarDimensions(stroke) {

var offset = _shape.getOffset();

var scale = _shape.getScale();

switch (_shape.shapeType) {

case“Rect”:

_marWidth = _shape.getWidth() * scale.x;

_marHeight = _shape.getHeight() * scale.y;

_marX = (_shape.getX());

_marY = (_shape.getY());

break;

default:

_marWidth = 100;

_marHeight = 100;

break;

}

}

/* Function to draw border line for a shape */

function createBorderLines(stroke, x, y, height, width, refresh) {

if (refresh) {

var borderLines = _group.get(“.borderLines”)[0];

borderLines.setPoints([x + (width / 2), y – RESIZE_LINE_SHORT_WIDTH,

x + (width / 2), y,

x + width, y,

x + width, y + height,

x, y + height,

x, y,

x + (width / 2), y]);

}

else {

var borderLines = new Kinetic.Line({

id: “marquee”,

name: “borderLines”,

points: [x + (width / 2), y – RESIZE_LINE_SHORT_WIDTH,

x + (width / 2), y,

x + width, y,

x + width, y + height,

x, y + height,

x, y,

x + (width / 2), y],

dashArray: [STROKE_WIDTH, STROKE_WIDTH, STROKE_WIDTH, STROKE_WIDTH],

stroke: RESIZE_LINE_STROKE_COLOR,

strokeWidth: stroke,

lineCap: ’round’,

lineJoin: ’round’,

draggable: true,

dragOnTop: false

});

borderLines.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “move”);

});

borderLines.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

_group.add(borderLines);

}

}

function handleResizeRotate(anchor) {

if (anchor) {

var prevX = _marX;

var prevY = _marY;

var currX = _marX + anchor.getX();

var currY = _marY + anchor.getY();

var scale = _shape.getScale();

var offset = _shape.getOffset();

var width = _shape.getWidth();

var height = _shape.getHeight();

var aspectRatio = width / height;

switch (anchor.getName()) {

case“cpRotate”:

var haf = prevX + ((width * scale.x) / 2);

var isRight = currX > haf ? true : false;

//center origin so rotation happens around center of group

if (!_group.hasAdjustedForRotation) {

_group.setOffset(_group.getWidth() / 2, _group.getHeight() / 2);

_group.move(_group.getScale().x * (_group.getWidth() / 2), _group.getScale().y * (_group.getHeight() / 2));

_group.hasAdjustedForRotation = true;

}

if (isRight) {

_group.rotateDeg(1);

}

else {

_group.rotateDeg(-1);

}

exports.refresh();

break;

case“cpTop”:

currY += (anchor.getHeight() / 2);

var diffY = (currY – prevY) / scale.y;

_shape.setHeight(parseFloat(height – diffY), anchor);

_group.setY(_group.getY() + (diffY * scale.y));

exports.refresh();

break;

case“cpLeft”:

currX += (anchor.getWidth() / 2);

var diffX = (currX – prevX) / scale.x;

_shape.setWidth(parseFloat(width – diffX), anchor);

_group.setX(_group.getX() + (diffX * scale.x));

exports.refresh();

break;

case“cpTopLeft”:

currY += (anchor.getHeight() / 2);

currX += (anchor.getWidth() / 2);

var diffX = (currX – prevX) / scale.x;

var diffY = (currY – prevY) / scale.y;

_shape.setWidth(parseFloat(width – diffX), anchor);

_shape.setHeight(parseFloat(height – diffY), anchor);

_group.setX(_group.getX() + (diffX * scale.x));

_group.setY(_group.getY() + (diffY * scale.y));

exports.refresh();

break;

case“cpBottomLeft”:

currY -= (anchor.getHeight() / 2);

currX += (anchor.getWidth() / 2);

var diffX = (currX – prevX) / scale.x;

var diffY = (currY – prevY) / scale.y;

_shape.setWidth(parseFloat(width – diffX), anchor);

_shape.setHeight(parseFloat(height + parseFloat(diffY – height)), anchor);

_group.setX(_group.getX() + (diffX * scale.x));

exports.refresh();

break;

case“cpRight”:

currX += (anchor.getWidth() / 2);

var diffX = (currX – prevX) / scale.x;

_shape.setWidth(parseFloat(width + parseFloat((diffX – width))), anchor);

exports.refresh();

break;

case“cpTopRight”:

currY += (anchor.getHeight() / 2);

currX -= (anchor.getWidth() / 2);

var diffX = (currX – prevX) / scale.x;

var diffY = (currY – prevY) / scale.y;

_shape.setWidth(parseFloat(width + parseFloat(diffX – width)), anchor);

_shape.setHeight(height – diffY, anchor);

_group.setY(_group.getY() + (diffY * scale.y));

exports.refresh();

break;

case“cpBottomRight”:

currY += (anchor.getHeight() / 2);

currX += (anchor.getWidth() / 2);

var diffX = (currX – prevX) / scale.x;

var diffY = (currY – prevY) / scale.y;

_shape.setWidth(parseFloat(width + parseFloat(diffX – width)), anchor);

_shape.setHeight(parseFloat(height + parseFloat(diffY – height)), anchor);

_group.setWidth(parseFloat(width + parseFloat(diffX – width)));

_group.setHeight(parseFloat(height + parseFloat(diffY – height)));

exports.refresh();

break;

case“cpBottom”:

currY += (anchor.getHeight() / 2);

var diffY = (currY – prevY) / scale.y;

_shape.setHeight(parseFloat(height + parseFloat(diffY – height)), anchor);

exports.refresh();

break;

default:

break;

}

}

}

function createControlPoints(stroke, x, y, height, width, refresh) {

// rotate control point

if (refresh) {

var cpRotate = _group.get(“.cpRotate”)[0];

cpRotate.setX(x + (width / 2));

cpRotate.setY(y – RESIZE_LINE_SHORT_WIDTH);

}

else {

var cpRotate = new Kinetic.Circle({

id: “marquee”,

name: “cpRotate”,

x: x + (width / 2),

y: y – RESIZE_LINE_SHORT_WIDTH,

radius: stroke * RESIZE_CIRCLE_WIDTH_FACTOR,

stroke: RESIZE_CIRCLE_FILL_COLOR,

fill: RESIZE_CIRCLE_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpRotate.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “wait”);

});

cpRotate.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpRotate.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_ROTATE;

_group.setDraggable(false);

});

cpRotate.on(“mouseup dragend”, function () {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpRotate.on(“dragmove”, function (evt) {

handleResizeRotate(cpRotate);

});

_group.add(cpRotate);

}

// resizing left control point

if (refresh) {

var cpLeft = _group.get(“.cpLeft”)[0];

cpLeft.setX(x – stroke);

cpLeft.setY(y – stroke + (height / 2));

}

else {

var cpLeft = new Kinetic.Rect({

id: “marquee”,

name: “cpLeft”,

x: x – stroke,

y: y – stroke + (height / 2),

width: stroke * RESIZE_RECT_WIDTH_FACTOR,

height: stroke * RESIZE_RECT_WIDTH_FACTOR,

stroke: RESIZE_RECT_FILL_COLOR,

fill: RESIZE_RECT_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpLeft.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “e-resize”);

});

cpLeft.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpLeft.on(“mousedown”, function (evt) {

_cpAction = CP_ACTION.CP_LEFT;

_group.setDraggable(false);

});

cpLeft.on(“mouseup dragend”, function (evt) {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpLeft.on(“dragmove”, function (evt) {

handleResizeRotate(cpLeft);

});

_group.add(cpLeft);

}

// resizing right control point

if (refresh) {

var cpRight = _group.get(“.cpRight”)[0];

cpRight.setX(x – stroke + width);

cpRight.setY(y – stroke + (height / 2));

}

else {

var cpRight = new Kinetic.Rect({

id: “marquee”,

name: “cpRight”,

x: x – stroke + width,

y: y – stroke + (height / 2),

width: stroke * RESIZE_RECT_WIDTH_FACTOR,

height: stroke * RESIZE_RECT_WIDTH_FACTOR,

stroke: RESIZE_RECT_FILL_COLOR,

fill: RESIZE_RECT_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpRight.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “e-resize”);

});

cpRight.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpRight.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_RIGHT;

_group.setDraggable(false);

});

cpRight.on(“mouseup dragnend”, function (evt) {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpRight.on(“dragmove”, function (evt) {

handleResizeRotate(cpRight);

});

_group.add(cpRight);

}

if (_shape.shapeType == “Line”)

return;

// resizing top control point

if (refresh) {

var cpTop = _group.get(“.cpTop”)[0];

cpTop.setX(x – stroke + (width / 2));

cpTop.setY(y – stroke);

}

else {

var cpTop = new Kinetic.Rect({

id: “marquee”,

name: “cpTop”,

x: x – stroke + (width / 2),

y: y – stroke,

width: stroke * RESIZE_RECT_WIDTH_FACTOR,

height: stroke * RESIZE_RECT_WIDTH_FACTOR,

stroke: RESIZE_RECT_FILL_COLOR,

fill: RESIZE_RECT_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpTop.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “s-resize”);

});

cpTop.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpTop.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_TOP;

_group.setDraggable(false);

});

cpTop.on(“mouseup dragnend”, function () {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpTop.on(“dragmove”, function (evt) {

handleResizeRotate(cpTop);

});

_group.add(cpTop);

}

// resizing bottom control point

if (refresh) {

var cpBottom = _group.get(“.cpBottom”)[0];

cpBottom.setX(x – stroke + (width / 2));

cpBottom.setY(y – stroke + height);

}

else {

var cpBottom = new Kinetic.Rect({

id: “marquee”,

name: “cpBottom”,

x: x – stroke + (width / 2),

y: y – stroke + height,

width: stroke * RESIZE_RECT_WIDTH_FACTOR,

height: stroke * RESIZE_RECT_WIDTH_FACTOR,

stroke: RESIZE_RECT_FILL_COLOR,

fill: RESIZE_RECT_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpBottom.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “s-resize”);

});

cpBottom.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpBottom.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_BOTTOM;

_group.setDraggable(false);

});

cpBottom.on(“mouseup dragnend”, function () {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpBottom.on(“dragmove”, function (evt) {

handleResizeRotate(cpBottom);

});

_group.add(cpBottom);

}

// resizing top-left control point

if (refresh) {

var cpTopLeft = _group.get(“.cpTopLeft”)[0];

cpTopLeft.setX(x);

cpTopLeft.setY(y);

}

else {

var cpTopLeft = new Kinetic.Circle({

id: “marquee”,

name: “cpTopLeft”,

x: x,

y: y,

radius: stroke * RESIZE_CIRCLE_WIDTH_FACTOR,

stroke: RESIZE_CIRCLE_FILL_COLOR,

fill: RESIZE_CIRCLE_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpTopLeft.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “se-resize”);

});

cpTopLeft.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpTopLeft.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_TOP_LEFT;

_group.setDraggable(false);

});

cpTopLeft.on(“mouseup dragnend”, function () {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpTopLeft.on(“dragmove”, function (evt) {

handleResizeRotate(cpTopLeft);

});

_group.add(cpTopLeft);

}

// resizing top-right control point

if (refresh) {

var cpTopRight = _group.get(“.cpTopRight”)[0];

cpTopRight.setX(x + width);

cpTopRight.setY(y);

}

else {

var cpTopRight = new Kinetic.Circle({

id: “marquee”,

name: “cpTopRight”,

x: x + width,

y: y,

radius: stroke * RESIZE_CIRCLE_WIDTH_FACTOR,

stroke: RESIZE_CIRCLE_FILL_COLOR,

fill: RESIZE_CIRCLE_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpTopRight.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “ne-resize”);

});

cpTopRight.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpTopRight.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_TOP_RIGHT;

_group.setDraggable(false);

});

cpTopRight.on(“mouseup dragnend”, function () {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpTopRight.on(“dragmove”, function (evt) {

handleResizeRotate(cpTopRight);

});

_group.add(cpTopRight);

}

// resizing bottom-left control point

if (refresh) {

var cpBottomLeft = _group.get(“.cpBottomLeft”)[0];

cpBottomLeft.setX(x);

cpBottomLeft.setY(y + height);

}

else {

var cpBottomLeft = new Kinetic.Circle({

id: “marquee”,

name: “cpBottomLeft”,

x: x,

y: y + height,

radius: stroke * RESIZE_CIRCLE_WIDTH_FACTOR,

stroke: RESIZE_CIRCLE_FILL_COLOR,

fill: RESIZE_CIRCLE_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpBottomLeft.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “sw-resize”);

});

cpBottomLeft.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpBottomLeft.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_BOTTOM_LEFT;

_group.setDraggable(false);

});

cpBottomLeft.on(“mouseup dragnend”, function () {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpBottomLeft.on(“dragmove”, function (evt) {

handleResizeRotate(cpBottomLeft);

});

_group.add(cpBottomLeft);

}

// resizing bottom-right control point

if (refresh) {

var cpBottomRight = _group.get(“.cpBottomRight”)[0];

cpBottomRight.setX(x + width);

cpBottomRight.setY(y + height);

}

else {

var cpBottomRight = new Kinetic.Circle({

id: “marquee”,

name: “cpBottomRight”,

x: x + width,

y: y + height,

radius: stroke * RESIZE_CIRCLE_WIDTH_FACTOR,

stroke: RESIZE_CIRCLE_FILL_COLOR,

fill: RESIZE_CIRCLE_FILL_COLOR,

draggable: true,

dragOnTop: false

});

cpBottomRight.on(“mouseenter”, function (evt) {

$(“body”).css(“cursor”, “se-resize”);

});

cpBottomRight.on(“mouseleave”, function (evt) {

$(“body”).css(“cursor”, “default”);

});

cpBottomRight.on(“mousedown”, function () {

_cpAction = CP_ACTION.CP_BOTTOM_RIGHT;

_group.setDraggable(false);

});

cpBottomRight.on(“mouseup dragnend”, function () {

_cpAction = CP_ACTION.NONE;

_group.setDraggable(true);

});

cpBottomRight.on(“dragmove”, function (evt) {

handleResizeRotate(cpBottomRight);

});

_group.add(cpBottomRight);

}

}

_shape = shape;

_layer = shape.getLayer();

_stage = shape.getStage();

init();

return exports;

};