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
Related
I want to add moving arrows or overlay animation in the Flights Animation example in OpenLayers 6.
I tried doing the overlay moving animation with JavaScript setInterval(), but so far I have only succeeded in animating a single LineString, that too after the line is finished drawing. I wanted to add the moving animation as the line is being drawn, kind of like tracing the LineString's path.
Can someone please help me with this?
Following is the code snippet where I have tried to add the moving animation:
var markerEl = document.getElementById('geo-marker');
var marker = new Overlay({
positioning: 'center-center',
offset: [0, 0],
element: markerEl,
stopEvent: false
});
map.addOverlay(marker);
function animateFlights(event) {
var coords;
var vectorContext = getVectorContext(event);
var frameState = event.frameState;
var features = flightSource.getFeatures();
for (var i = 0; i < features.length; i++) {
var feature = features[i];
if (!feature.get('finished')) {
coords = feature.getGeometry().getCoordinates();
var elapsedTime = frameState.time - feature.get('start');
var elapsedPoints = elapsedTime * pointsPerMs;
if (elapsedPoints >= coords.length) {
feature.set('finished', true);
}
var maxIndex = Math.min(elapsedPoints, coords.length);
var currentLine = new LineString(coords.slice(0, maxIndex));
vectorContext.setStyle(strokeStyle1);
vectorContext.drawGeometry(currentLine);
if (feature.get('finished')) {
var interval = setInterval(
function () { return animatePath(coords, interval) }, 10);
}
}
}
map.render();
}
function animatePath(path, clearInterval) {
if (i == path.length) {
stopAnimatePath(clearInterval);
}
marker.setPosition(path[i]);
i = i + 1;
}
function stopAnimatePath(clearInterval) {
clearInterval(clearInterval);
}
Here is a link to a snapshot of how my app looks right now
Trace your LineString
It should be enough to set your map center to the last point of your LineString if you update often enough
map.getView().setCenter(lastPoint)
If it gets laggy use
var pan = ol.animation.pan({
source: map.getView().getCenter()
});
map.beforeRender(pan);
map.getView().setCenter(lastPoint);
Draw arrows
To draw arrows on your LineString you can use the following style
var styleFunction = function (feature) {
var geometry = feature.getGeometry();
var styles = [
// linestring
new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#000',
width: 2
})
})
];
geometry.forEachSegment(function (start, end) {
var dx = end[0] - start[0];
var dy = end[1] - start[1];
var rotation = Math.atan2(dy, dx);
styles.push(new ol.style.Style({
geometry: new ol.geom.Point(end),
image: new ol.style.RegularShape({
fill: new ol.style.Fill({color: '#000'}),
points: 3,
radius: 8,
rotation: -rotation,
angle: Math.PI / 2 // rotate 90°
})
}));
});
return styles;
};
more details: https://stackoverflow.com/a/58237497/546526
I have five rectangles placed at different points along a circle like this - http://imgur.com/uVYkwl7.
Upon clicking any rectangle i want the circle to move to the left of the screen, gradually scaling down it's radius until the circle's center reaches x=0. I'd like the five rectangles to move along with the circle while its being scaled down and also adjust their own positions and scale on the circle so that they are within the view's bounds, like this - http://imgur.com/acDG0Aw
I'd appreciate any help on how to go about doing this. Heres my code for getting to the 1st image and animating the circle:
var radius = 300;
var center = view.center;
var circle = new Path.Circle({
center: view.center,
radius: radius,
strokeColor: 'black',
name: 'circle'
});
var path = new Path.Rectangle({
size: [230, 100],
fillColor: '#1565C0'
});
var rectText = ['Text 1',
'Text 2',
'Text 3',
'Text 4',
'Text 5'
];
var symbol = new Symbol(path);
var corners = [
new Point(center.x, center.y - radius),
new Point(center.x - radius, center.y - radius / 2),
new Point(center.x + radius, center.y - radius / 2),
new Point(center.x - radius, center.y + radius / 2),
new Point(center.x + radius, center.y + radius / 2)
];
var rectClicked = false;
var clickedRect = null;
var rectClick = function(event) {
rectClicked = true;
clickedRect = this;
};
function onFrame(event) {
// Your animation code goes in here
if (rectClicked) {
for (var i = 0; i < 1; i++) {
var item = project.activeLayer.children[i];
if (item.name == 'circle') {
if (item.position.x < 0) {
rectClicked = false;
} else {
item.position.x -= 10;
item.scale(1/1.01);
}
}
}
}
}
// Place the instances of the symbol:
for (var i = 0; i < corners.length; i++) {
var placedSymbol = symbol.place(corners[i]);
placedSymbol.onMouseDown = rectClick;
var rText = new PointText({
point: placedSymbol.bounds.topLeft + 20,
content: rectText[i],
fontSize: '20',
fillColor: 'white'
});
}
Paper.js provides rotations around a pivot out of the box.
var pivotPoint = new Point(10, 5);
circle.rotate(30,pivotPoint);
Here is the docs reference for this behaviour and here is a very basic Sketch example to illustrate this
The above snippet will rotate a circle(you can change this to rectangle in your case) by 30 degrees around a pivot point at coordinates 10,5 on the x/y axis.
Thus what you describe is certainly doable as long as the path that your elements will follow is always circular.
Bear in mind that in order for the pivot rotation to work the way you want them to you need to update the pivotPoint and reinitiate the rotation again.
Note: In case you want to move along an arbitrary shape instead of circular path, you should search for Paper.js animation-along-a-path which is something that I've seen been done before without much difficulty - e.g this simple Sketch by the creator of Paper.js himself.
The sketch I provided above is a basic example of rotation around a pivot point.
I'm dumping the Sketch code here in case the link goes dead:
//Create a center point
var centerCircle = new Path.Circle(paper.view.center, 100);
centerCircle.strokeColor = 'black';
centerCircle.dashArray = [10, 12];
//Create the circles
var circle1Radius = 30;
var circle1 = new Path.Circle((centerCircle.position-centerCircle.bounds.width/2)+circle1Radius, circle1Radius);
circle1.fillColor = '#2196F3';
var circle2Radius = 40;
var circle2 = new Path.Circle((centerCircle.position-centerCircle.bounds.width/2)+circle2Radius, circle2Radius);
circle2.fillColor = '#E91E63';
var circle3Radius = 40;
var circle3 = new Path.Circle((centerCircle.position-centerCircle.bounds.width/2)+circle2Radius, circle2Radius);
circle3.fillColor = '#009688';
var i=0;
var animationGap = 125; //how long to move before animating the next circle
var rotationSpeed = 2;
function onFrame(event) {
circle1.rotate(rotationSpeed,centerCircle.position);
if(i>animationGap)
circle2.rotate(rotationSpeed,centerCircle.position);
if(i>animationGap*2)
circle3.rotate(rotationSpeed,centerCircle.position);
i++;
}
I want to scale my group (image and something).
group.setScale(zoom, zoom);
http://jsfiddle.net/K8nK3/
But when i scale my group, I think it's not scale from center of my group. i think it like this
I want it scale from center like
I try more but it's not success. How can i do that, thanks.
[Edited to better fit questioner's needs]
Here’s how to scale a Kinetic group from the centerpoint
First we store the centerX and centerY of the original group so we can keep the group centered there:
group.cx=group.getX()+group.getWidth()/2;
group.cy=group.getY()+group.getHeight()/2;
Then we write a custom scaling method that both scales the group and re-centers it:
group.scale=function(x,y){
group.setScale(x,y);
group.setPosition(
group.cx-group.getWidth()/2*group.getScale().x,
group.cy-group.getHeight()/2*group.getScale().y);
group.draw();
}
Here’s code and a Fiddle: http://jsfiddle.net/m1erickson/dVGGz/
<!DOCTYPE HTML>
<html>
<head></head>
<body>
<button id="larger">Larger</button>
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.4.0.min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script>
$(function(){
var stage = new Kinetic.Stage({
container: 'container',
width: 400,
height: 400
});
var layer = new Kinetic.Layer();
// Be sure to set width and height
// They are required for this method to work
var group = new Kinetic.Group({
x: 100,
y: 100,
width:100,
height:100
});
// store the original group center
// so we can center the group there
group.cx=group.getX()+group.getWidth()/2;
group.cy=group.getY()+group.getHeight()/2;
// custom scale function to both
// scale the group and center the results
group.scale=function(x,y){
group.setScale(x,y);
group.setPosition(
group.cx-group.getWidth()/2*group.getScale().x,
group.cy-group.getHeight()/2*group.getScale().y);
group.draw();
}
var box1 = new Kinetic.Rect({
x: 0,
y: 0,
width: 50,
height: 50,
fill: "blue",
stroke: 'black',
strokeWidth: 4
});
group.add(box1);
var box2 = new Kinetic.Rect({
x: 50,
y: 50,
width: 50,
height: 50,
fill: "green",
stroke: 'black',
strokeWidth: 4
});
group.add(box2);
layer.add(group);
stage.add(layer);
var scaleFactor=1;
$("#larger").click(function(){
scaleFactor+=0.10;
group.scale(scaleFactor,scaleFactor);
console.log(scaleFactor);
});
});
</script>
</body>
</html>
Have you tried to use offset property? i.e. offset: [width/2, height/2]
Problem #1: I need to scale a group from its center.
Problem #2: Kinetic JS group.getWidth() and group.getHeight() don't work unless you explicitly set the width at creation.
Problem #3: I don't know the width and height, I'm dynamically changing the shapes.
This means in order to try your code out, I'll have to write my own getWidth and getHeight functions. Then I can use the method you propose.
Solution below.
(There may be a better way, but this is my level of programming right now . . . if anyone can add recursion to this to look through groups that contain groups that contain groups etc, that would be great.)
var results = getGroupMinsAndMaxes(myKineticGroup);
console.log('results: '+ results.groupMinX, results.groupMaxX,
results.groupMinY, results.groupMaxY);
function getGroupMinsAndMaxes( group ) {
// note this does not handle children that are groups,
// it expects children to be shapes
var item;
var groupMinX = 99999;
var groupMaxX = -99999;
var groupMinY = 99999;
var groupMaxY = -99999;
var shapeFunctionDefinition = '';
var bounds = {};
for (var i = 0; i < group.children.length; i++ ) {
item = group.children[ i ];
if ( item.getType() !== 'Shape' ) {
alert('The getGroupMinsAndMaxes function is not designed to handle groups that contain groups.');
break;
} else {
shapeFunctionDefinition = item.attrs.drawFunc.toString();
bounds = getShapeMinsAndMaxes( shapeFunctionDefinition );
if ( bounds.shapeMinX < groupMinX ) { groupMinX = bounds.shapeMinX; }
if ( bounds.shapeMaxX > groupMaxX ) { groupMaxX = bounds.shapeMaxX; }
if ( bounds.shapeMinY < groupMinY ) { groupMinY = bounds.shapeMinY; }
if ( bounds.shapeMaxY > groupMaxY ) { groupMaxY = bounds.shapeMaxY; }
}
}
return {
groupMinX: groupMinX,
groupMaxX: groupMaxX,
groupMinY: groupMinY,
groupMaxY: groupMaxY
};
}
function getShapeMinsAndMaxes( shape ) {
// the shape definition is passed as a string
var regex = /[+-]?\d+\.\d+/g;
var floats = shape.match(regex).map(function (v) {
return parseFloat(v);
});
// preset some unrealistically large numbers which
// we will never encounter
var shapeMinX = 99999;
var shapeMaxX = -99999;
var shapeMinY = 99999;
var shapeMaxY = -99999;
for (var i = 0; i < floats.length; i++) {
if (isOdd(i)) {
// Check Y values
if (floats[i] < shapeMinY) {
shapeMinY = floats[i];
}
if (floats[i] > shapeMaxY) {
shapeMaxY = floats[i];
}
} else {
// Check X values
if (floats[i] < shapeMinX) {
shapeMinX = floats[i];
}
if (floats[i] > shapeMaxX) {
shapeMaxX = floats[i];
}
}
}
return {
shapeMinX: shapeMinX,
shapeMaxX: shapeMaxX,
shapeMinY: shapeMinY,
shapeMaxY: shapeMaxY
};
}
function isOdd(num) {
return num % 2;
}
the easiest way would be to reposition / center the offset. all scaling and moving will be based on your new offset position.
group.offset( { x: group.width() * .5, y: group.height() * .5 } );
http://kineticjs.com/docs/Kinetic.Group.html
I have managed to dynamically create an array of shapes, and they are nicely placed at different coordinates.
However, when I try to assign an event within that loop, the result of click is always the same. As if the click event is still referencing the last iteration of my loop.
What am I doing wrong? Thanks!
EDIT: Actually, re-produced this behaviour in an isolated environment:
var stage = new Kinetic.Stage({
container: 'container',
width: 1024,
height: 768
});
var layer = new Kinetic.Layer();
singleSegment=40;
for (var i = 0; i < 4; i++) {
depth=singleSegment+(singleSegment*i);
dotLabel = new Kinetic.Text({
x: depth,
y: depth,
text: "test"
});
dotLabel.on('click', function(evt){
console.log(this.x);
});
layer.add(dotLabel);
}
stage.add(layer);
How do I add different events to these four labels?
You are doing everything correct, I think. but because of this;
console.log(i);
The last value of i is array.length-1, and when it is clicked, it shows that value, which does not change because it's outside of loop when it is clicked.
This will show different value.
console.log(this.attrs.x);
I just had to deal with this same issue. I solved it by storing to each shape its location.
for (var axisItem=0;axisItem<innerCircleXAxisArray.length;axisItem++)
{
var arc = new Kinetic.Shape({
drawFunc: function(canvas){
var allAttrs = this.getAttrs();
var start = allAttrs['start'];
var end = allAttrs['end'];
var context = canvas.getContext();
context.strokeStyle = 'red';
var centerOfCanvasX = canvasWidth / 2;
var centerOfCanvasY = canvasHeight / 2;
context.translate(centerOfCanvasX, centerOfCanvasY);
context.lineWidth = 15;
context.beginPath();
context.arc(0, 0, 284, start , end, true);
canvas.stroke(this); // Fill the path
context.closePath();
context.translate(-centerOfCanvasX, -centerOfCanvasY);
},
fill: 'red',
stroke: 'red',
strokeWidth: 15
});
arc.setAttrs({'start': innerCircleXAxisArray[axisItem]['start'], 'end': innerCircleXAxisArray[axisItem]['end']});
layer.add(arc);
}
stage.add(layer);
When the object is created, I use setAttrs to store the object's location - a start and end angle since these are arcs, but it could just as easily be an x and y point. Then in the drawFunc I use getAttrs to retrieve that data and to draw the object.
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/