KineticJS
KineticJS is an HTML5 Canvas JavaScript framework that extends the 2d context by enabling canvas interactivity for desktop and mobile applications.
KineticJS
I'm starting a new project using HTML5. Two of the most popular graphical toolkits are KineticJS and RaphaelJS. If you have experience of using these, do you have any advice? Which features do they offer, and is there an advantage in using one over the other?
E.g. only RaphaelJS works on legacy browsers (but this in not a feature I require).
Source: (StackOverflow)
I wondered if anyone could shed some light on the way in which layers are managed in Canvas and KineticJS. I'm struggling understand why when i clear(); a layer, then use the draw(); function on that layer again, it comes back with the shapes etc that I originally add to that layer.
for example:
http://jsfiddle.net/vPGbz/1/
I assumed clearing a layer completely removes it, and in order to redraw it i would have to set up new shapes and construct a new layer.
If anyone could explain this too me I would be very thankful.
Cheers, Caius
Source: (StackOverflow)
I'm working on a project with HTML5 Canvas and KineticJS. A half-transparent overlay (a KineticJS group or layer) is placed all over the scene. The problem is: Mouse events bound to KineticJS objects under this overlay are not processed.
How can I make this overlay (or any other object) "transparent" to mouse events?
NOTE: The question is only about the Canvas, there are no other HTML elements interfering to it. (To make clear what was asked below.)
Source: (StackOverflow)
I am using using JavaScript library Kinetic.js
for HTML5 canvas.
Here is the method stage.get()
to get objects from stage (canvas). I have assigned the id's or class names to each object
if I get the object by id var obj = stage.get('#obj_id')
it works but If try to get the multiple objects by class name var objs = stage.get('.obj_class_name')
it returns an empty []
object
How I can get multiple objects from stage .
Source: (StackOverflow)
Is there a way one could zoom and pan on a canvas using KineticJS? I found this library kineticjs-viewport, but just wondering if there is any other way of achieving this because this library seems to be using so many extra libraries and am not sure which ones are absolutely necessary to get the job done.
Alternatively, I am even open to the idea of drawing a rectangle around the region of interest and zooming into that one particular area. Any ideas on how to achieve this? A JSFiddle example would be awesome!
Source: (StackOverflow)
I implemented a function that detects collision between two shapes, and another function that limits the dragBound to the borders when collision is detected, in order to implement the snap functionality, and then add to Kinetic.Group
. So practically I'm limiting the drop target to the borders of an existing rectangle.
Here's a jsfiddle for a clear view of my problem: http://jsfiddle.net/4Y87X/6/
This logic worked for the big rectangles, when width = height. But when I drag the little grey rectangle - which represents a window in my project - the collision is detected on the right and on top of the big rectangle only, the left and bottom are not detected! Is this due to some math equations in the DragBoundWindow method?
If anyone could locate the error, please?
CODE :
var stage = new Kinetic.Stage({
container: "container",
width: 900,
height: 600
});
var leftLayer = new Kinetic.Layer({
x:0,
y:0,
id: 'leftLayer',
width:250,
height:600
});
var leftBackground = new Kinetic.Rect({
width: 250,
height: 600,
stroke: "red"
});
leftLayer.add(leftBackground);
stage.add(leftLayer);
var rightLayer = new Kinetic.Layer({
x: 280,
y:0,
id: 'rightLayer',
width:600,
height:600
});
var rightBackground = new Kinetic.Rect({
x: 0,
y: 0,
width: 600,
height: 600,
stroke: "green"
});
rightLayer.add(rightBackground);
stage.add(rightLayer);
//draw a room
var room = new Kinetic.Rect({
width: 100,
height: 100,
fill: 'cornsilk',
stroke: 'black',
strokeWidth: 3,
dragOnTop: false
});
var window = new Kinetic.Rect({
x: 30,
y: 250,
width: 40,
height: 4,
name: 'window',
id: "window_ID",
fill: '#ccc',
stroke: 'black',
strokeWidth: 1
});
var rooms = [];
var id = 0
var group = new Kinetic.Group({
x: 10,
y: 100,
width: 100,
height: 100,
id: id++
});
group.add(room);
leftLayer.add(group);
leftLayer.add(window);
stage.draw();
group.on('mousedown touchstart', function(e){
if (e.which != 3) {
var cloneOfItem= group.clone({added: false, id: id++});
cloneOfItem.setDraggable(true);
cloneOfItem.off('mousedown touchstart');
leftLayer.add(cloneOfItem);
cloneOfItem.dragBoundFunc(dragBoundCarre);
cloneOfItem.on('dragmove', function(){
this.moveToTop();
});
cloneOfItem.on('dragend', function(){
var userPos = stage.getPointerPosition();
if (userPos.x>280 && userPos.y>0 && !this.getAttr('added'))
{
this.setAttr('added', true);
this.x(this.x() - 280);
this.moveTo(rightLayer);
rooms.push(this);
}
else if (userPos.x<280)
{
this.destroy();
rightLayer.draw();
rooms.splice(this,1);
}
stage.draw();
});
cloneOfItem.fire('mousedown');
cloneOfItem.fire('touchstart');
}
});
window.on('mousedown touchstart', function(e){
if (e.which != 3) {
var cw = window.clone({draggable:true});
leftLayer.add(cw);
cw.off('mousedown touchstart');
cw.dragBoundFunc(dragBoundWindow);
cw.on('dragstart', function(){
this.startX=this.x();
this.startY=this.y();
this.lastPos=this.position();
this.getParent().moveToTop();
});
cw.on('dragmove', function(){
var pos=this.position();
this.lastPos = pos;
rightLayer.draw();
});
cw.on('dragend', function(e){
var userPos = stage.getPointerPosition();
if (userPos.x > 280 && userPos.y > 0)
{
var pos = cw.getAbsolutePosition();
var x = pos.x;
var y = pos.y;
var hit = -1;
for (var i = 0; i < rooms.length; i++)
{
var c = rooms[i];
var cpos = c.getAbsolutePosition();
var cx = cpos.x + room.x();
var cy = cpos.y + room.y();
//detect collision
if (x >= cx && x <= cx + c.width() && y >= cy && y <= cy + c.height())
{
hit = i;
rooms[hit].off('dragend');
this.x(x - rooms[hit].x()-280);
this.y(y - rooms[hit].y());
this.moveTo(rooms[hit]);
}
}
if (hit < 0)
{
this.x(x - 280);
this.y(y);
this.moveTo(rightLayer);
alert("Window is not in the rigth target. \n\nTry again.");
}
}
else
{
this.remove();
}
rightLayer.draw(this);
stage.draw();
});
cw.fire('mousedown');
cw.fire('touchstart');
cw.fire('dragstart');
}
rightLayer.draw();
});
stage.add(rightLayer);
stage.add(leftLayer);
stage.draw();
function dragBoundCarre(pos, event) {
var collided = doObjectsCollide(pos, this);
if (collided.length > 1) {
pos.x = this.getAbsolutePosition().x;
pos.y = this.getAbsolutePosition().y;
}
else if (collided.length === 1) {
collided = collided[0];
var colAbsX = collided.getAbsolutePosition().x;
var colAbsY = collided.getAbsolutePosition().y;
var tempPos = Object.create(pos);
tempPos.x -= colAbsX;
tempPos.y -= colAbsY;
if (Math.abs(tempPos.x) > Math.abs(tempPos.y) && tempPos.x < 0 && Math.abs(tempPos.x) < collided.width()) {
pos.x = colAbsX - this.width();
} else if (Math.abs(tempPos.x) > Math.abs(tempPos.y) && tempPos.x > 0 && Math.abs(tempPos.x) < collided.width()) {
pos.x = colAbsX + collided.width();
} else if (Math.abs(tempPos.x) < Math.abs(tempPos.y) && tempPos.y < 0 && Math.abs(tempPos.y) < collided.height()) {
pos.y = colAbsY - this.height();
} else if (Math.abs(tempPos.x) < Math.abs(tempPos.y) && tempPos.y > 0 && Math.abs(tempPos.y) < collided.height()) {
pos.y = colAbsY + collided.height();
}
}
return {
x: pos.x,
y: pos.y
};
}
function dragBoundWindow(pos, event, group) {
var collided = doObjectsCollide(pos, this);
if (collided.length > 1) {
pos.x = this.getAbsolutePosition().x;
pos.y = this.getAbsolutePosition().y;
}
else if (collided.length === 1) {
collided = collided[0];
var colAbsX = collided.getAbsolutePosition().x;
var colAbsY = collided.getAbsolutePosition().y;
var tempPos = Object.create(pos);
tempPos.x -= colAbsX;
tempPos.y -= colAbsY;
if (Math.abs(tempPos.x) > Math.abs(tempPos.y) && tempPos.x < 0 && Math.abs(tempPos.x) < collided.width()) {
pos.x = colAbsX - this.width() + 40;
//switch width and height
this.setWidth(window.getHeight());
this.setHeight(window.getWidth());
console.log('collision: left');
} else if (Math.abs(tempPos.x) > Math.abs(tempPos.y) && tempPos.x > 0 && Math.abs(tempPos.x) < collided.width()) {
pos.x = colAbsX + collided.width();
this.setWidth(window.getHeight());
this.setHeight(window.getWidth());
console.log('collision: right');
} else if (Math.abs(tempPos.x) < Math.abs(tempPos.y) && tempPos.y < 0 && Math.abs(tempPos.y) < collided.height()) {
pos.y = colAbsY - this.height();
this.setWidth(window.getWidth());
this.setHeight(window.getHeight());
console.log('collision: top');
} else if (Math.abs(tempPos.x) < Math.abs(tempPos.y) && tempPos.y > 0 && Math.abs(tempPos.y) < collided.height()) {
pos.y = colAbsY + collided.height();
this.setWidth(window.getWidth());
this.setHeight(window.getHeight());
console.log('collision: bottom');
}
}
return {
x: pos.x,
y: pos.y
};
group.add(this);
}
function doObjectsCollide(pos, a) {
var col = [];
for (var i = 0; i < rooms.length; i++) {
var b = rooms[i];
if (a.id() != b.id()) {
if (!((pos.y + a.getHeight()) < (b.getAbsolutePosition().y) || (pos.y > (b.getAbsolutePosition().y + b.getHeight())) || ((pos.x + a.getWidth()) < b.getAbsolutePosition().x) || (pos.x > (b.getAbsolutePosition().x + b.getWidth()))))
{
col.push(b);
}
}
}
return col;
}
Source: (StackOverflow)
I'd like to draw a rectangle with click and drag. How can I do this ? Where do I have to put my click event listener ? On the stage or on the layer ? I have the following code but it doesn't work :
stage = new Kinetic.Stage({...})
layer = new Kinetic.Layer({...})
stage.add(layer)
stage.on('click', function() {
var pos = stage.getMousePosition();
var rect = new Kinetic.Rect({
x: pos.x,
y: pos.y,
width: 10,
height: 10,
});
layer.add(rect);
layer.draw();
})
Thanks.
Source: (StackOverflow)
I am currently working a project involving KineticJS.
I have to dynamically create and delete shapes constantly, but cannot seem to figure out how to do the latter. I've been trying to do:
$ myLayer.remove(myShape)
because this is what most posts seem to recommend. But, the documentation says that this will remove the layer from the stage and not the shape from the layer. When I try this in project it in fact removes the layer from the stage.
So am I doing something wrong or is there some other way to remove a shape from a layer?
Source: (StackOverflow)
What is the difference between KineticJS methods layer.draw()
, layer.drawScene()
and layer.drawHit()
?
Source: (StackOverflow)
I am trying to combine drag and drop resize image and rotating image on touch, and mine is behaving strange http://jsfiddle.net/littlechad/Kuaxn/
My code is as follows:
function update(activeAnchor) {
var group = activeAnchor.getParent();
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var image = group.get('.image')[0];
var stage = group.getStage();
var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();
// update anchor positions
switch (activeAnchor.getName()) {
case 'topLeft':
topRight.setY(anchorY);
bottomLeft.setX(anchorX);
break;
case 'topRight':
topLeft.setY(anchorY);
bottomRight.setX(anchorX);
break;
case 'bottomRight':
bottomLeft.setY(anchorY);
topRight.setX(anchorX);
break;
case 'bottomLeft':
bottomRight.setY(anchorY);
topLeft.setX(anchorX);
break;
}
image.setPosition(topLeft.getPosition());
var height = bottomLeft.attrs.y - topLeft.attrs.y;
var width = image.getWidth()*height/image.getHeight();
topRight.attrs.x = topLeft.attrs.x + width
topRight.attrs.y = topLeft.attrs.y;
bottomRight.attrs.x = topLeft.attrs.x + width;
bottomRight.attrs.y = topLeft.attrs.y + height;
if(width && height) {
image.setSize(width, height);
}
}
function rotate(activeAnchor){
var group = activeAnchor.getParent();
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var image = group.get('.image')[0];
var stage = group.getStage();
var pos = stage.getMousePosition();
var xd = 150 - pos.x ;
var yd = 150 - pos.y ;
var theta = Math.atan2(yd, xd);
var degree = theta / (Math.PI / 180) - 45;
var height = bottomLeft.attrs.y - topLeft.attrs.y;
var width = image.getWidth()*height/image.getHeight();
console.log(degree);
console.log(width);
console.log(height);
image.setRotationDeg(degree);
return {
x: image.getAbsolutePosition().x,
y: image.getAbsolutePosition().y
}
}
function addAnchor(group, x, y, name) {
var stage = group.getStage();
var layer = group.getLayer();
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: 'transparent',
strokeWidth: 0,
radius: 20,
name: name,
draggable: false,
dragOnTop: false
});
if(name === 'topRight'){
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
radius: 20,
name: name,
draggable: true,
dragOnTop: false
});
}
anchor.on('dragmove', function() {
update(this);
rotate(this);
layer.draw();
});
anchor.on('mousedown touchstart', function() {
group.setDraggable(false);
this.moveToTop();
});
anchor.on('dragend', function() {
group.setDraggable(true);
layer.draw();
});
group.add(anchor);
}
function initStage() {
var stage = new Kinetic.Stage({
container: 'container',
width: 500,
height: 800
});
var imageGroup = new Kinetic.Group({
x: 150,
y: 150,
draggable: true,
});
var layer = new Kinetic.Layer({
width: 128,
height: 128,
offset: [64, 64]
});
layer.add(imageGroup);
var imgObj = new Image();
var imageInstance = new Kinetic.Image({
x: 0,
y: 0,
image: imgObj,
width: 200,
height: 138,
name: 'image',
});
imgObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
imageGroup.add(imageInstance);
addAnchor(imageGroup, 0, 0, 'topLeft');
addAnchor(imageGroup, 200, 0, 'topRight');
addAnchor(imageGroup, 200, 138, 'bottomRight');
addAnchor(imageGroup, 0, 138, 'bottomLeft');
imageGroup.on('dragstart', function() {
update(this);
rotate(this);
this.moveToTop();
});
stage.add(layer);
stage.draw();
}
function writeMessage(messageLayer, message) {
var context = messageLayer.getContext();
messageLayer.clear();
context.font = '18pt Calibri';
context.fillStyle = 'black';
context.fillText(message, 10, 25);
}
//loadImages(sources, initStage);
initStage();
It seems that updating the offset is the problem, I have tried several things to set the offset so that it stays in the middle, yet I still can't figure out how, I am really new to HTML5 and KineticJs, please help me on this.
UPDATE:
The above fiddle is no longer working due to the fillColor broken on new browsers, I have updated the fiddle to http://jsfiddle.net/Kuaxn/28/ i haven't been able to figure out the solution for this though.
thanks
Source: (StackOverflow)
I'm updating a div in a page using jquery mobile in order to play some animations.
The animations also play sound through use of document.createElement('audio');
My problem is, when I update the page to another animation, the old sound keeps playing.
Sorry, this is my first question and if it seems like im not phrasing it properly I apologise.
Here is my code..
Here is the code of the animation to be loaded into the #animation div
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="scripts/kinetic-v4.3.2.min.js"></script>
<script src="scripts/jquery-1.9.1.js"></script>
<script>
/*BABY SCENE*/
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 400
});
var babyLayer = new Kinetic.Layer();
var backLayer = new Kinetic.Layer();
var imageObj = new Image();
var backObj = new Image();
imageObj.onload = function() {
var baby = new Kinetic.Image({
x: stage.getWidth() / 2 -100 ,
y: stage.getHeight() / 2 -100,
image: imageObj,
width: 200,
height: 200,
opacity: 0.0
});
var background = new Kinetic.Image({
x: 0,
y: 0,
image: backObj,
width: 578,
height: 400,
opacity: 0.0
});
// add the shape to the layer
babyLayer.add(baby);
backLayer.add(background);
// add the layer to the stage
stage.add(babyLayer);
stage.add(backLayer);
babyLayer.moveToTop();
babyLayer.draw();
/*ANIMATION TIMELINE
*
* FIRST EVENT: FADE IN BABY
* SECOND EVENT: BABY TRANSITION
* THIRD EVENT: FADE IN BACKGROUND
*/
/*1) Fade in Baby*/
setTimeout(function() {
baby.transitionTo({
opacity: 1,
duration: 1
});
}, 200);
setTimeout(function() {
/*JQuery Baby Speech*/
$(document).ready(function() {
var babySpeech = document.createElement('audio');
babySpeech.setAttribute('src', '../Animations/sounds/babyspeech.mp3');
babySpeech.setAttribute('autoplay', 'autoplay');
//audioElement.load()
$.get();
babySpeech.addEventListener("load", function() {
babySpeech.play();
}, true);
});
}, 800);
/*2) Baby Transition*/
setTimeout(function() {
baby.transitionTo({
x: 140,
y: stage.getHeight() / 2 + 59,
width: 106,
height: 118,
opacity: 1,
duration: 3
});
}, 3000);
setTimeout(function() {
/*JQuery Baby Giggle*/
$(document).ready(function() {
var baby = document.createElement('audio');
baby.setAttribute('src', '../Animations/sounds/baby.mp3');
baby.setAttribute('autoplay', 'autoplay');
//audioElement.load()
$.get();
baby.addEventListener("load", function() {
baby.play();
}, true);
});
}, 3000);
/*3) Fade in Background*/
setTimeout(function() {
background.transitionTo({
opacity: 1,
duration: 3
});
}, 3200);
setTimeout(function() {
/*Second JQuery Baby Speech*/
$(document).ready(function() {
var babySpeech = document.createElement('audio');
babySpeech.setAttribute('src', '../Animations/sounds/babyspeech.mp3');
babySpeech.setAttribute('autoplay', 'autoplay');
//audioElement.load()
$.get();
babySpeech.addEventListener("load", function() {
babySpeech.play();
}, true);
});
}, 8700);
};
imageObj.src = '../Animations/images/baby.png';
backObj.src = '../Animations/images/background/babyroom.jpg';
</script>
Here is the main page code. It gets the filename of the animation page to be loaded into #animation from an array when you click the next button.
<div id="nav" data-role="content" style="width: 600px; margin: 0 auto; text-align: center; position: relative; top:450px">
<a rel='nofollow' href="#animation" data-role="button" data-inline="true" id ="back">Back</a>
<a rel='nofollow' href="#animation" data-role="button" data-inline="true" data-theme="b" id="next">Next</a>
<script>
var count=0;
var link_array = ['baby', 'bed', 'book', 'cat', 'chair', 'daddy', 'dog', 'mummy', 'shoe', 'teddy'];
$("#next").click(function(e) {
if(count!==9)
{
$('audio').stop();
count+=1;
}
$('#animation1wl').load('../Animations/' + link_array[count] + '.html');
$('#animation1wl').trigger('create');
});
$("#back").click(function(e) {
if(count !==0)
{
count-=1;
}
$('#animation1wl').load('../Animations/' + link_array[count] + '.html');
$('#animation1wl').trigger('create');
});
</script>
</div>
<div id="animation" data-role="content" style="width: 600px; margin: 0 auto; text-align: left">Problem Loading Resource LNW</div>
When I click the forward or back buttons, I want the audio from the previous animation loaded into #animation to stop playing..
Here is the code rendered inside #animation
<div id="animation1wl" data-role="content" style="width: 600px; margin: 0 auto; text-align: left" class="ui-content" role="main">
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
<div id="container"><div style="position: relative; display: inline-block; width: 578px; height: 400px;" class="kineticjs-content"><canvas width="578" style="width: 578px; height: 400px; position: absolute;" height="400"></canvas><canvas width="578" style="width: 578px; height: 400px; position: absolute;" height="400"></canvas></div></div>
<script src="scripts/kinetic-v4.3.2.min.js"></script>
<script src="scripts/jquery-1.9.1.js"></script>
<script>
/*BABY SCENE*/
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 400
});
var babyLayer = new Kinetic.Layer();
var backLayer = new Kinetic.Layer();
var imageObj = new Image();
var backObj = new Image();
imageObj.onload = function() {
var baby = new Kinetic.Image({
x: stage.getWidth() / 2 -100 ,
y: stage.getHeight() / 2 -100,
image: imageObj,
width: 200,
height: 200,
opacity: 0.0
});
var background = new Kinetic.Image({
x: 0,
y: 0,
image: backObj,
width: 578,
height: 400,
opacity: 0.0
});
// add the shape to the layer
babyLayer.add(baby);
backLayer.add(background);
// add the layer to the stage
stage.add(babyLayer);
stage.add(backLayer);
babyLayer.moveToTop();
babyLayer.draw();
/*ANIMATION TIMELINE
*
* FIRST EVENT: FADE IN BABY
* SECOND EVENT: BABY TRANSITION
* THIRD EVENT: FADE IN BACKGROUND
*/
/*1) Fade in Baby*/
setTimeout(function() {
baby.transitionTo({
opacity: 1,
duration: 1
});
}, 200);
setTimeout(function() {
/*JQuery Baby Speech*/
$(document).ready(function() {
var babySpeech = document.createElement('audio');
babySpeech.setAttribute('src', '../Animations/sounds/babyspeech.mp3');
babySpeech.setAttribute('autoplay', 'autoplay');
//audioElement.load()
$.get();
babySpeech.addEventListener("load", function() {
babySpeech.play();
}, true);
});
}, 800);
/*2) Baby Transition*/
setTimeout(function() {
baby.transitionTo({
x: 140,
y: stage.getHeight() / 2 + 59,
width: 106,
height: 118,
opacity: 1,
duration: 3
});
}, 3000);
setTimeout(function() {
/*JQuery Baby Giggle*/
$(document).ready(function() {
var baby = document.createElement('audio');
baby.setAttribute('src', '../Animations/sounds/baby.mp3');
baby.setAttribute('autoplay', 'autoplay');
//audioElement.load()
$.get();
baby.addEventListener("load", function() {
baby.play();
}, true);
});
}, 3000);
/*3) Fade in Background*/
setTimeout(function() {
background.transitionTo({
opacity: 1,
duration: 3
});
}, 3200);
setTimeout(function() {
/*Second JQuery Baby Speech*/
$(document).ready(function() {
var babySpeech = document.createElement('audio');
babySpeech.setAttribute('src', '../Animations/sounds/babyspeech.mp3');
babySpeech.setAttribute('autoplay', 'autoplay');
//audioElement.load()
$.get();
babySpeech.addEventListener("load", function() {
babySpeech.play();
}, true);
});
}, 8700);
console.log($('animation1wl').find('audio')[0]);
};
imageObj.src = '../Animations/images/baby.png';
backObj.src = '../Animations/images/background/babyroom.jpg';
</script>
</div>
Any help would be great..
Thanks very much.
Source: (StackOverflow)
I'm trying to build a transform manager for KineticJS that would build a bounding box and allow users to scale, move, and rotate an image on their canvas. I'm getting tripped up with the logic for the anchor points.
http://jsfiddle.net/mharrisn/whK2M/
I just want to allow a user to scale their image proportionally from any corner, and also rotate as the hold-drag an anchor point.
Can anyone help point me in the right direction?
Thank you!
Source: (StackOverflow)
I'm trying to set a simple zoom in/out functionality on a canvas. I'm using KineticJS to handle touch events and drawing in canvas, but couldn't implement the zoom. KinteicJS have a similar example, but they always zoom on the center while I'm intrested to zoom on the point between the fingers.
Thank you!
Source: (StackOverflow)
I am trying to choose the right technology to use for updating a project that basically renders thousands of points in a zoomable, pannable graph. The current implementation, using Protovis, is underperformant. Check it out here:
http://www.planethunters.org/classify
There are about 2000 points when fully zoomed out. Try using the handles on the bottom to zoom in a bit, and drag it to pan around. You will see that it is quite choppy and your CPU usage probably goes up to 100% on one core unless you have a really fast computer. Each change to the focus area calls a redraw to protovis which is pretty darn slow and is worse with more points drawn.
I would like to make some updates to the interface as well as change the underlying visualization technology to be more responsive with animation and interaction. From the following article, it seems like the choice is between another SVG-based library, or a canvas-based one:
http://www.sitepoint.com/how-to-choose-between-canvas-and-svg/
d3.js, which grew out of Protovis, is SVG-based and is supposed to be better at rendering animations. However, I'm dubious as to how much better and what its performance ceiling is. For that reason, I'm also considering a more complete overhaul using a canvas-based library like KineticJS. However, before I get too far into using one approach or another, I'd like to hear from someone who has done a similar web application with this much data and get their opinion.
The most important thing is performance, with a secondary focus on ease of adding other interaction features and programming the animation. There will probably be no more than 2000 points at once, with those small error bars on each one. Zooming in, out, and panning around need to be smooth. If the most recent SVG libraries are decent at this, then perhaps the ease of using d3 will outweigh the increased setup for KineticJS, etc. But if there is a huge performance advantage to using a canvas, especially for people with slower computers, then I would definitely prefer to go that way.
Example of app made by the NYTimes that uses SVG, but still animates acceptably smoothly:
http://www.nytimes.com/interactive/2012/05/17/business/dealbook/how-the-facebook-offering-compares.html . If I can get that performance and not have to write my own canvas drawing code, I would probably go for SVG.
I noticed that some users have used a hybrid of d3.js manipulation combined with canvas rendering. However, I can't find much documentation about this online or get in contact with the OP of that post. If anyone has any experience doing this kind of DOM-to-Canvas (demo, code) implementation, I would like to hear from you as well. It seems to be a good hybrid of being able to manipulate data and having custom control over how to render it (and therefore performance), but I'm wondering if having to load everything into the DOM is still going to slow things down.
I know that there are some existing questions that are similar to this one, but none of them exactly ask the same thing. Thanks for your help.
Follow-up: the implementation I ended up using is at https://github.com/zooniverse/LightCurves
Source: (StackOverflow)