Large SVG / Raphael circle distorts when being animated - javascript

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.

Related

fabric js - Resize complete canvas according to a rectangle added in a canvas in order to achieve cropping

I am using fabric js for generating templates, I want user to set height and width of a canvas for which I am using following code
canvas.setHeight(height);
canvas.setWidth(width);
However on doing this, the full width canvas stretches itself and the object disappears from the resized canvas as it was lets say 500 px wide before and user resized it to 100px then the items disappear.
I want to achieve a functionality so that if a user wants to change the size of canvas then instead of resizing the canvas I will show a rectangle and user can move rectangle accordingly to get the visible area and once user clicks on save then I will resize the canvas according to that rectangle, so that I can process the SVG later for further conversions but by doing so I am not sure how to get the visible area in that rectangle to the resized canvas, in short I want functionality like Darkroom js but for fabric js canvas object.
The current functionality is added to this JS fiddle http://jsfiddle.net/tbqrn/102/
Finally after doing some research I am able to do that, instead of resizing the canvas I am adding a rectangle object with "cropper" type as mentioned below
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'transparent',
width: 400,
height: 400,
strokeDashArray: [5, 5],
stroke: 'black',
type: 'cropper',
lockScalingX: true,
lockScalingY: true,
lockRotation: true
});
canvas.add(rect);
This rectangle will serve as the cropper, when a user submits the form then I am getting that cropper's dimensions as follow along with changing canvas size
var i;
var croppedLeft = 0;
var croppedTop = 0;
var canvasJson = canvas.getObjects();
// Cropping canvas according to cropper rectangle
if (canvas.getObjects().length > 0) {
var i;
for (i = 0; i < canvas.getObjects().length; i++) {
if (canvas.getObjects()[i].type == 'cropper') {
croppedLeft = canvas.getObjects()[i].left;
croppedTop = canvas.getObjects()[i].top;
canvas.setHeight(canvas.getObjects()[i].height);
canvas.setWidth(canvas.getObjects()[i].width);
canvas.getObjects()[i].remove();
}
}
}
And after that I am shifting all the other canvas objects accordingly as follow.
for (i = 0; i < canvasJson.length; i++) {
canvas.getObjects()[i].left = canvas.getObjects()[i].left - croppedLeft
canvas.getObjects()[i].top = canvas.getObjects()[i].top - croppedTop
canvas.renderAll();
}
And after doing that I am able to achieve the desired functionality, hope this helps someone.
Here is my fiddle http://jsfiddle.net/tbqrn/115/

Multiple objects in canvas using kinetic js

I am trying to do an application with canvas and which have multiple objects with events binded to them like, mouse down and mouse up e.t.c.,
I am using kinetic js.
Well My doubt is if I have to draw two circles on canvas using kinetic js. I can use objects directly. But if I have to place 500 circles in the division, Its too difficult to have 500 circle objects to create manually. So, is there any other alternative to implement this one?
I use these events for every circle object I use
var circle = new Kinetic.Circle({
x: 100,
y: 100,
radius: 5,
fill: 'red',
stroke: 'black',
strokeWidth: 1
});
circle.setAttr('fact','Kinetic JS is Awesome')
circle.on('mouseover', function() {
document.body.style.cursor = 'pointer'
});
circle.on('mouseout', function() {
document.body.style.cursor = 'default'
});
circle.on('mousedown', function() {
var fill = this.getFill() == 'red' ? '#00d00f' : 'red';
this.setFill(fill);
alert(this.getAttr('fact'));// I do some stuff here
layer.draw();
});
I recommend also that you check the tutorials of KineticJS, this one is about a stress test: 1000 shapes + their respective tooltips.
http://www.html5canvastutorials.com/labs/html5-canvas-10000-shape-stress-test-with-kineticjs
PS: You just have to copy paste the code and update the KineticJS version here.

KineticJS shape opacity

I need some assistance changing the opacity of a shape using KineticJS (5.0.0.).
In an mouse event, I want to change the opacity of the shape, which triggered the event. Whenever the shape is hovered, it gets visible ( opacity 1.0 ) and when it's left, it becomes invisible ( opacity 0.0 ). It works fine, whenever I redraw the whole Layer of the specified shape.
The point is, I can't redraw the whole Layer because it takes to much time ( ~300 shapes ). For that reason I changed some code, to just draw the shape.
jsFiddle:
http://jsfiddle.net/p39uH/2/ ( see lines 25 and 30 of HTML )
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var pentagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 5,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
opacity: 0.1
});
pentagon.on('mouseover', function() {
this.opacity(0.3);
this.draw(); // instead of layer.draw();
});
pentagon.on('mouseout', function() {
this.opacity(0.0);
this.draw(); // instead of layer.draw();
});
// add the shape to the layer
layer.add(pentagon);
// add the layer to the stage
stage.add(layer);
( Code is based on this: http://www.html5canvastutorials.com/kineticjs/html5-canvas-set-alpha-with-kineticjs/ )
Even though I set the opacity of the shape to 0.0 when left, it's still visible as you can see. Every time it is hovered, it becomes more and more visible ( I guess the shape gets redrawn ).
Is there any way to (re)draw the shape with an opacity of 0.0 WITHOUT drawing the whole stage and/or layer ?
Thanks in advance.
Yes, a quick look indicates node.draw() might be broken in 5.0.1.
Workarounds:
Drop back to version 4.4.0
Use layer.drawScene() which saves redraw time by not redrawing the hit-canvas.

Donut / Radial Chart with Raphael

Im a beginner to Raphael. Can anyone show me how I can do a donut/radial chart, with animation, similar to these.
http://dribbble.com/shots/670348-Segment-Graphs
Im working at it now. So far Ive got this far. I will update as I make progress. My sumbling block right now is animating a change in color for the outer ring.
window.onload = function () {
// Creates canvas 320 × 200 at 10, 50
var paper = Raphael(10, 50, 320, 200);
// Creates circle at x = 50, y = 40, with radius 10
var circle1 = paper.circle(50, 40, 40);
var circle2 = paper.circle(50, 40, 20);
circle2.attr("fill", "#fff");
circle2.attr("stroke", "#fff");
circle1.attr("fill", "#336699");
circle1.attr("stroke", "#fff");
}
Credits:
On the raphael website there is an example that uses arcs. There is another question on stackoverflow with a similar topic: drawing centered arcs in raphael js. The accepted answer there has a simplified and commented version of the most important parts of the code, plus there is a jsfiddle link showing the code in action: http://jsfiddle.net/Bzdnm/2/
So what I did: I took the code from the linked question, combined it with eve, another javascript library made by the creator of RaphaelJS and what I got was this: http://jsfiddle.net/cristighenea/aP7MK/
At a glance:
1.after the arc is created we rotate it 180 degrees and begin animating it:
theArc.rotate(180, 100, 100).animate({
arc: [100, 100, amount, 100, 40]
}, 1900, function(){
//animation finish callback goes here
});
2.using eve we bind an event to *raphael.anim.frame.**
3.each time the event is fired we update the text in the middle with the new value of the arc
If you have any questions let me know

Zoom and Pan in KineticJS

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!
You can simply add .setDraggable("draggable") to a layer and you will be able to drag it as long as there is an object under the cursor. You could add a large, transparent rect to make everything draggable. The zoom can be achieved by setting the scale of the layer. In this example I'm controlling it though the mousewheel, but it's simply a function where you pass the amount you want to zoom (positive to zoom in, negative to zoom out). Here is the code:
var stage = new Kinetic.Stage({
container: "canvas",
width: 500,
height: 500
});
var draggableLayer = new Kinetic.Layer();
draggableLayer.setDraggable("draggable");
//a large transparent background to make everything draggable
var background = new Kinetic.Rect({
x: -1000,
y: -1000,
width: 2000,
height: 2000,
fill: "#000000",
opacity: 0
});
draggableLayer.add(background);
//don't mind this, just to create fake elements
var addCircle = function(x, y, r){
draggableLayer.add(new Kinetic.Circle({
x: x*700,
y: y*700,
radius: r*20,
fill: "rgb("+ parseInt(255*r) +",0,0)"
})
);
}
var circles = 300
while (circles) {
addCircle(Math.random(),Math.random(), Math.random())
circles--;
}
var zoom = function(e) {
var zoomAmount = e.wheelDeltaY*0.001;
draggableLayer.setScale(draggableLayer.getScale().x+zoomAmount)
draggableLayer.draw();
}
document.addEventListener("mousewheel", zoom, false)
stage.add(draggableLayer)
http://jsfiddle.net/zAUYd/
Here's a very quick and simple implementation of zooming and panning a layer. If you had more layers which would need to pan and zoom at the same time, I would suggest grouping them and then applying the on("click")s to that group to get the same effect.
http://jsfiddle.net/renyn/56/
If it's not obvious, the light blue squares in the top left are clicked to zoom in and out, and the pink squares in the bottom left are clicked to pan left and right.
Edit: As a note, this could of course be changed to support "mousedown" or other events, and I don't see why the transformations couldn't be implemented as Kinetic.Animations to make them smoother.
this is what i have done so far.. hope it will help you.
http://jsfiddle.net/v1r00z/ZJE7w/
I actually wrote kineticjs-viewport. I'm happy to hear you were interested in it.
It is actually intended for more than merely dragging. It also allows zooming and performance-focused clipping. The things outside of the clip region aren't rendered at all, so you can have great rendering performance even if you have an enormous layer with a ton of objects.
That's the use case I had. For example, a large RTS map which you view via a smaller viewport region -- think Starcraft.
I hope this helps.
As I was working with Kinetic today I found a SO question that might interest you.
I know it would be better as a comment, but I don't have enough rep for that, anyway, I hope that helps.
These answers seems not to work with the KineticJS 5.1.0. These do not work mainly for the signature change of the scale function:
stage.setScale(newscale); --> stage.setScale({x:newscale,y:newscale});
However, the following solution seems to work with the KineticJS 5.1.0:
JSFiddle: http://jsfiddle.net/rpaul/ckwu7u86/3/
Unfortunately, setting state or layer draggable prevents objects not draggable.
Duopixel's zooming solution is good, but I would rather set it for stage level, not layer level.
Her is my solution
var stage = new Kinetic.Stage({
container : 'container',
width: $("#container").width(),
height: $("#container").height(),
});
var layer = new Kinetic.Layer();
//layer.setDraggable("draggable");
var center = { x:stage.getWidth() / 2, y: stage.getHeight() / 2};
var circle = new Kinetic.Circle({
x: center.x-100,
y: center.y,
radius: 50,
fill: 'green',
draggable: true
});
layer.add(circle);
layer.add(circle.clone({x: center.x+100}));
// zoom by scrollong
document.getElementById("container").addEventListener("mousewheel", function(e) {
var zoomAmount = e.wheelDeltaY*0.0001;
stage.setScale(stage.getScale().x+zoomAmount)
stage.draw();
e.preventDefault();
}, false)
// pan by mouse dragging on stage
stage.on("dragstart dragmove", function(e) {window.draggingNode = true;});
stage.on("dragend", function(e) { window.draggingNode = false;});
$("#container").on("mousedown", function(e) {
if (window.draggingNode) return false;
if (e.which==1) {
window.draggingStart = {x: e.pageX, y: e.pageY, stageX: stage.getX(), stageY: stage.getY()};
window.draggingStage = true;
}
});
$("#container").on("mousemove", function(e) {
if (window.draggingNode || !window.draggingStage) return false;
stage.setX(window.draggingStart.stageX+(e.pageX-window.draggingStart.x));
stage.setY(window.draggingStart.stageY+(e.pageY-window.draggingStart.y));
stage.draw();
});
$("#container").on("mouseup", function(e) { window.draggingStage = false } );
stage.add(layer);
http://jsfiddle.net/bighostkim/jsqJ2/

Categories

Resources