Rotating object with Matrix.rotate in snap.svg morphs the object - javascript

I'm trying to use snap.svg library to rotate and move my object but I am getting this weird behavior where the object gets morphed.
http://codepen.io/anon/pen/vOwRga
var s = Snap.select("#svg");
var rect1 = s.select("#rect1");
var rect2 = s.select("#rect2");
var rotateMatrix = new Snap.Matrix();
rotateMatrix.rotate(180,302,495);
this.spin = function() {
rect1.animate({transform:'r180,302,160'},5000,function(){
rect1.animate({transform:'r0,302,160'},5000);
});
rect2.animate({transform: rotateMatrix},5000,function(){
rect2.animate({transform: rotateMatrix.invert},5000);
});
}
So here I'm rotating two different rectangles. First one works fine and the second one is where i try to use matrix.
What am I doing wrong? Why is this happening? what is the meaning of life?

In the first one, you are animating a rotation. Snap knows it is a rotation because of the way you have defined it with Snap's special 'r' initialiser.
In the second one you are just passing a matrix that describes a transform from one orientation to another. Snap, and the browser, have no idea that it represents a rotation. So all it is doing is interpolating the values in the matrix.

Related

Two pens on one canvas in one loop?

This code creates a line that has been torn between the two pens like if you were trying to write on paper while I am grabbing your pen and make you write something I want.
var pen=[];
pen[0]=$('#canvas')[0].getContext("2d");
pen[1]=$('#canvas')[0].getContext("2d");
pen[0].beginPath();
pen[1].beginPath();
var y=[[],[]]; // two arrays of pixel values, numbers between 0-100
var x=0;
var i=y[0].length; // the arrays are both the same length
pen[0].moveTo(x,y[0][y[0].length-1]);
pen[1].moveTo(x,y[1][y[1].length-1]);
while(i--){
x+=2;
pen[0].lineTo(x,y[0][i]);
pen[1].lineTo(x,y[1][i]);
}
pen[0].stroke();
pen[1].stroke();
Or best said; if the first array were only the number 100 repeated and the second array was only 0 repeated. I would expect to see two lines being drawn in parallel, but instead I get a zigzag.
Is there a way I can use one canvas and one loop only and get the two separate lines?
There's only one context per canvas - I just tested it in Chrome 39:
var c = document.createElement('canvas');
var x1 = c.getContext('2d');
var x2 = c.getContext('2d');
x1 === x2; // true
Whilst it could be done in one loop if you do a separate moveTo and lineTo for each line segment within the loop (i.e. `moveTo previousPoint, lineTo thisPoint) you'll find that you need to use two loops for best results.
The reason is that Canvas knows how to cleanly join adjacent segments of a line, but only if they're part of a single path. If you create a new path for each segment the lines won't join up properly.
I think the issue is that $('#canvas')[0].getContext("2d") is only going to return a pointer to the canvas context object, so really it's the same object when you get it for pen 0 and pen 1.
That's probably why you get the zigzag - it's drawing a line from the last drawn pixel on that Context to your new position.
To draw two lines, you'd have to use moveTo(x,y) to move the pen (pointer for the context) without drawing when swapping from one line to the other. Because there's only one context object, I don't think having two pens will work I'm afraid.

EaselJS, Matrix2D and wrap Image

I did this post asking your opinion about what JS library is better, or can do the work
that I have shown. Since I'm not allowed to do that here I did a research and tried out EaselJS to do the work. So my question now have changed.
I have this piece of code:
function handleImageLoad(event) {
var img = event.target
bmp = new createjs.Bitmap(img);
/*Matrix2D Transformation */
var a = 0.880114;
var b = 0.0679298;
var c = -0.053145;
var d = 0.954348;
var tx = 37.4898;
var ty = -16.5202;
var matrix = new createjs.Matrix2D(a, b, c, d, tx, ty);
var polygon = new createjs.Shape();
polygon.graphics.beginStroke("blue");
polygon.graphics.beginBitmapFill(img, "no-repeat", matrix).moveTo(37.49, -16.52).lineTo(336.27, -36.20).lineTo(350.96, 171.30).lineTo(50.73, 169.54).lineTo(37.49, -16.52);
stage.addChild(polygon);
stage.update();
}
where the variables a,b,c,tx and ty are values from a Homography matrix,
0.880114 0.067979298 37.4898
-0.053145 0.954348 -16.5202
-0.000344 1.0525-006 1
As you can see in attached files, I draw well a deformed rectangle but the image still doesn´t wrap the shape created. Anyone know how can I do it? There is a way better do to this? I'm doing something wrong?
Thanks for your time.
Edit: To be more specific I have added other image to see what I want.
You are attempting to do something similar to a perspective transform, using a 3x3 matrix.
Canvas's 2D context, and by extension EaselJS, only supports affine transformations with a 2x3 matrix - transformations where the opposite edges of the bounding rectangle remain parallel. For example, scaling, rotation, skewing, and translation.
http://en.wikipedia.org/wiki/Affine_transformation
You might be able to fake this with multiple objects that have been skewed (this was used extensively in Flash to fake perspective transforms), or you may have to look into another solution.

Fabric JS Groups not resizing

I hope you can help. I'm having some trouble programmatically resizing a Group of Fabric JS objects. I add the objects to the canvas using canvas.add(object) and then add them to the group. However when trying to resize the objects as a group, the usual setHeight(), scale() and scaleToWidth/Height() methods seem to have no effect.
My code is in several places, but the essence is:
var obj = ........ // I Get my obj from an SVG path
var objectGroup = new fabric.Group();
canvas.clipTo = function(ctx) {
obj.render(ctx);
}
objectGroup.add(obj);
I also tried objectGroup.addWithUpdate(obj);
Then, after adding more objects to the group; in my resize event, I work out the scale I need and do:
objectGroup.scaleToWidth(newX);
objectGroup.scaleToHeight(newY);
objectGroup.setCoords();
canvas.renderAll();
Am I missing something?? Do I have to add my obj to the canvas as well as rendering it inside the clipTo function?
Thanks in advance :)

How can I make Raphael.js elements "wiggle" on the canvas?

I'm working on a project that uses SVG with Raphael.js. One component is a group of circles, each of which "wiggles" around randomly - that is, slowly moves along the x and y axes a small amount, and in random directions. Think of it like putting a marble on your palm and shaking your palm around slowly.
Is anyone aware of a Raphael.js plugin or code example that already accomplishes something like this? I'm not terribly particular about the effect - it just needs to be subtle/smooth and continuous.
If I need to create something on my own, do you have any suggestions for how I might go about it? My initial idea is along these lines:
Draw a circle on the canvas.
Start a loop that:
Randomly finds x and y coordinates within some circular boundary anchored on the circle's center point.
Animates the circle from its current location to those coordinates over a random time interval, using in/out easing to smooth the effect.
My concern is that this might look too mechanical - i.e., I assume it will look more like the circle is tracing a star pattern, or having a a seizure, or something like that. Ideally it would curve smoothly through the random points that it generates, but that seems far more complex.
If you can recommend any other code (preferably JavaScript) that I could adapt, that would be great too - e.g., a jQuery plugin or the like. I found one named jquery-wiggle, but that seems to only work along one axis.
Thanks in advance for any advice!
Something like the following could do it:
var paper = Raphael('canvas', 300, 300);
var circle_count = 40;
var wbound = 10; // how far an element can wiggle.
var circleholder = paper.set();
function rdm(from, to){
return Math.floor(Math.random() * (to - from + 1) + from);
}
// add a wiggle method to elements
Raphael.el.wiggle = function() {
var newcx = this.attrs.origCx + rdm(-wbound, wbound);
var newcy = this.attrs.origCy + rdm(-wbound, wbound);
this.animate({cx: newcx, cy: newcy}, 500, '<');
}
// draw our circles
// hackish: setting circle.attrs.origCx
for (var i=0;i<circle_count;i++) {
var cx = rdm(0, 280);
var cy = rdm(0, 280);
var rad = rdm(0, 15);
var circle = paper.circle(cx, cy, rad);
circle.attrs.origCx = cx;
circle.attrs.origCy = cy;
circleholder.push(circle);
}
// loop over all circles and wiggle
function wiggleall() {
for (var i=0;i<circleholder.length;i++) {
circleholder[i].wiggle();
}
}
// call wiggleAll every second
setInterval(function() {wiggleall()}, 1000);
http://jsfiddle.net/UDWW6/1/
Changing the easing, and delays between certain things happening should at least help in making things look a little more natural. Hope that helps.
You can accomplish a similar effect by extending Raphael's default easing formulas:
Raphael.easing_formulas["wiggle"] = function(n) { return Math.random() * 5 };
[shape].animate({transform:"T1,1"}, 500, "wiggle", function(e) {
this.transform("T0,0");
});
Easing functions take a ratio of time elapsed to total time and manipulate it. The returned value is applied to the properties being animated.
This easing function ignores n and returns a random value. You can create any wiggle you like by playing with the return formula.
A callback function is necessary if you want the shape to end up back where it began, since applying a transformation that does not move the shape does not produce an animation. You'll probably have to alter the transformation values.
Hope this is useful!
There is a very good set of easing effects available in Raphael.
Here's a random set of circles that are "given" bounce easing.
Dynamically add animation to objects
The full range of easing effects can be found here. You can play around with them and reference the latest documentation at the same time.
Putting calls in a loop is not the thing to do, though. Use callbacks, which are readily available.

Raphael 2.0 - how to correctly set the rotation point

I don't understand how to rotate my object at a certain point. This is the javascript I have
// create needle
var rsr = Raphael('rsr', '320', '240');
var needle = rsr.path("m 156.74443,870.84631 -2.26177,119.38851
4.38851,0 z"); needle.attr({id: 'needle',parent: 'layer1',fill:
'#ff6600',stroke: '#000000',"stroke-width": '0.61',"stroke-linecap":
'butt',"stroke-linejoin": 'miter',"stroke-miterlimit": '4',"stroke-
opacity": '1',"stroke-dasharray": 'none'});
needle.rotate(0);
needle.transform("t0,-812.36218").data('id', 'needle');
// get needle bounding box
var needleBox = needle.getBBox();
// calculate rotation point (bottom middle)
var x_rotate_point = needleBox.x + (needleBox.width/2);
var y_rotate_point = needleBox.y + needleBox.height;
// rotate needle
needle.attr({rotation: 0}).animate({transform:"r45,"+x_rotate_point
+","+y_rotate_point}, 6000);
// Creates circle at rotation point
var circle = rsr.circle(x_rotate_point, y_rotate_point, 10);
circle.attr("fill", "#f00");
circle.attr("stroke", "#fff");
I have created a dummy circle to check if my coordinates of my center point is correct
and it is, but it doesn't rotate around that point :-/
When I created gauges in different frameworks that always seemed the way to go, but that logic doesn't seem to translate well into Raphael 2.0.
I did google for it and found some entries but the problem seems it is for
older versions which doesn't translate well because a lot of
stuff got changed or is deprecated.
You are setting the center of rotation correctly, but there are some other things going on which are causing some confusion. I was able to get this to work by changing the animation target to:
{transform: needle.attr("transform") + "R45,"+x_rotate_point+","+y_rotate_point}
I think the way you had it, the animation was gradually removing the previous translation while also doing the rotation. This addition allows it to accumulate the transformations. Also, note that I had to switch the 'r' to 'R'. It is not really clear to me what the small 'r' is doing in this example.
Anyway, here is a working demo.
Btw, I also commented out a few rotations that didn't seem to be doing anything.

Categories