I need to create a kind of container to manage dynamic actions with KineticJS.
I have a simple object from which we will be able to add a circle by using a function.
Here's my code:
function Stage() {
var self = this;
self.stage = new Kinetic.Stage({
container: "museumMapContainer",
width: 500,
height: 500
});
self.layer = new Kinetic.Layer();
self.addCircle = function (x,y) {
var circle = new Kinetic.Circle({
x: x,
y: y,
radius: 40,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
circle.on('mouseover', function() {
document.body.style.cursor = 'pointer';
});
circle.on('mouseout', function() {
document.body.style.cursor = 'default';
});
self.layer.add(circle);
}
self.stage.add(self.layer);
}
stage = new Stage();
stage.addCircle(250,250);
Normally, if I don't put the code inside a function, I can easily create a circle without any problems. However, this code doesn't work and I really don't know why.
Here's a Plunker: http://plnkr.co/edit/E1fbCFMeZwGNAKhsArhm?p=preview
There are no errors in the console and nothing is showing and I don't know why...
Make sure you do layer.draw after creating your new circles:
<!DOCTYPE html>
<html>
<head>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.1.min.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div id="museumMapContainer" style="width:500px;height:500px;border:1px solid black;"></div>
<script defer="defer">
function Stage() {
var self = this;
self.stage = new Kinetic.Stage({
container: "museumMapContainer",
width: 500,
height: 500
});
self.layer = new Kinetic.Layer();
self.stage.add(self.layer);
self.addCircle = function (x,y) {
var circle = new Kinetic.Circle({
x: x,
y: y,
radius: 40,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
circle.on('mouseover', function() {
document.body.style.cursor = 'pointer';
});
circle.on('mouseout', function() {
document.body.style.cursor = 'default';
});
self.layer.add(circle);
self.layer.draw();
}
}
stage = new Stage();
stage.addCircle(250,250);
</script>
</body>
</html>
Related
I'd like to drag a Konva layer even if empty. I see that I can drag the layer only where objects are present (the code shows that you can drag where the rectangle is but nowhere else).
How can I drag the layer dragging empty parts?
Moreover, I see that also the mouseenter and mousleave aren't respected if you are out of the objects of the layer.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
</head>
<body>
<script src="js/modulePattern.js"></script>
<script src="https://unpkg.com/konva#2.4.2/konva.min.js"></script>
<div id="container"></div>
<script>
function demo() {
var stage = new Konva.Stage({
container: 'container',
width: 100,
height: 100
});
var layer = new Konva.Layer({draggable: true});
stage.add(layer);
layer.on("dragmove", function() {
console.log(this.x(), this.y())
});
layer.on("mouseenter", function() {
stage.container().style.cursor = 'pointer';
});
layer.on("mouseleave", function() {
stage.container().style.cursor = 'default';
});
var rect = new Konva.Rect({
width: 50,
height: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 5
});
layer.add(rect);
stage.draw();
}
demo();
</script>
</body>
</html>
Konva does not detect events on empty space of layers. And it is unclear how can it do it if you have several layers on the stage.
If you still need to listen on empty layers and make a layer draggable at any space you can:
Make stage draggable instead and listen events on it (click will work on empty space too).
Add a transparent rectangle as a background, so it will be used to listen to events:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
</head>
<body>
<script src="js/modulePattern.js"></script>
<script src="https://unpkg.com/konva#2.4.2/konva.min.js"></script>
<div id="container"></div>
<script>
function demo() {
var stage = new Konva.Stage({
container: 'container',
width: 300,
height: 300
});
var layer = new Konva.Layer({draggable: true});
stage.add(layer);
layer.on("mouseenter", function() {
stage.container().style.cursor = 'pointer';
});
layer.on("mouseleave", function() {
stage.container().style.cursor = 'default';
});
var rect = new Konva.Rect({
width: 50,
height: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 5
});
var back = new Konva.Rect({
fill: 'red',
width: stage.width(),
height: stage.height()
})
layer.add(back);
// move rectangle so it always stay on the screen, no matter where layer it
layer.on('dragend', function() {
back.setAbsolutePosition({x: 0, y: 0});
layer.draw();
});
layer.add(rect);
stage.draw();
}
demo();
</script>
</body>
</html>
I am inspired with KonvaJS tutorial Modify Curves with Anchor Points to make my own example which is to create multiple custom arrows.
on click on the selectionBox create an anchor.
on the creation of the third anchor create the curved arrow.
on the fourth click reset all variables in order to draw a new curved arrow.
var width = window.innerWidth;
var height = window.innerHeight;
// globals
var selectionBoxLayer, curveLayer, lineLayer, anchorLayer, quad, bezier;
function updateDottedLines() {
var q = quad;
var quadLine = lineLayer.get('#quadLine')[0];
quadLine.setPoints([q.start.attrs.x, q.start.attrs.y, q.control.attrs.x, q.control.attrs.y, q.end.attrs.x, q.end.attrs.y]);
lineLayer.draw();
}
function buildAnchor(x, y) {
var anchor = new Konva.Circle({
x: x,
y: y,
radius: 20,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
draggable: true
});
// add hover styling
anchor.on('mouseover', function() {
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
anchorLayer.draw();
});
anchor.on('mouseout', function() {
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
anchorLayer.draw();
});
anchor.on('dragend', function() {
drawCurves();
updateDottedLines();
});
anchorLayer.add(anchor);
anchorLayer.draw();
return anchor;
}
function drawCurves() {
var context = curveLayer.getContext();
var arrowLine = new Konva.Shape({
sceneFunc: function(context){
debugger;
// draw quad
context.beginPath();
context.moveTo(quad.start.attrs.x, quad.start.attrs.y);
context.quadraticCurveTo(quad.control.attrs.x, quad.control.attrs.y, quad.end.attrs.x, quad.end.attrs.y);
//Draw Arrow Head
var headlen = 10; // length of head in pixels
var angle = Math.atan2(quad.end.attrs.y - quad.control.attrs.y, quad.end.attrs.x - quad.control.attrs.x);
context.lineTo(quad.end.attrs.x-headlen*Math.cos(angle-Math.PI/6), quad.end.attrs.y-headlen*Math.sin(angle-Math.PI/6));
context.moveTo(quad.end.attrs.x, quad.end.attrs.y);
context.lineTo(quad.end.attrs.x- headlen*Math.cos(angle+Math.PI/6), quad.end.attrs.y-headlen*Math.sin(angle+Math.PI/6));
context.fillStrokeShape(this);
},
stroke: 'black',
strokeWidth: 4
});
curveLayer.add(arrowLine);
curveLayer.draw();
}
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
selectionBoxLayer = new Konva.Layer();
anchorLayer = new Konva.Layer();
lineLayer = new Konva.Layer();
// curveLayer just contains a canvas which is drawn
// onto with the existing canvas API
curveLayer = new Konva.Layer();
var quadLine = new Konva.Line({
dash: [10, 10, 0, 10],
strokeWidth: 3,
stroke: 'black',
lineCap: 'round',
id: 'quadLine',
opacity: 0.3,
points: [0, 0]
});
// add dotted line connectors
lineLayer.add(quadLine);
quad = {};
// keep curves insync with the lines
anchorLayer.on('beforeDraw', function() {
if(quad.start && quad.control && quad.end){
drawCurves();
updateDottedLines();
}
});
var selectionBoxBackground = new Konva.Rect({
x: 0,
y: 0,
height:stage.height(),
width: stage.width(),
fill: 'transparent',
draggable: false,
name: 'selectionBoxBackground'
});
selectionBoxLayer.add(selectionBoxBackground);
var clickCounter = 0;
selectionBoxBackground.on("click", function(){
clickCounter +=1;
var mousePos = {};
switch(clickCounter){
case 1:
mousePos = stage.getPointerPosition();
quad.start = buildAnchor(mousePos.x, mousePos.y);
break;
case 2:
mousePos = stage.getPointerPosition();
quad.control = buildAnchor(mousePos.x, mousePos.y);
break;
case 3:
mousePos = stage.getPointerPosition();
quad.end = buildAnchor(mousePos.x, mousePos.y);
drawCurves();
updateDottedLines();
break;
default:
clickCounter = 0;
quad = {};
anchorLayer.destroyChildren();
anchorLayer.draw();
}
});
stage.add(curveLayer);
stage.add(lineLayer);
stage.add(selectionBoxLayer);
stage.add(anchorLayer);
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
<script src="https://cdn.rawgit.com/konvajs/konva/0.11.1/konva.min.js"></script>
<body>
<div id="container"></div>
</body>
P.S Please note when I write in the browser console curveLayer.children, it will bring all created curved arrows.
Hint: I think on the creation of new Shape() the values of all created shapes will be changed to the new one.
I don't know what I am missing.
I've got the following code:
var text = new Kinetic.Text({
text: carNames,
fontFamily: 'Calibri',
fontSize: 17,
fill: 'black',
align: 'center'
});
text.toImage({
width: text.getWidth(),
height: text.getHeight(),
callback: function (img) {
var cachedText = new Kinetic.Image({
image: img,
x: 180,
y: 0
});
wedge.add(cachedText);
layer.draw();
}
});
Which produces wedges and text that look like this:
But I need the text to be centered inside the wedge, like this:
Does anyone know of a way to achieve this? I've tried several things but I just can't get the text to align as in the second image.
Thanks in advance for your trouble.
One way to do it is using the text's offset in combination with the text's rotationDeg:
Demo: http://jsfiddle.net/m1erickson/mqsY3/
Here's code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.2.min.js"></script>
<style>
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:350px;
height:350px;
}
</style>
<script>
$(function(){
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
var cx=175;
var cy=175;
var wedgeRadius=120;
var accumAngle=0;
var center = new Kinetic.Circle({
x:cx,
y:cy,
radius:5,
fill: 'red'
});
layer.add(center);
for(var i=0;i<12;i++){
newTextWedge(30,"Element # "+i);
}
function newTextWedge(angle,text){
var wedge = new Kinetic.Wedge({
x: cx,
y: cy,
radius: wedgeRadius,
angleDeg: angle,
stroke: 'gray',
strokeWidth: 1,
rotationDeg:-accumAngle+angle/2
});
layer.add(wedge);
if(accumAngle>90 && accumAngle<270){
var offset=[wedgeRadius-10,7];
var textAngle=accumAngle-180;
}else{
var offset=[-50,7];
var textAngle=accumAngle;
}
var text = new Kinetic.Text({
x:cx,
y:cy,
text:text,
fill: 'red',
offset:offset,
rotationDeg:textAngle
});
layer.add(text);
layer.draw();
accumAngle+=angle;
}
}); // end $(function(){});
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>
I feel I am being incredible stupid but for some reason am blind to being able to fix this issue.
I want 3 individual layers, each that can have multiple object/shapes on them and then on click I want the visible layer to be removed or hid and the next layer to appear.
I think my issue is dying in the logic and calling the function. Here is the function and the jsfiddle:
var version = 0;
function layerVersion() {
if (version === 1) {
stage.add(layerBlue);
layerBlue.on('click', function() {
layerOrange.hide;
version = 2;
});
} else if (version === 2) {
stage.add(layerOrange);
} else {
stage.add(layerPink);
layerpink.on('click', function() {
layerPink.hide;
version = 1;
});
}
}
Here is the jsFiddle link: http://jsfiddle.net/TJ96r/2/
Any help would be much appreciate I feel so dumb for not being able to figure it out.
Check this out.
http://jsfiddle.net/TJ96r/3/
var stage = new Kinetic.Stage({
container: 'container',
width: 300,
height: 300
});
var layerPink = new Kinetic.Layer();
layerPink.hide();
var layerBlue = new Kinetic.Layer();
var layerOrange = new Kinetic.Layer();
layerOrange.hide();
// pink box
var pink = new Kinetic.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'pink',
stroke: 'black',
strokeWidth: 2
});
// blue box
var blue = new Kinetic.Rect({
x: 100,
y: 100,
width: 100,
height: 100,
fill: 'blue',
stroke: 'black',
strokeWidth: 2
});
// orange box
var orange = new Kinetic.Rect({
x: 150,
y: 150,
width: 100,
height: 100,
fill: 'orange',
stroke: 'black',
strokeWidth: 2
});
layerPink.add(pink);
layerBlue.add(blue);
layerOrange.add(orange);
var version = 0;
stage.add(layerBlue);
stage.add(layerOrange);
stage.add(layerPink);
layerBlue.on('click', function() {
layerBlue.hide();
layerOrange.show();
layerPink.hide();
stage.draw();
});
layerOrange.on('click', function() {
layerBlue.hide();
layerOrange.hide();
layerPink.show();
stage.draw();
});
layerPink.on('click', function() {
layerPink.hide();
layerOrange.hide();
layerBlue.show();
stage.draw();
});
The code below creates a scalable and draggable triangle with anchor points at its vertices. i want that the anchor points should only visible when mouse goes over them??
and also, how collision detection can be implemented to avoid drawing of other spaces inside the triangle?
<html>
<head>
<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,
radius: 6,
stroke: "#666",
fill: "#ddd",
strokeWidth: 2,
draggable: true,
name : name
});
// add hover styling
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();
});
layer.add(anchor);
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 };
layer.add(triangle);
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();
}
}
window.onload = function() {
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
stage.add(layer);
}
</script>
<style>
#container {
border: 1px solid black;
}
</style>
</head>
<body onmousedown="return false;">
<div id="container"></div>
</body>
</html>
jsFiddle: http://jsfiddle.net/Y9AtR/
I like #Tomalak's solution, here is mine:
http://jsfiddle.net/Y9AtR/2/
triangle.on('mouseover', function(){
triangle.a.show();
triangle.b.show();
triangle.c.show();
layer.draw();
});
triangle.on('mouseout', function(){
//if( not near triangle ) // add some logic so that they don't disappear right away, maybe use distance formula?
triangle.a.hide();
triangle.b.hide();
triangle.c.hide();
layer.draw();
})