Cannot read property 'rotation' of undefined kineticjs - javascript

I've been staring at this for the past hour and cannot figure it out.
I'm trying to use KineticJS to rotate a shape 45 degrees when it is clicked. I found http://jsfiddle.net/JUu2Q/6/ from a previous question on Stack Overflow which does basically what I want. When I apply this to my code (and change 'layer' to 'stage'), I get the following error: Cannot read property 'rotation' of undefined:
layer.on('click tap', function(evt) {
evt.targetNode.tween = new Kinetic.Tween({
node: evt.targetNode,
duration: 0.3,
rotationDeg: evt.targetNode.rotation()+45,
easing: Kinetic.Easings.EaseOut
});
evt.targetNode.tween.play();
});
I'm sure I'm doing something wrong but I just can't figure it out.
My code can be found at http://jsfiddle.net/0h55fdzL/
I've only be using KineticJS for a few hours so I apologize if this is stupid question
Thanks for your help!

1 Create closure for x variable.
2 Use target instead of targetNode
var x = -50;
var y = -50;
var stage = new Kinetic.Stage({
container: 'container',
width: 1200,
height: 1200,
});
for (i=0; i<3; i++){
x = x + 50;
y = y + 50;
var layer = [];
layer[i] = new Kinetic.Layer();
var hex = [];
(function(x){
hex[i] = new Kinetic.Shape({
sceneFunc: function(context) {
context.beginPath();
context.moveTo(x+25, 0);
context.lineTo(x+40, 10);
context.lineTo(x+40, 25);
context.lineTo(x+25, 35);
context.lineTo(x+10, 25);
context.lineTo(x+10, 10);
context.closePath();
// KineticJS specific context method
context.fillStrokeShape(this);
},
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true,
rotation:0
});
})(x);
// add the triangle shape to the layer
layer[i].add(hex[i]);
// add the layer to the stage
stage.add(layer[i]);
}
stage.on('click tap', function(evt) {
evt.target.tween = new Kinetic.Tween({
node: evt.target,
duration: 0.3,
rotationDeg: evt.target.rotation()+45,
easing: Kinetic.Easings.EaseOut
});
evt.target.tween.play();
});
http://jsfiddle.net/0h55fdzL/1/

Related

Scaling bug with multi-touch Canvas scale with pinch zoom

Using the sample code from Konvajs.org as a base (https://konvajs.org/docs/sandbox/Multi-touch_Scale_Stage.html), I have added a large SVG to a layer (4096 x 3444) to experiment with zoom / pan of a vector-based map, base64 encoded SVG in this instance. Initial impressions are good however during testing I experience an odd bug where during a pinch the view of the map would snap to a different location on the map not the area that I centred on.
Here is the code (map base64 code removed due to length):
// by default Konva prevent some events when node is dragging
// it improve the performance and work well for 95% of cases
// we need to enable all events on Konva, even when we are dragging a node
// so it triggers touchmove correctly
Konva.hitOnDragEnabled = true;
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
draggable: true,
});
var layer = new Konva.Layer();
var triangle = new Konva.RegularPolygon({
x: 190,
y: stage.height() / 2,
sides: 3,
radius: 80,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
var circle = new Konva.Circle({
x: 380,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
let bg = new Konva.Image({
width: 4096,
height: 3444
});
layer.add(bg);
var image = new Image();
image.onload = function() {
bg.image(image);
layer.draw();
};
image.src = 'data:image/svg+xml;base64,...';
function getDistance(p1, p2) {
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}
function getCenter(p1, p2) {
return {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2,
};
}
var lastCenter = null;
var lastDist = 0;
stage.on('touchmove', function (e) {
e.evt.preventDefault();
var touch1 = e.evt.touches[0];
var touch2 = e.evt.touches[1];
if (touch1 && touch2) {
// if the stage was under Konva's drag&drop
// we need to stop it, and implement our own pan logic with two pointers
if (stage.isDragging()) {
stage.stopDrag();
}
var p1 = {
x: touch1.clientX,
y: touch1.clientY,
};
var p2 = {
x: touch2.clientX,
y: touch2.clientY,
};
if (!lastCenter) {
lastCenter = getCenter(p1, p2);
return;
}
var newCenter = getCenter(p1, p2);
var dist = getDistance(p1, p2);
if (!lastDist) {
lastDist = dist;
}
// local coordinates of center point
var pointTo = {
x: (newCenter.x - stage.x()) / stage.scaleX(),
y: (newCenter.y - stage.y()) / stage.scaleX(),
};
var scale = stage.scaleX() * (dist / lastDist);
stage.scaleX(scale);
stage.scaleY(scale);
// calculate new position of the stage
var dx = newCenter.x - lastCenter.x;
var dy = newCenter.y - lastCenter.y;
var newPos = {
x: newCenter.x - pointTo.x * scale + dx,
y: newCenter.y - pointTo.y * scale + dy,
};
stage.position(newPos);
lastDist = dist;
lastCenter = newCenter;
}
});
stage.on('touchend', function () {
lastDist = 0;
lastCenter = null;
});
layer.add(triangle);
layer.add(circle);
stage.add(layer);
I am unsure if this is due to the large size of the image and / or canvas or an inherent flaw in the example code from Konvas.js. This has been tested, with the same results, on 2 models of iPad Pro, iPhone X & 11, Android Pixel 3, 5 and 6 Pro.
Here is the code on codepen as an example: https://codepen.io/mr-jose/pen/WNXgbdG
Any help would be appreciated, thanks!
I faced the same issue and discovered that it was caused by the dragging functionality of the stage. Everytime if (stage.isDragging()) evaluated to true, the jump happened.
For me what worked was setting draggable to false while pinch zooming and back to true on touch end.
stage.on('touchmove', function (e) {
...
if (touch1 && touch2) {
stage.draggable(false);
....
}
});
stage.on('touchend', function (e) {
...
stage.draggable(true);
});

Creating directional animation using canvas

I am utilizing FabricJs it is a javascript canvas library to make a small application where you can create shapes and animation between different objects.
To Run it You can follow the following steps.
Click new animation
Click Rectangle
Click Add Child button (This allows you to link objects)
Click Circle or Rectangle
If you follow the steps above you will see that you can create 2 shapes and animation between the two displayed by a small circle going back and forth.
I was wondering if it is possible to create similar animation that goes either left to right or right to left only once. I would really appreciate if someone can guide me.
Here is my FIDDLE
Here is the code for the animation
var animateBallBetweenObjects = function (obj1, obj2) {
// Add the "ball"
var circle = new fabric.Circle({
radius: 10,
fill: 'blue',
left: obj1.getCenterPoint().x,
top: obj1.getCenterPoint().y,
originX: 'center',
originY: 'middle',
selectable: false
});
canvas.add(circle);
var period = 1000;
var amplitude = 0;
var angle = 0;
var prevTime = Date.now();
var loop = function () {
// Calculate the new amplitude
var now = Date.now();
var elapsed = now - prevTime;
prevTime = now;
angle += Math.PI * (elapsed / (period * 0.5));
amplitude = 0.5 * (Math.sin(angle - (0.5 * Math.PI)) + 1);
// Set the new position
var obj1Center = obj1.getCenterPoint();
var obj2Center = obj2.getCenterPoint();
circle.setLeft(obj1Center.x + (amplitude * (obj2Center.x - obj1Center.x)));
circle.setTop(obj1Center.y + (amplitude * (obj2Center.y - obj1Center.y)));
canvas.renderAll();
requestAnimationFrame(loop);
}
// Animate as fast as possible
requestAnimationFrame(loop);
};
You can try yo use fabric built in animation:
http://jsfiddle.net/asturur/s6ju2858/2/
I did not replicate your loop function, that requires extra work, but fabricJS gives you the ability to define an animation for a property ( in our case left and top ) with a start and end value.
var animateBallBetweenObjects = function (obj1, obj2) {
var completedLeft, completedTop;
// obj1, obj2 are predefined.
var c1 = obj1.getCenterPoint();
var c2 = obj2.getCenterPoint();
var circle = new fabric.Circle({
radius: 10,
fill: 'blue',
left: c1.x,
top: c1.y,
originX: 'center',
originY: 'center',
selectable: false
});
canvas.add(circle);
circle.animate('left', c2.x, {
onChange: canvas.renderAll.bind(canvas),
startValue: c1.x,
endValue: c2.x,
onComplete: function() {
completedLeft = true;
completedTop && canvas.remove(circle);
},
easing: fabric.util.ease['easeInQuad']
}).animate('top', c2.y, {
startValue: c1.y,
endValue: c2.y,
onChange: canvas.renderAll.bind(canvas),
onComplete: function() {
completedTop = true;
completedLeft && canvas.remove(circle);
},
easing: fabric.util.ease['easeInQuad']
});
};
Updated fiddle with ball removal at the end of animation.

Animating and moving a draggable shape in KineticJS's dragend event

I would like to animate moving a draggable shape to another position after it has been dragged in KineticJS. I would like to animate the movement of the shape over a period of time (for example, over 1 second). For example, I create a draggable shape and save its initial xy coordinates. I register a "dragend" event on this shape. Then, I drag the shape to a new position. When I release the drag, the dragend event is called. In that event function, I want to animate/ease the shape back to its original position. See my JSFiddle for a complete example: DragSample.
(function () {
//create variables at global scope
var layer;
var stage;
var triangle;
var triangleLastX = 190;
var triangleLastY = 120;
var tween;
function initTween() {
tween = new Kinetic.Tween({
node: triangle,
duration: 1,
easing: Kinetic.Easings.EaseInOut,
x: 400,
y: 200,
});
}
this.init = function () {
layer = new Kinetic.Layer();
stage = new Kinetic.Stage({
container: 'container',
width: 800,
height: 600
});
triangle = new Kinetic.RegularPolygon({
x: 190,
y: 120,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
triangle.on('dragstart', function () {
triangleLastX = triangle.attrs.x;
triangleLastY = triangle.attrs.y;
});
triangle.on('dragend', function () {
tween.play();
stage.draw();
});
layer.add(triangle);
stage.add(layer);
initTween ();
}
window.onload = init();
})();
I have tried doing this several ways. The last way I attempted to do this was using Kinetic's Tween(), however, when I play this Tween from the dragend event handler function, it moves the shape back to its original position immediately (i.e. the position when the drag started), then applies the Tween.
Is there any way to achieve animating the movement of a draggable shape to its original position (or any other position for that matter) in dragend using KineticJS?
As I mentioned in my comment above, I have found the solution and that is to simply call my initTween() function in the dragend event handler. Following is the updated source for DragSample
(function () {
//create variables at global scope
var layer;
var stage;
var triangle;
var triangleLastX = 190;
var triangleLastY = 120;
var tween;
function initTween() {
tween = new Kinetic.Tween({
node: triangle,
duration: 1,
easing: Kinetic.Easings.EaseInOut,
x: triangleLastX,
y: triangleLastY,
});
}
this.init = function () {
layer = new Kinetic.Layer();
stage = new Kinetic.Stage({
container: 'container',
width: 800,
height: 600
});
triangle = new Kinetic.RegularPolygon({
x: 190,
y: 120,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
triangle.on('dragstart', function () {
});
triangle.on('dragend', function () {
initTween();
tween.play();
stage.draw();
});
layer.add(triangle);
stage.add(layer);
}
window.onload = init();
})();

Bouncing image balls with KineticJS

I'm just starting out the KineticJS library and been playing about with it creating shapes etc.. However, I'm struggling to create a custom circle with my own image in it. I have tried using the fillPattern but it doesn't scale/centre correctly at all. Am I meant to use my own circle image or a rectangle image and then let KineticJS take care of things?
Just to give a bit of background: What I want is 3 balls bouncing in and then settling in place.
Any advice is welcome.
Sorted it... needed the offset values
var ball = new Image();
ball.src = 'ball2.jpg';
ball.height = 230;
ball.width = 230;
var stage = new Kinetic.Stage({
container: 'container',
width: 1000,
height: 1000
});
var circle = new Kinetic.Circle({
x: 300,
y: 300,
radius: 115,
fillPatternImage: ball,
fillPatternOffset :{
x: -115,
y: -115
}
});
var layer = new Kinetic.Layer();
// add the shape to the layer
layer.add(circle);
// add the layer to the stage
stage.add(layer);
ball.onload = function () {
stage.draw();
}

animate to a specific point

var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var Circle = new Kinetic.Circle ({
x: 100,
y: 100,
radius: 10,
fill: 'green',
stroke: 'black',
strokeWidth: 5
});
layer.add(Circle);
stage.add(layer);
var a = 1;
var anim = new Kinetic.Animation(function(frame) {
Circle.setX(frame.time * 350 / 1000 + 100);
}, layer);
anim.start();
How do i stop the animation at a specific point or coordinate? like animate to x=700 and then stop. i want to have a circle that is able to animate with a button to coordinate x=700, stop and then stop, and after that with another button back or down.
thank you.
There are 2 ways,
1.
var anim = new Kinetic.Animation(function(frame) {
if(Circle.getX() < 700)
Circle.setX(frame.time * 350 / 1000 + 100);
else
this.stop();
}, layer);
OR2. Use Kinetic.Transition check here

Categories

Resources