This is driving me crazy. If I run the simple HTML below it works on my IPad2 (A1396) but not on my IPad 3 (A1416). Whenever I rotate the IPad 3 from Portrait to Landscape Safari will crash. I looks like there is a relation between the number of layers added and the width/height (of each layer).
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.2.min.js"></script>
<script defer="defer">
var stage = new Kinetic.Stage({
container: 'container',
width: 580,
height: 400
});
var NrOfAttribute = 50;
for (var AttributeNo = 0, NrOfAttribute; AttributeNo < NrOfAttribute; AttributeNo++) {
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: 20*AttributeNo,
y: 20*AttributeNo,
width: 50,
height: 10,
fill: 'green',
stroke: 'black',
strokeWidth: 4
});
// add the shape to the layer
layer.add(rect);
// add the layer to the stage
stage.add(layer);
}
</script>
</body>
</html>
Is this a bug or some memory problem on the IPad? Any help would be appreciated because I really need this to work.
Thanks!
Edit: I just tested to do the same with "regular" Canvas and if I increase the number of layers it will also crash (so it doesn't seem to be Kinetic specific). It will also crash on Ipad2 if you increase the number of layers enough.
This might not be an option for you, but do you need a separate layer for each Rect, or could you put them all in the same layer? I suspect that would help reduce memory usage, as you'd wouldn't end up with 50 canvas elements. You could give each Rect a unique name and then still be able to manipulate them individually with something like rect = layer.find('.{name}'); # do something with rect
Related
I want to create an application that the user will have the ability to click on a picture add points and for each (certain) pairs of points the software will draw a line between those two. The points are draggable so the line must be able to readjust itself to the new position of its two ancors. The points are pre-specified and more than 5 or 6 (it can be 10 or more)
So my design so far.
One kinetic stage
One backgroundLayer which will have the following children:
backgournImage (will hodl the image to click on)
The points aded
the lines between the points
I guess for each of the lines I will have to use a group that will contain the two ancors and the line. My problem is the following: Is there a convenient way to make sure a group is allready created for a certain pair of points so as not to create a new one when clicking on picture to add a new point?
stage.('contentClick', function(event){
//create a new point
// if a group for the specific pair of ancors exists
//add the point and draw the line
// else this is the first point of the pair we are talking about
// so create the group
//and add the new point
// add the group on the backgroundLayer
//redraw stage
);
All points can have a specific id the name of the point.
I know the code for creating points groups adding them removing them checking parents etc, just don't know how could I do it with the least manual method. I mean it's not very productive checking each and every one of the points right?
Hope I am making some sense...
One way is to track pairs of anchors that must be connected & reset their connecting lines when an anchor is dragged:
Create a Line to act as a connector.
Create a js object that holds references to the 2 anchors and the line connecting them.
Add the connection-object in an array.
When any anchor is dragged, iterate the array & reset the connectors for the anchor that was dragged.
Example code and a Demo: http://jsfiddle.net/m1erickson/b7h5dfg5/
<!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-v5.0.1.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 connectors=[];
var a1=addAnchor(50,50,'one');
var a2=addAnchor(150,50,'two');
var a3=addAnchor(100,100,'three');
addConnection(a1,a3);
addConnection(a2,a3);
function resetConnections(id){
for(var i=0;i<connectors.length;i++){
var c=connectors[i];
if(c.id1==id || c.id2==id){
c.line.points([
c.anchor1.x(),
c.anchor1.y(),
c.anchor2.x(),
c.anchor2.y()
]);
}
}
layer.draw();
}
function addAnchor(x,y,id){
var anchor=new Kinetic.Circle({
id:id,
x:x,
y:y,
radius: 10,
fill: 'red',
stroke: 'black',
strokeWidth:2,
draggable: true
});
anchor.on('dragstart',function(){
stage.find(".connector").each(function(n){ n.hide(); });
layer.draw();
})
anchor.on('dragend',function(){
stage.find(".connector").each(function(n){ n.show(); });
resetConnections(this.id());
})
layer.add(anchor);
layer.draw();
return(anchor);
}
function addConnection(anchor1,anchor2){
var line=new Kinetic.Line({
name:'connector',
points:[anchor1.x(),anchor1.y(),anchor2.x(),anchor2.y()],
stroke:'black'
});
layer.add(line);
line.moveToBottom();
layer.draw();
connectors.push({
line:line,
id1:anchor1.id(),
id2:anchor2.id(),
anchor1:anchor1,
anchor2:anchor2
});
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag red anchors and connectors will follow</h4>
<div id="container"></div>
</body>
</html>
My problem started with the version 5 of KineticJS, before that it was not a problem. Native KineticJS shapes such as squares and circles can be saved to an image file using the stage.toDataURL function. But it doesn't work for non-Kinetic shapes drawn with normal canvas methods such as beginPath(); and canvas.fill(); (version 4 did this fine). The following code draws two rectangles, one red and one blue. The red is custom, the blue is a native kinetic rectangle.
<body>
<div id="container"></div>
<button id="save">
Save as image
</button>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.0.2.min.js"> </script>
<script>
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var box = new Kinetic.Rect({
x: 400,
y: 80,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
layer.add(box);
stage.add(layer);
var canvas = layer.getCanvas().getContext('2d');
canvas.beginPath();
canvas.setAttr('strokeStyle', 'black');
canvas.setAttr('fillStyle', '#FF2222');
canvas.setAttr('lineWidth', 8);
canvas.rect(50,80,100,50);
canvas.stroke();
canvas.fill();
document.getElementById('save').addEventListener('click', function() {
stage.toDataURL({
callback: function(dataUrl) {
window.location.href = dataUrl;
}
});
}, false);
</script>
</body>
Both shapes appear, but only the blue rectangle appears in the image generated by the toDataURL function. The way they are drawn has changed in KineticJS 5, where you set attributes for fillStyle etc. so I'm thinking that may have something to do with it, or maybe the fact that the custom shape is added after the layer is added to the stage...
You are correct, between recent versions much has changed, and this has probably broken something in your drawing function.
You should consult the official docs on each item, but basically a custom shape has slightly updated properties... first of all "StrokeStyle" is no longer a valid property. Just use 'stroke'. Same thing with FillStyle.
Also -- 'dashArray' is no longer valid, now it's just 'dash' -- so I'm sure there are more things that changed that I'm not recalling... right, such as 'lineWidth' is now 'strokeWidth'...
Also -- the way you show or don't show strokes and fills has changed... yep, pretty much most of the way you used to do it has been changed slightly. 'drawFunc' is now 'sceneFunc' also...
var ctx = layer.getContext();
var customShape01 = new Kinetic.Shape({
sceneFunc: function(ctx) {
ctx.beginPath();
ctx.moveTo(162.1, 213.8);
ctx.bezierCurveTo(162.1, 213.8, 180.7, 215.3, 193.5, 214.5);
ctx.bezierCurveTo(205.8, 213.7, 221.8, 212.3, 222.8, 221.4);
ctx.bezierCurveTo(222.9, 221.7, 222.9, 222.0, 222.9, 222.3);
ctx.bezierCurveTo(222.9, 232.4, 204.6, 232.7, 192.0, 227.1);
ctx.bezierCurveTo(179.4, 221.5, 163.1, 213.8, 162.1, 213.8);
ctx.closePath();
ctx.fillStrokeShape(this);
},
id: 'customShape01',
fill: 'rgb(255, 0, 255)',
stroke: 'black',
strokeWidth: 2,
lineJoin: 'round',
dash: [5,5],
dashEnabled: 'true',
strokeEnabled: 'true'
});
check out a full working sample (you'll have to allow popups).
http://jsfiddle.net/axVXN/1/
I have a Kinetic JS stage and a layer
var stage = new Kinetic.Stage({
container: 'container',
width : STAGE_WIDTH,
height : STAGE_HEIGHT
});
var layer = new Kinetic.Layer();
I've set the page color to be #bbb.
body {
background: #bbb;
}
I'd like to set the canvas color to be white. But I can't seem to find a method or a way to add a background color to the stage itself or the layer that I add all the object on.
You can also set the background color of your container element through CSS. That's essentially the same as setting the background color of the stage. If you want a background at the layer level, you'll need to add a filled rectangle or similar, as previously mentioned.
I had the same problem, I wanted to add a "background". I added a rectagle with the 100% height and width, with this code:
var rect = new Kinetic.Rect({
x: 0,
y: 0,
width: stageDimensions.width, //full width
height: stageDimensions.height, //full height
fill: 'white', //background color
});
layer.add(rect);
Since I wanted to be able to remove the "background", this is how I manage to solve my problem.
Hope it helps you.
You can change the background color with JavaScript...
document.getElementById('container').style.background = '#fff';
There isn't an API method to add a background color.
Instead add a colored rectangle that covers the layer.
Of course, add the background rectangle before all other shapes.
Old question, but you can use the get properties of the stage, and fill a full rectangle, adding it to the layer before anything else. Sample code:
var stage = new Kinetic.Stage({
container: 'canvas-container',
width: 900,
height: 450
});
// create background
var stageBg = new Kinetic.Rect({
x: 0,
y: 0,
width: stage.getWidth(),
height: stage.getHeight(),
fill: "rgb(40,40,40)"
});
layer.add(stageBg);
stage.add(layer);
I am animating a circle using Raphael. When the circle is large I get artifacts around the circle when its moving. It seems to be something of a clipping / redraw region issue and wondered if there was a work around?
It seems to be OK in firefox (if a little jerky) and appears very reliably in Chrome. It also is exacerbated by using opacity on the fill property i.e. rgba(255,0,0,0.7)
Here is a jsFiddle showing the issue. Just click around the paper on the right to move the circle.
Code:
var discattr = {
fill: "#666",
stroke: "none",
width: 35
};
var paper = Raphael("svgcontainer", 400, 400);
circle = paper.circle(150, 150, discattr.width, discattr.width).attr({
stroke: "none",
fill: "rgba(255,0,0,0.7)"
});
var coords = []
var animateCircle = function(coords) {
if (!coords.length) return;
var nextCoords = coords.shift()
var move = Raphael.animation(nextCoords, 500, "linear", function() {animateCircle(coords)});
circle.animate(move);
}
$("#svgcontainer").on("mouseup", function(e) {
coords.push({cx: e.pageX, cy: e.pageY})
animateCircle(coords);
});
Buffering is a technique used to prevent animation artifacts (tearing, as JamWaffles points out). If you look at the answer to this Stack Overflow question you'll find information about an SVG setting to turn on buffering, but so far it doesn't appear to be supported by major browsers.
I've a problem which deals with canvas.
I would like to use kinetic to use mobile events (most particularly for draggable) and I would also like to use the clip() function at the same time.
Here is my code :
<!DOCTYPE HTML>
<html>
<head>
<style>
canvas {
border: 1px solid #9C9898;
}
</style>
<script src="kinetic-v3.9.4.js"></script>
<script>
window.onload = function() {
var stage = new Kinetic.Stage({
container: "container",
width: 708,
height: 500
});
var layer = new Kinetic.Layer();
var circle1 = new Kinetic.Circle({
x: 150,
y: 150,
radius: 75,
fill: "red",
draggable: true
});
var circle2 = new Kinetic.Circle({
x: 350,
y: 150,
radius: 75,
fill: "blue",
});
layer.add(circle2);
layer.add(circle1);
stage.add(layer);
};
</script>
</head>
<body onmousedown="return false;">
<div id="container"></div>
</body>
</html>
I've the first circle draggable but I would like to use it and the clip function but I don't really know how to do it. In addition, I tried to get the 2d context like this:
var context = this.getContext();
But it didn't work. If there's someone that can help me thanks.
I also saw this example but it doesn't work with mobile devices.
The reason why the example you mentioned doesn't work on mobile is because its only using mouse event handlers:
stage.on("mousemove", function(){})
But you'll need to add touch events like this:
stage.on("mousemove touchmove", function(){})
touchstart, touchmove, touchend
Good luck!