Rotate objects as a group in Raphael - javascript

I'm new to Raphael and I'm trying to do two circles move in circular orbits related to the center of the canvas.
Here I made ellipses to illustrate the case. The black point is the center of the canvas.
http://jsfiddle.net/QCSb9/
I used set() to group the circles but when trying to rotate them, they rotate using their own centers individually, I thought that grouping the circles the new geometry of the group would change, becoming the black point the center of the group.
How can I rotate these circles continuously as one object.
Here is the code I'm using:
$().ready(function(){
var paper = Raphael("canvas", 640, 480);
paper.rect(0, 0, 640, 480, 10).attr({fill: "#fff", stroke: "none"});
paper.circle(320, 240, 1).attr({"fill":"#000000","stroke-width":0});
var circles = paper.set();
circles.push(
paper.ellipse(200, 240, 30, 25),
paper.ellipse(440, 240, 30, 25)
);
circles.attr({"fill":"#e00000","stroke-width":0});
var anim = Raphael.animation({"transform":"r360"},2000);
circles.animate(anim.repeat(Infinity));
});

Solved: It was a matter of specifying the center of rotation to "r360,320,240":
var anim = Raphael.animation({"transform":"r360,320,240"},2000);
rotate() is deprecated but explains the parameters admitted:
http://raphaeljs.com/reference.html#Element.rotate
Here the example corrected on jsfiddle.net
http://jsfiddle.net/SW3sP/1/

Related

`line()` in p5js crosses the edges. Beside doing the math myself, is there an easier way to make the line just connect their edges?

I'm trying to draw a line to connect two given circles.
function setup() {
createCanvas(300, 100);
background(220);
noFill();
ellipse(150, 30, 20, 20);
ellipse(100, 50, 20, 20);
line(100, 50, 150, 30);
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.min.js"></script>
The parameters I get are the x, y of the circle's center. If I use the info directly, the line crosses both circles.
I know I can do the math, I'd just like to know if there is an easier way to make the line just connect their edges?
One easy way would be draw the line first then draw the circles and fill them with the background color this way the line inside the circles will be hidden, this only work if you don't mind the background and the circles color to be the same
function setup() {
createCanvas(300, 100);
background(220);
line(100, 50, 150, 30);
fill(220);
ellipse(150, 30, 20, 20);
ellipse(100, 50, 20, 20);
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.min.js"></script>
Using opaque circles
A solution similar to what Sarkar said before, as far as you don't mind the circles having a fill color (whether their color is the same or different to the background color, it doesn't matter), the easiest way of doing this is by simply making the circles cover the line by drawing them afterwards with any fill opaque color.
Using a graphics object
However, if you would like to have this shape as a transparent shape, in order to have more freedom with the use you intend to do of it, you could try this: you create a graphics object, you draw the line, then you activate the erase mode and draw the circles so they erase the part of the line they are overlapping, then you exit the erase mode and draw normally the unfilled circles. Once you have finished with your graphic, you use the image function to draw it over the canvas.
let graphic;
function setup() {
createCanvas(300, 100);
graphic = createGraphics(width, height);
graphic.line(100, 50, 150, 30);
graphic.erase();
graphic.ellipse(150, 30, 20, 20);
graphic.ellipse(100, 50, 20, 20);
graphic.noErase();
graphic.noFill();
graphic.ellipse(150, 30, 20, 20);
graphic.ellipse(100, 50, 20, 20);
background(220);
image(graphic,0,0);
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.min.js"></script>

Regular polygon with different height and width

I want to create a polygon with Kinetic.js and I know the width, height, rotation and number of points for the polygon.
I thought this would be possible by using the RegularPolygon object, but for it I have to set a value for radius. A triangle would be created like this:
var hexagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 3,
radius: 70,
fill: 'red',
});
See a similar polygon being created here:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-regular-polygon-tutorial/
The result would look something like this:
But what if I want to create a triangle of which the width should be twice the height? Looking something like this:
As far as I understand, this not possible by just adjusting the radius.
How can I achieve this for any polygon? Note that I don't know the values for the points to begin with (they could be calculated though). I think that scaleX and scaleY might be possible to use, but is it possible to achieve it in an easier way? I would like just to set width and height directly.
KineticJS polygons are regular polygons (all sides are equal length).
Scaling a regular-polygon is awkward if you want to stroke the polygon because the stroke is also scaled and therefore the stroke deforms.
So your best solution might be to just draw a poly-line forming your triangles.
Here's example code and a Demo:
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
var PI=Math.PI;
var PI2=PI*2;
scaledRegularPolygon(100,100,30,5,2,1,'red');
function scaledRegularPolygon(cx,cy,radius,sides,scaleX,scaleY,fill){
var points=[];
for(var i=0;i<sides;i++){
var sweep=PI2/sides;
var midbottom=PI/2;
var rightbottom=midbottom-sweep/2;
var start=rightbottom-sweep;
var angle=start+sweep*i;
var x=cx+radius*Math.cos(angle);
var y=cy+radius*Math.sin(angle);
x=cx+(x-cx)*scaleX;
y=cy+(y-cy)*scaleY;
points.push(x,y);
}
var poly=new Kinetic.Line({
points:points,
closed:true,
fill:fill,
stroke: 'black',
strokeWidth:4
});
layer.add(poly);
layer.draw();
}
body{padding:20px;}
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:350px;
height:350px;
}
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v5.1.0.min.js"></script>
<div id="container"></div>
Kinetic.Transform allows us to calculate the position of a point from a transform matrix by passing in the point to Kinetic.Transform.point(). From the object you want to transform the points for, get its transform matrix with Kinetic.Node.getAbsoluteTransform().copy() (or Kinetic.Node.getTransform().copy(), whatever seems suitable for your purposes). Then we call Kinetic.Transform.scale(2,2) on the transform matrix to get a matrix with twice the scale. Then for each point, use Kinetic.Transform.point() to get its new position.

KineticJS: How can I include custom canvas shapes in a toDataURL function

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/

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

Large SVG / Raphael circle distorts when being animated

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.

Categories

Resources