Upgraded from 4.0.5 to 4.4.1 because Chrome stopped rendering correctly.
However, in the 4.0.5 version it was possible to draw a line in a Kinetic.Shape object and detect mouse events on it. This seems no longer the case. Even when using the recommended Canvas.fillStroke(this) call.
Here is some code:
var myshape = new Kinetic.Shape({
drawFunc: function(canvas) {
var context = canvas.getContext();
context.beginPath();
context.setLineWidth(20);
context.moveTo(100, 10);
context.lineTo(100, 60);
context.closePath();
context.stroke(); //this does
//canvas.fillStroke(this); //this doesn't bring the line on the screen
//context.fill(); //this doesn't make the event work either
context.beginPath();
context.setLineWidth(10);
context.moveTo(100, 60);
context.lineTo(100, 120);
context.closePath();
//canvas.fillStroke(this); //this doesn't bring the line on the screen
context.stroke(); //this does
canvas.fillStroke(this);
},
draggable: true
});
myshape.on('mousedown', function(event){
alert('mousedown');
});
An example in this fiddle: http://jsfiddle.net/GDQ6G/ (Only seems to render the line in Chrome. Not in firefox)
Another example here on this test page: http://www.planetinaction.com/linetest.htm
It is clear I am doing something wrong since this code doesn't render in Firefox. Can someone please show me this is done in the linked fiddle? The documentation for a shape shows how to draw a single item. I need to draw multiple items to form my custom shape as illustrated with this simplified two line example.
As it turns out based on Eric Rowells answer, a shape can only contain one path. That is a pity because version 4.0.5 was able to handle multiple paths until Google changed something funky in Chrome.
Anyway, the answer I was looking for is held in KineticJS groups. The code gets a lot more elaborate but it works.
var stage = new Kinetic.Stage({
container: 'container',
width: $('#container').width(),
height: $('#container').height()
});
var layer = new Kinetic.Layer('spline');
var group = new Kinetic.Group({
draggable: true,
});
group.add(new Kinetic.Shape({
drawFunc: function(canvas) {
var context = canvas.getContext();
context.beginPath();
context.moveTo(100, 10);
context.lineTo(100, 60);
context.closePath();
canvas.stroke(this);
},
strokeWidth: 6,
}));
group.add(new Kinetic.Shape({
drawFunc: function(canvas) {
var context = canvas.getContext();
context.beginPath();
context.moveTo(100, 60);
context.lineTo(100, 120);
context.closePath();
canvas.stroke(this);
},
strokeWidth: 20,
}));
group.on('mousedown', function(event){
alert('mousedown');
});
group.on('mouseover', function(event){
alert('mouseover');
});
layer.add(group);
stage.add(layer);
Here is the code in Fiddle: http://jsfiddle.net/QcsBH/
I could not find a reference in the documentation with regard to event handling by a group but I am pleasantly surprised to see that a group handles events of all the members in it.
Each KineticJS shape should only have one beginPath() and one closePath() execution. You also shouldn't ever directly stroke or fill using the context. You need to use the methods tied to the KineticJS canvas renderer:
canvas.stroke(this);
canvas.fill(this);
canvas.fillStroke(this);
Here's an example of drawing custom shapes:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-shape-tutorial/
If you bind a simple listener to the triangle in that tutorial, the event fires correctly (you can modify the code right on the page)
Related
I’d like to rebuild this animation http://imgur.com/l5Vhswe in paper.js.
I already tried SVG animations (http://codepen.io/magglomag/pen/jrVwzy) but despite from the fact that they’ll be deprecated soon I was not able to move the two points asynchronously.
What I have so far is the shape and I know that I can animate with the onFrame event handler. But I have no clue how to say that the point should animate between the coordinates [43,168.7] and [43,35.3].
http://codepen.io/magglomag/pen/yaVXrr
var firstSegment = new Segment({
point: [109,3.7]
});
var secondSegment = new Segment({
point: [43,168.7]
});
var thirdSegment = new Segment({
point: [109,202.2]
});
var path = new Path({
segments: [firstSegment, secondSegment, thirdSegment],
fillColor: '#2dfd9a',
closed: true
});
secondSegment.onFrame = function(event) {
this.point = [43,35.3]
}
The error you are making is that you are trying to bind an handler to segment.onFrame event.
But only item.onFrame and view.onFrame are available.
In PaperScript context, you can even use a global onframe named function as a convenient way to animate things.
Here is a simple example demonstrating how a path segment can be animated.
// create a triangle
var triangle = new Path.RegularPolygon({
center: view.center,
sides: 3,
radius: 50,
fillColor: 'orange'
});
// store initial first point position
var initialPoint = triangle.firstSegment.point.clone();
// on frame
function onFrame(event) {
// use sine function as a convenient way to demonstrate animation
var newPoint = initialPoint + Math.sin(event.count * 0.05) * 30;
// update first point
triangle.firstSegment.point = newPoint;
}
I'm trying to detect mouse events (currently mousedown) on a Kinetic.Group containing a grid made of Kinetic.Line's
I'm listening to the mousedown event on the Layer. When it happens that i hit a line, no event is fired.
var grid = new Kinetic.Group({
x: 0,
y: 0,
width: this.group.width(),
height: this.group.height()
});
grid.on("mousedown", function(){
alert("At least this one should fire!");
});
var gridX = this.gridWidth, gridY = this.gridHeight;
this.group.add(grid);
while(gridY < this.rect.height()){
var line = new Kinetic.Line({
points : [0,gridY, this.rect.width(), gridY],
stroke: "grey",
strokeWidth: 1
});
grid.add(line);
gridY += this.gridHeight;
}
while(gridX < this.rect.width()){
var line = new Kinetic.Line({
points : [gridX,0, gridX, this.rect.height()],
stroke: "grey",
strokeWidth: 1
});
grid.add(line);
gridX += this.gridWidth;
}
I found this post:
Kinetic.Line mouseover
The answer mentioned there is using "saveData()" on the shape. This seems to be old because this method does not exist in Kinetic.Shape.
The example where the above post is pointing to is for images. And it uses the cache() method to create a hit graph or something. I tried that for my lines but this won't work either.
How can i simply detect mouse events on a Kinetic.Line?
Just in case you are still looking for an answer to this #Chris, and in case others find your thread, I believe you and I were suffering from the same issue. #Sjiep so graciously provided a fix for my problem under this thread.
Turns out it was indeed a bug!
I have this fiddle where I have text arranged in a circle, I would like now to animate it and rotate the text in a clockwise/counter clockwise motion.
Every animation demo I have seen uses a container as the starting point however all the examples i could find about manipulating text in a circular arrangement have all started with the element. I have tried 100's of variations trying to get this working but I am either missing something or it's not possible with the construction i have used thus far.
Here is the fiddle for the circular text I have so far:
http://jsfiddle.net/jamesburt/Sa2G8/
<canvas id="canvas1" width="500" height="500"></canvas>
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
Where as the animation examples start off with:
<div id="container"></div>
var stage = new Kinetic.Stage({container: 'container'});
I'm open to any ideas / rewrites needed as ultimately my goal is an animated text circle.
Also if this is easily accomplished in an alternative to KineticJS I'd be interested in trying that out.
Here is a demo I made using KineticJS: http://jsfiddle.net/Moonseeker/Xf7hp/
var stage = new Kinetic.Stage({
container: 'container',
width: 500,
height: 500
});
var layer = new Kinetic.Layer();
var myText = "My text in a circle. ";
var centerCoords = {x:250, y:250};
for(var i=0;i<myText.length;i++){
var rotation = i*360/myText.length;
var oneChar = new Kinetic.Text({
x: centerCoords.x,
y: centerCoords.y,
text: myText[i],
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green',
offset: {x:0, y:100},
rotationDeg: rotation
});
layer.add(oneChar);
}
// add the layer to the stage
stage.add(layer);
var angularSpeed = Math.PI / 2;
var anim = new Kinetic.Animation(function(frame){
var angleDiff = frame.timeDiff * angularSpeed / 1000;
for(var i=0;i<layer.children.length;i++){
layer.children[i].rotate(angleDiff);
};
}, layer);
anim.start();
You can rotate at every direction or speed you wish, you can change the style of the circle.
You should be able to use layer.find('Text').each() instead of the for-loop for looping through the text to rotate but the KineticJS library at jsfiddle seems outdated as it doesn't know the find method.
One efficient way:
Render your text-around-a-circle on an offscreen canvas.
Save that offscreen canvas as an image using .toDataURL
Create a Kinetic.Image from that offscreen image.
You can then efficiently rotate/animate the Kinetic.Image as you need.
I am using the following to change the style of the cursor when the mouse is over the circle:
circle1.on('mouseover', function () {
document.body.style.cursor = 'pointer';
});
circle1.on('mouseout', function () {
document.body.style.cursor = 'default';
});
It works great if I draw the circle using:
var circle1 = new Kinetic.Circle({
x: 512,
y: 512,
radius: 140,
stroke: '#00ffff',
strokeWidth: 4,
opacity: 0.5
});
However if I use:
var circle1 = new Kinetic.Circle({
drawFunc: function (canvas) {
var context1 = canvas.getContext();
context1.beginPath();
context1.arc(512, 512, this.getRadius(), 0, 2 * Math.PI, false);
context1.lineWidth = this.getStrokeWidth();
context1.strokeStyle = this.getStroke();
context1.stroke();
},
radius: 140,
stroke: '#00ffff',
strokeWidth: 15,
opacity: 0.5
});
It does not work! The cursor does not change its style; can we just use radius for mouse over. I would appreciate your suggestions, thanks in advance.
As I know you also need to define "drawHitFunc":
circle1.setDrawHitFunc(function (canvas) {
var context2 = canvas.getContext();
context2.beginPath();
context2.arc(100, 100, this.getRadius(), 0, 2 * Math.PI, false);
context2.closePath();
canvas.fillStroke(this);
});
Example: http://jsfiddle.net/lavrton/4DJdU/1/
no, you just need to correctly structure the drawFunc when creating custom shapes. Here's an example:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-shape-tutorial/
The problem is that you're using context.stroke(). You need to use canvas.stroke(this);
Anytime you actually render something, like strokes and fills, you need to use the canvas renderer object because it draws onto both the scene graph (what you see) and a specialized hit graph (used for event detection)
Docs:
http://kineticjs.com/docs/symbols/Kinetic.Canvas.php
I have tried to find a solution on how to implement fixed score table to visible stage (canvas) area by using kineticjs library layers and stages. The basic idea is that shape layer can be scrolled and stage is wider than the visible browser area but the score table should be all the time fixed to certain position on the visible area like in many casual games.
What would be the best approach on creating this kind of fixed always visible shapes to stage/canvas? Is there a way to refer to visible area xy position?
Your best bet for making a fixed position group is either to use a second layer on the stage or to create two groups for your objects - one that remains in place and another that can be moved. I'd go with the dual-layer approach as this allows you to redraw the layers separately so that when one has updated information, you don't need to redraw the other.
cvsObj.page = new Kinetic.Layer();
cvsObj.dynamic = new Kinetic.Layer();
cvsObj.stage.add(cvsObj.page);
cvsObj.stage.add(cvsObj.dynamic);
I took the two layer approach but actually my challenge was how to implement 'page' static behavior. I figured out that as it seems that Kinetic layers/stages do not offer methods for this I need to use other means to make 'page' layer on top of viewable area. I used jquery scrollTop() and scrollLeft()functions for this. And layer method .setAbsolutePosition() is used to update position for whole layer. Here pageLayer position is updated and dynamicLayer not so naming might be bit confusing but reflects what you see on browser.
Here is a simple example what does what I was looking for. Now the green rectangle moves when you scroll stage and the red rectangle and the x/y position counter stay on visible area. I attach this solution here with tutorial type of content as it might be useful.
<script>
window.onload = function() {
var stage = new Kinetic.Stage({
container: 'container',
width: 1600, height: 1600
});
var layerDynamic = new Kinetic.Layer();
var layerPage = new Kinetic.Layer();
var rectGreen = new Kinetic.Rect({
x: 239, y: 75, width: 100, height: 50, fill: 'green'
});
var rectRed = new Kinetic.Rect({
x: 100, y: 75, width: 100, height: 50, fill: 'red'
});
var simpleText = new Kinetic.Text({
x: 1, y: 1, text: "Init", textFill: "black"
});
layerDynamic.add(rectGreen);
layerPage.add(rectRed);
layerPage.add(simpleText);
$(document).scroll(function(e) {
var visibleXTop = $(this).scrollTop();
var visibleYTop = $(this).scrollLeft();
simpleText.setAttrs({text: visibleXTop + ' ' + visibleYTop});
layerPage.setAbsolutePosition(visibleYTop , visibleXTop);
layerPage.draw();
});
stage.add(layerDynamic);
stage.add(layerPage);
};
</script>