Kineticjs Dragend - How to Store results of getPosition() - javascript

I am new to Kineticjs and not a great javascript coder so I am hoping to get some help with this example.
http://jsfiddle.net/pwM8M/
I am trying to store the x axis on doors so when a redraw unrelated to the doors is done they don't go back to the default position. (multiple types of doors and windows too)
Each form element can have multiple quantities (more than one door) so I need a way to store/retrieve the data currently contained in the alert on jsfiddle.
I did some research but have come up empty. Can anyone help with what I have provided?
function OH(x,y,w,h) {
var oh = new Kinetic.Text({
x:x,
y: y,
width: w*scale,
height: h*scale,
stroke: wtc,
strokeWidth: 1,
fill: 'brown',
fontSize: 6,
fontFamily: 'Calibri',
padding:4,
text: w+' x '+h+' OH\n\n<- DRAG ->',
align: 'center',
textFill: 'white',
draggable: true,
dragConstraint:'horizontal',
dragBounds: {
left:xoffset, right: xoffset+width+length-(w*scale)
}
});
oh.on('dragend', function(e) {
alert(oh.getPosition().x);
});
window.south.add(oh);
}
Thanks,

I have fixed sized 40x40 rectangle here in which i am using dragging function. try this
var box = new Kinetic.Rect({
x: parseFloat(area.size.x),
y: parseFloat(area.size.y),
width: 40, //parseFloat(area.size.width)
height: 40,
fill: area.color,
stroke: 'black',
strokeWidth: 1,
opacity: 0.6,
id : area.id + id,
draggable: true,
dragBoundFunc: function(pos) {
// x
var newX = pos.x < 0 ? 40 : pos.x;
var newX = newX > _self.canvasWidth - area.size.width ? _self.canvasWidth - area.size.width : newX;
// y
var newY = pos.y < 0 ? 40 : pos.y;
var newY = newY > _self.canvasHeight - area.size.height ? _self.canvasHeight - area.size.height : newY;
return {
x: newX,
y: newY
};
Use this function
box.on('dragend', function() {
_self.draw = false;
_self.dragArea(area, box);
});
and try this to play with x y coordinates
dragArea: function(area, box){
if(box){
$$('#' + this.formId + ' [name="areas[' + area.id + '][size][x]"]').first().value = parseInt(box.attrs.x);
$$('#' + this.formId + ' [name="areas[' + area.id + '][size][y]"]').first().value = parseInt(box.attrs.y);
area.size.x = parseInt(box.attrs.x);
area.size.y = parseInt(box.attrs.y);
}
},

Create a new array before the function, like so:
var xArray = new Array();
then inside your function
oh.on('dragend', function(e) {
alert(oh.getPosition().x);
// ADD NEW ITEM TO ARRAY, STORE X POSITION
xArray.push(oh.getPosition().x);
});
and so all the x values get stored in the array.
If you need to clear the array, you can simply create a new one again with the same name.
And you can iterate through it with a loop if needed.
updated:
http://jsfiddle.net/pwM8M/2/

Related

Kineticjs Group Image dragging

I am new to Kinetic js and having issue figuring out how to stop the image from moving outside the boundary which then creates a white space
Here is my code so far
//photo layer
var photoGroup = new Kinetic.Group({ x: layout.photoGroup.x, y: layout.photoGroup.y, draggable: true });
var Photolayer = new Kinetic.Layer();
var cropArea = new Kinetic.Rect({
x: layout.cropAreaRect.x,
y: layout.cropAreaRect.y,
width: layout.cropAreaRect.width,
height: layout.cropAreaRect.height,
fill: "white",
listening: false
});
Photolayer.add(cropArea);
Photolayer.add(photoGroup);
stage.add(Photolayer);
Here is a working code snippet illustrating a dragBoundFunc().
The gold rect is the area we intend to restrict the drag within. The blue rect shows the area we compute - different from the gold because we use the topleft of the draggable as the basis for our maths and we have to account for its own width and height. The red block could be an image or any Konvajs element.
Drag the red block!
// add a stage
var s = new Konva.Stage({
container: 'container',
width: 800,
height: 600
});
// add a layer
var l = new Konva.Layer();
s.add(l);
// Add a green rect to the LAYER just to show boundary of the stage.
var green = new Konva.Rect({stroke: 'lime', width: 800, height: 600, x: 0, y: 0});
l.add(green);
// Add a gold rect to the LAYER just to give some visual feedback on intended drag limits
var gold = new Konva.Rect({stroke: 'gold', width: 400, height: 200, x: 55, y: 55});
l.add(gold);
// Add a red rect to act as our draggable
var red = new Konva.Rect({fill: 'red', stroke: 'red', width: 40, height: 50, x: 65, y: 65, draggable: true,
dragBoundFunc: function(pos) {
var newX = pos.x;
if (newX < minX){ newX = minX};
if (newX > maxX){ newX = maxX};
var newY = pos.y;
if (newY < minY){ newY = minY};
if (newY > maxY){ newY = maxY};
$("#info").html('Info: Pos=(' + newX + ', ' + newY + ') range X=' + minX + ' - ' + maxX + ' Y=' + minY + ' - ' + maxY);
return {
x: newX,
y: newY
}
}
});
l.add(red);
// calculate the drag boundary once for performance - we need to have the draggable element size for this !
var goldPos = gold.getAbsolutePosition()
var minX = goldPos.x;
var maxX = goldPos.x + gold.width() - red.width();
var minY = goldPos.y;
var maxY = goldPos.y + gold.height() - red.height();
// Add a blue rect to the LAYER just show the computed drag limits - note the subtraction of the draggable element dimensions from maxX & Y. Note the 1px adjustment is to avoid overlapping the gold rect in the demo and is not required for live.
var blue = new Konva.Rect({stroke: 'blue', opacity: 0.2, width: 399 - red.width(), height: 199 - red.height(), x: 56, y: 56});
l.add(blue);
// bring red back to top so we can drag it
red.moveToTop();
l.draw(); // redraw the layer it all sits on.
#container {
border: 1px solid #ccc;
}
#info {
height: 20px;
border-bottom: 1px solid #ccc;
}
#hint {
font-style: italic;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.3/konva.min.js"></script>
<body>
<div id='hint'>Hint: green is stage, gold is intended drag limit, blue is computed drag limit.<br/>Drag the red block!</div>
<div id='info'>Info:</div>
<div id="container"></div>
</body>
As an example of dragBoundFunc:
const dragRect = new Konva.Rect({
x: window.innerWidth / 2 - 50,
y: window.innerHeight / 2 - 50,
width: 100,
height: 100,
fill: 'green',
draggable: true,
dragBoundFunc: (pos) => {
const leftBound = cropRect.x();
const rightBound = cropRect.x() + cropRect.width() - dragRect.width();
pos.x = Math.max(leftBound, Math.min(rightBound, pos.x));
const topBound = cropRect.y();
const bottomBound = cropRect.y() + cropRect.height() - dragRect.height();
pos.y = Math.max(topBound, Math.min(bottomBound, pos.y));
return pos;
}
});
Demo: http://jsbin.com/jilagadeqa/1/edit?js,output

Position text and circle in Highcharts graph dynamically?

I want to place a few pieces of text in my graphic. Now, I can do this manually, by specifying the xy coordinates. But this is probably not the best way, as the size of the graph can change, and then the positions are hard coded.
Is there a way to derive in a dynamical way the positions of a line? That is: What is the x/y coordinate of the position x=1980? Here is a fiddle for it.
options.series.push(series);
options.chart.events = {
load: function() {
var chart = this;
chart.renderer.text('1970: Arbitrary chosen reference year: All cumulative values are relative to 1970.', xOffset, yOffset)
.css({
fontSize: '13px',
color: '#aaa',
width: '550px'
})
.add();
chart.renderer.text('1980: observed loss in glacier thickness cumulates to 2 m w.e. since 1970.', 520, 220)
.css({
fontSize: '13px',
color: '#7994d9',
width: '520px'
})
.add();
chart.renderer.text('1990: cumulative ice loss of 4 m w.e.', 690, 255)
.css({
fontSize: '13px',
color: '#ff6c7f',
width: '320px'
})
.add();
}
Maybe this can help you:
var points = chart.series[1].points;
for(var i = 0; i < points.length; i++) {
var point = points[i];
if(point.x === 1980) {
console.log(point.plotX); //263.51049191606
console.log(point.plotY); //40.81559999999999
}
}
UPDATE 1
Adding translated locations to an array:
var locations = [];
var points = chart.series[1].points;
for(var i = 0; i < points.length; i++) {
var point = points[i];
if(point.x === 1980 || point.x === 1990) {
locations.push({
x: point.plotX + chart.yAxis[0].left,
y: point.plotY + chart.yAxis[0].top
});
}
}
Updated fiddle

How to add additional labels to base of columns in Highcharts?

I need two labels for a column, one above to show the score and one below to show if it is a "test" or a "retest". How do I go about doing it?
I assume complicated calculation is needed, something like this? http://jsfiddle.net/72xym/4/ (but I can't really understand)
I got other diagrams need to achieve this as well. Basically I need to do this:
My code is inside here: http://jsfiddle.net/kscwx139/6/
I did this for the part to add the label:
..., function() {
var i = 0, j = 0, len_i, len_j, self = this, labelBBox, labels=[];
for(i=0; i<this.series.length; i++) {
labels[i] = [];
for(j=0; j<this.series[i].data.length; j++) {
labels[i][j] = this.renderer.label(self.series[i].name)
.css({
width: '100px',
color: '#000000',
fontSize: '12px'
}).attr({
zIndex: 100
}).add();
labelBBox = labels[i][j].getBBox();
labels[i][j].attr({
x: 100, //1) what to put here?
y: 100 //2) what to put here?
});
}
}
}
I would do something like this:
var chart2 = new Highcharts.Chart(options2, function() {
var i = 0, j = 0, len_i, len_j, self = this, labelBBox, labels=[], point;
for(i=0; i<this.series.length; i++) {
labels[i] = [];
for(j=0; j<this.series[i].data.length; j++) {
labels[i][j] = this.renderer.label(self.series[i].name)
.css({
width: '100px',
color: '#000000',
fontSize: '12px'
}).attr({
zIndex: 100,
align: "center"
}).add();
point = this.series[i].data[j];
labelBBox = labels[i][j].getBBox();
labels[i][j].attr({
x: point.plotX + this.plotLeft + point.series.pointXOffset + point.shapeArgs.width / 2,
y: this.plotTop + this.plotHeight - labelBBox.height
});
}
}
});
Demo: http://jsfiddle.net/kscwx139/7/
Note: I would suggest using chart.redraw event to update labels position (label[i][j].animate({ x: ..., y: ... });) when resizing browser, updating data, etc.
Short explanation:
point.plotX - x-position in the plotting area (center of the point's shape)
this.plotLeft - left offset for left yAxis (labels, title)
point.series.pointXOffset - offset for multiple columns in the same category
this.plotTop - the same as plotLeft but from top ;)
this.plotHeight - height of the plotting area
labelBBox.height - height of the label to place it above the xAxis line

Tweened Kinetic.js shape doesn't fire most of clicks

Purpose
I'm making a simple "shoot the word" game, where user needs to click on some moving rectangles with words to "shoot" them.
Problem
So i create some objects and move them using simple kinetic.js tweening.
Word creation
function createWord(value){
//here comes some word object construction
var wordGroup = new Kinetic.Group({
x: 0,
y: 0
});
var padding = 10;
wordGroup.label = new Kinetic.Text({
x: padding,
y: padding,
text: value,
fontFamily: 'Times New Roman',
fontSize: 30,
fill: 'white'
});
wordGroup.tag = new Kinetic.Rect({
x: 0,
y: 0,
width: wordGroup.label.width() + (padding << 1),
height: wordGroup.label.height() + (padding << 1),
fill: 'black',
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: {x:10,y:20},
shadowOpacity: 0.5,
cornerRadius: 10
});
wordGroup.add(wordGroup.tag);
wordGroup.add(wordGroup.label);
wordGroup.shoot = function(){ //shooting mechanism (simple stop from moving and remove from scene)
wordGroup.tween.pause();
wordGroup.clean();
dropNextWord(); //drops fresh blood! (new word instead of shooted)
}
wordGroup.clean = function(){ //remove from scene and set it free to drop again
wordGroup.remove();
wordGroup.isActive = false;
}
wordGroup.move = function(callback){ //animates word
wordLayer.add(wordGroup);
moveToSide(wordGroup, callback); //calls moving function
}
wordGroup.on('click', function(e){
wordGroup.shoot();
});
return wordGroup;
}
Tweening part
//move word to opposite side
function moveToSide(word, callback){
var side = Math.random();
var d = 100;
spawnFromSide(word, side); //set random side word position
tweenPosition = {
x: word.x(),
y: word.y()
}
if(side < 0.25){ //left
tweenPosition.x = - d;
} else if(side > 0.25 && side < 0.5){ //right
tweenPosition.x = defaultStageWidth + d;
} else if(side > 0.5 && side < 0.75){ //up
tweenPosition.y = - d;
} else { //down
tweenPosition.y = defaultStageHeight + d;
}
word.tween = new Kinetic.Tween({
node: word,
duration: 4,
easing: Kinetic.Easings.Linear,
x: tweenPosition.x,
y: tweenPosition.y,
onFinish: function(){
word.clean();
callback();
}
});
word.tween.play();
}
But the problem is that click event doesn't fire on large amount of user clicks. As i think, this caused by delayed drawHit() calls inside tweening mechanism, that draws new object position before updating the hit area, so when we shoot object thinking that we hit its current position we miss because its hit area still have the same old position.
Live example
http://jsfiddle.net/hd6z21de/7/
Take a minute on shooting to see this effect in action
Solved this weird behavior by listening canvas touches and check if pointer collide some target word-rect by myself instead of using their own onclick events.
//i listen to canvas because of my app specific, you could simple listen your own layer or even document
$("canvas").bind('click', function(event){
var x = (event.pageX) / stage.scaleX(); //you don't need to divide by scale if your stage isn't scaled as mine does
var y = (event.pageY) / stage.scaleY();
var wordArray = wordGroup.getChildren();
for(var i = 0; i < wordArray.length; i++){ //go through all words and check if we shoot someone (is mouse position included in some word rect)
if(x > wordArray[i].x() &&
y > wordArray[i].y() &&
x < (wordArray[i].x() + wordArray[i].width()) &&
y < (wordArray[i].y() + wordArray[i].height())){
wordArray[i].shoot(); //shoot that word
break;
}
}
}

Can't get counter variable in event handler

k=0;
var sf = new Array();
function draw_sf (type,sf_text,schriftgroesse,sf_width,sf_height,x0,y0,ports_top,ports_right,ports_bottom,ports_left) {
sf[k] = new Kinetic.Group({
draggable: true
});
sf[k]['x0']=x0;
sf[k]['y0']=y0;
m=0;
sf[k][m] = new Kinetic.Rect({
x: x0,
y: y0,
width: sf_width,
height: sf_height,
fill: '#EEE',
stroke: '#000',
strokeWidth: randbreite_sf
});
sf[k].add(sf[k][m]);
m++;
sf[k].on('dragend', function() {
var dx=parseFloat(document.getElementsByName("dx")[0].value);
var dy=parseFloat(document.getElementsByName("dy")[0].value);
sf[k]['x']=sf[k]['x0']+dx;
sf[k]['y']=sf[k]['y0']+dy;
});
boxLayer.add(sf[k]);
k++;
}
My function draw_sf() draws rectangles, which can be moved my drag&drop. After being droped (event handler 'dragend') i want to save the new position of the element in an array. But my counter variable 'k' doesnt count in the dragend-function. k is always the number of times i called the draw_sf().
So how can i save the actual positions of my rectangles?
Consider changing the approach a bit into something like:
var sf = new Array();
function draw_sf(type, sf_text, schriftgroesse, sf_width, sf_height, x0, y0, ports_top, ports_right, ports_bottom, ports_left) {
var kineticGroup = new Kinetic.Group({
draggable:true
});
kineticGroup['x0'] = x0;
kineticGroup['y0'] = y0;
m = 0;
kineticGroup[m] = new Kinetic.Rect({
x:x0,
y:y0,
width:sf_width,
height:sf_height,
fill:'#EEE',
stroke:'#000',
strokeWidth:randbreite_sf
});
kineticGroup.add(kineticGroup[m]);
m++;
kineticGroup.on('dragend', function () {
var dx = parseFloat(document.getElementsByName("dx")[0].value);
var dy = parseFloat(document.getElementsByName("dy")[0].value);
kineticGroup['x'] = kineticGroup['x0'] + dx;
kineticGroup['y'] = kineticGroup['y0'] + dy;
});
boxLayer.add(kineticGroup);
sf.push(kineticGroup);
}
The truth is that you actually don't need any counter to store the history of the rect positions - you can just push it to the array using Array.prototype.push: http://www.w3schools.com/jsref/jsref_push.asp

Categories