Horizontal flip not working after vertical flip - css-transforms

I am using the two classes two flip vertically and horizontally. Whichever one I do first works fine. However, If for example I flip vertically it works fine, but if I then try to flip horizontally it doesn't flip. Any ideas why this is?
.flippedhorizontal {
-moz-transform: scaleX(-1);
-o-transform: scaleX(-1);
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
filter: FlipH;
-ms-filter: "FlipH";
}
.flippedvertical {
-moz-transform: scaleY(-1);
-o-transform: scaleY(-1);
-webkit-transform: scaleY(-1);
transform: scaleY(-1);
filter: FlipV;
-ms-filter: "FlipV";
}

Check your dev toolbar to see which one of the declarations is used, the others will be shown crossed out. By adding the second class, you are asking it to either keep or replace the current declaration. In this case, the default behavior seems to be keeping the original value.
I have omitted prefixes for brevity, here is what I think you want:
// START WITH HORIZONTAL
transform: scaleX(-1);
// ADD VERTICAL
transform: scaleX(-1) scaleY(-1);
and the reverse:
// START WITH VERTICAL
transform: scaleY(-1);
// ADD HORIZONTAL
transform: scaleY(-1) scaleX(-1);
// putting X first ^here would be same in this case... *I think...
But to make it easy, lets put in the implicit transforms:
// START WITH IDENTITY TRANSFORM
transform: scaleX(1) scaleY(1)
// ADD HORIZONTAL
transform: scaleX(-1) scaleY(1);
// ADD VERTICAL
transform: scaleX(-1) scaleY(-1);
To solve this, you will need to control the transform itself, rather than the classes. This is relatively simple:
var el, scaleX, scaleY;
function setTransform (element, scaleXArg, scaleYArg) {
var scaleString = ("scaleX(" + scaleXArg + ") scaleY(" + scaleYArg + ")");
// now attach that variable to each prefixed style
element.style.webkitTransform = scaleString;
element.style.MozTransform = scaleString;
element.style.msTransform = scaleString;
element.style.OTransform = scaleString;
element.style.transform = scaleString;
}
el = document.getElementById("elementYouWantToUse");
scaleX = "1";
scaleY = "1";
// call setTransform to initialize
setTransform(el, scaleX, scaleY);
// to finish you can choose to call setTransform with new arguments:
setTransform(el, -1, 1);
setTransform(el, 1, -1);
setTransform(el, -1, -1);
setTransform(el, 1, 1);
The caveat is if you use the scaleX and scaleY variables, you can get away with only testing and changing one value in your event handler, rather than both as required when calling directly with values. Here is the logic for the simple test (replace scaleX with scaleY as needed):
if ( scaleX === "1" ) {
scaleX = "-1";
setTransform(el, scaleX, scaleY);
} else {
scaleX = "1";
setTransform(el, scaleX, scaleY);
}

An element can not have 2 different values for the same property. This is already in Thisiate answer, and it is absolutely correct.
In your case, transform can not have at the same time a scaleX and a scaleY, even if they come from 2 different classes.
The easiest way to solve your case is to create a new style for the combined classes (when the element has at the same time the 2 classes)
function flipX() {
$('#test').toggleClass("flipX");
}
function flipY() {
$('#test').toggleClass("flipY");
}
#test {
border: solid 1px blue;
font-size: 80px;
display: inline-block;
transition: all 1s;
}
.flipX {
transform: scaleX(-1);
}
.flipY {
transform: scaleY(-1);
}
.flipX.flipY {
transform: scale(-1, -1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="checkbox" onclick="flipX()">flip X</input>
<input type="checkbox" onclick="flipY()">flip Y</input>
<div id="test">TEST</div>

Related

Continuous Line Rendering on ChartJS

I wanted to show a vertical line that follows my mouse on my chartjs canvas and the following codes does what I one except one thing, it will stop updating once the tooltips fades out (when there is no data intersects with my cursor). I know it has something to do with rendering but I do not know how and what variable I have to manipulate with.
I can force the animation playing non-stop by setting my animation as timed loop but I don't think this is a proper solution. This will also consume more resources since I need only my lines to be updating constantly not the whole graph.
The function is implemented with inline plugin.
Please have a look, thank you!
let canvas = document.getElementById('myChart')
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
};
}
let mousePosX;
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
mousePosX = mousePos.x;
}, false);
const config = {
type: 'scatter',
plugins: [
{
afterDraw(chart) {
let x = mousePosX;
let yAxis = chart.scales.y;
let ctx = chart.ctx;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, yAxis.top);
ctx.lineTo(x, yAxis.bottom);
ctx.lineWidth = 1;
ctx.strokeStyle = 'rgba(0, 0, 255, 0.4)';
ctx.stroke();
ctx.restore();
},
}
],
.....

Cannot pitch canvas layer correctly in Mapbox GL JS

I am moving some code from Leaflet into Mapbox GL JS. I have a 2d animated canvas which is rendered correctly when viewed top-down (pitch 0), rotated (bearing whatever), but is skewed when the pitch isn't zero. I have spent the past few days trying to correct it without much success.
My code needs to be able to convert between screen dimensions and map coordinates. E.g. for a given pixel, what lat-lon results (with map.project([lon, lat]) ). Or for a lat-lon, what pixel does it correspond to (with map.unproject([x,y]) ). When the canvas is rendered with pitch 0, the latitudes are equally spaced (*enough) across the pixels and everything works as desired. The conversions occur correctly.
When pitch is > 0, the coordinate conversions appear to work as expected as well. However, I think Mapbox GL JS takes the canvas, and turns it into a WebGL texture. This texture is then somehow scaled/rotated which turns the previous rectangle canvas into a trapezoid.
I need to be able to include this trapezoid scaling or account for this distortion in the canvas calculations.
Rough Sample
The snippet below is an attempt at replicating what is going on with a far simpler codebase. It's based on https://docs.mapbox.com/mapbox-gl-js/example/canvas-source/. You'll see the circles moving around, turning into ovals at different points and stretching--they should remain circles. This is partly caused by the map.unproject() calls using the screen dimensions, which are used to determine the map bounds. In 2d space, this works well because the screen bounds are identical to the camera view. But I think in 3d space, the screen dimensions are distorted by the camera view, and so the relation is lost between screen--camera--map which causes the skew.
My goal is to either correct the skew, or somehow use the camera properties to create the bounds (and project/unproject), OR there's another way to solve this. Essentially I want what is on the canvas at particular coordinates to stay there and not moved when pitched.
Thank you!
const PITCH = 45; // Change to 0 to see uniform shapes
// Based on https://docs.mapbox.com/mapbox-gl-js/example/canvas-source/
mapboxgl.accessToken = 'pk.eyJ1IjoiYnVzaGZpcmVpbyIsImEiOiJjazU3NGwzNGEwM2QxM2VwcWJ0djk1amFyIn0.DP3giqn33iswMWzlclr9jg';
var map = new mapboxgl.Map({
container: 'map',
zoom: 5,
minZoom: 4,
center: [95.899147, 18.088694],
pitch: PITCH,
style: 'mapbox://styles/mapbox/streets-v11'
});
//Animation from https://javascript.tutorials24x7.com/blog/how-to-draw-animated-circles-in-html5-canvas
var canvas = document.getElementById('canvasID');
var ctx = canvas.getContext('2d');
var circles = [];
var radius = 20;
function Circle(x, y, dx, dy, radius, color) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
ctx.strokeStyle = color;
ctx.stroke();
};
this.update = function () {
if (this.x + this.radius > 400 || this.x - this.radius < 0) {
this.dx = -this.dx;
}
if (this.y + this.radius > 400 || this.y - this.radius < 0) {
this.dy = -this.dy;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
};
}
for (var i = 0; i < 5; i++) {
var color =
'#' +
(0x1000000 + Math.random() * 0xffffff).toString(16).substr(1, 6);
var x = Math.random() * (400 - radius * 2) + radius;
var y = Math.random() * (400 - radius * 2) + radius;
var dx = (Math.random() - 0.5) * 2;
var dy = (Math.random() - 0.5) * 2;
circles.push(new Circle(x, y, dx, dy, radius, color));
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, 400, 400);
for (var r = 0; r < 5; r++) {
circles[r].update();
}
}
animate();
let size = this.map.getContainer();
const nw = map.unproject([0, 0]);
const ne = map.unproject([size.clientWidth, 0]);
const se = map.unproject([size.clientWidth, size.clientHeight]);
const sw = map.unproject([0, size.clientHeight]);
map.on('load', function () {
map.addSource('canvas-source', {
type: 'canvas',
canvas: 'canvasID',
coordinates: [
[nw.lng, nw.lat],
[ne.lng, ne.lat],
[se.lng, se.lat],
[sw.lng, sw.lat],
],
animate: true
});
map.addLayer({
id: 'canvas-layer',
type: 'raster',
source: 'canvas-source'
});
});
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Canvas Demo</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.js"></script>
</head>
<body>
<canvas id="canvasID" width="400" height="400">Canvas not supported</canvas>
<div id="map"></div>
</body>
</html>

Drawing a Rectangle with color and thickness in OnGUI

I would like to draw a frame / rectangle in OnGUI in order to display a certain area for debugging purposes.
This rectangle should be displayed with a certain "thickness" / line width and color.
So far, I have only found GUI.Label and GUI.Box, which both seems inadequate for this.
Thank you!
If it is only for debugging I would recommend to use Gizmos.DrawWireCube
Note: Only drawn in the SceneView not in the GameView so really only for debugging
private void OnDrawGizmosSelected()
{
// Draw a yellow cube at the transform position
var color = Gizmos.color;
Gizmos.color = Color.yellow;
Gizmos.DrawWireCube(transform.position, new Vector3(1, 1, 1));
Gizmos.color = color;
}
for showing it only if object is selected or OnDrawGizmos for showing it allways
Note that this is done in WorldSpace so if you rather want the size vector etc rotate together with the object you can wrap in between
var matrix = Gizmos.matrix;
Gizmos.matrix = transform.localToWorldMatrix;
//...
Gizmos.matrix = matrix;
Unfortunately there is no option to change the line thikness...
... but you could overcome this by simply drawing e.g. 4 normal cubes using Gizmos.DrawCube to form a rectangle. Something maybe like
private void OnDrawGizmos()
{
DrawDebugRect(new Vector2(0.5f, 0.3f), 0.05f);
}
private void DrawRect(Vector2 size, float thikness)
{
var matrix = Gizmos.matrix;
Gizmos.matrix = transform.localToWorldMatrix;
//top cube
Gizmos.DrawCube(Vector3.up * size.y / 2, new Vector3(size.x, thikness, 0.01f);
//bottom cube
Gizmos.DrawCube(Vector3.down * size.y / 2, new Vector3(size.x, thikness, 0.01f);
//left cube
Gizmos.DrawCube(Vector3.left * size.x / 2, new Vector3(thikness, size.y, 0.01f);
//right cube
Gizmos.DrawCube(Vector3.right * size.x / 2, new Vector3(thikness, size.y, 0.01f);
Gizmos.matrix = matrix;
}
I'm only on smartphone so it might not be copy-past-able but I think you'll get the idea ;)

Coordinates from getBoundingClientRect() are off on Ionic 3 app

I am writing an Ionic 3 application to take small pictures.
I am using the Camera Preview plugin to do this:
Then I get the coordinates of the green box using this:
<div id="mask" style="width:100%;height:100px;border:10px solid rgb(125, 255, 0);" *ngIf="!picture"> </div>
var box = document.getElementById("mask");
var rect = box.getBoundingClientRect();
console.log("MASK: "+rect.left+" "+rect.top+" "+rect.width+" "+rect.height)
Then I crop the image using the coordinates I got from the box to get this:
I am using the following code to crop the image:
generateFromImage(img, x, y, w, h, quality: number = 1, callback) {
var canvas: any = document.createElement("canvas");
var image = new Image();
image.src = img;
image.onload = () => {
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, x+this.offsetX, y+this.offsetY, w*this.scaleX, h*this.scaleY, 0, 0, w, h);
var dataUrl = canvas.toDataURL('image/jpeg', quality);
callback(dataUrl)
}
}
Where x,y,w,h are the coordinates I got from box.getBoundingClientRect().
As you can see, I had to introduce an offsetX, offsetY, scaleX, scaleY to adjust the coordinates because it was not working.
drawImage() parameters are sx,sy,sw,sh (source box -> coordinates from the original image) and x,y,w,h (destination -> coordinate of the destination image). I do not understand why it is off.
After some trial and error, I found that the following configuration works for my iPhone 8:
offsetX=125
offsetY=48
scaleX=1.7
scaleY=1.4
The Y coordinate difference I suspect that has something to do with the application toolbar. I have no idea why x is off and why using the same w and h on both source and destination does not keep the aspect ratio.
I did not test on another devices but it will most certainly fail because the offsets and scale factors will be different.
Why is it happening? Why do I need to fix the coordinates?
Thanks for your help!
Your main problem is probably that you don't take the CSS scaling into account.
The coordinates we use in drawImage are relative to the image's natural size (i.e, the one of the media, not of the <img>). This means that you can't come from the rendered transform matrix (getBoundingClientRect) to the media's one directly.
You need to first determine the scale that has been applied by CSS, so you can transform your screen space coordinates to the media's space.
var img_bbox = img.getBoundingClientRect();
// the ratio by which our image has been scaled by CSS
var scale_x = img_bbox.width / img.naturalWidth;
var scale_y = img_bbox.height / img.naturalHeight;
// ...then
ctx.drawImage(img,
rect.left / scale_x,
rect.top / scale_y,
rect.width/ scale_x,
rect.height / scale_y,
0,
0,
rect.width,
rect.height
);
Now, you will also have to take into account the difference between the position of your rectangle and the one of your <img>, so e.g x will actually be (rect_bbox.left - img_bbox.left) / scale_x.
One thing you didn't cleared up though, is if you wanted to crop inside, or outside the border, i.e should the border itself be part of the cropped area.
Here is an example which does take the border area too.
onload = crop;
function crop() {
// get the rendered bounding box of our elements
var r_bbox = rect.getBoundingClientRect();
var img_bbox = img.getBoundingClientRect();
// the ratio by which our image has been scaled by CSS
var scale_x = img_bbox.width / img.naturalWidth;
var scale_y = img_bbox.height / img.naturalHeight;
// our output coords
var output = {
x: r_bbox.left - img_bbox.left,
y: r_bbox.top - img_bbox.top,
w: r_bbox.width,
h: r_bbox.height
};
var ctx = canvas.getContext('2d');
canvas.width = output.w;
canvas.height = output.h;
ctx.drawImage(img,
// source
// we need to scale all coords by the CSS scaling
output.x / scale_x,
output.y / scale_y,
output.w / scale_x,
output.h / scale_y,
// destination, to rendered space, no scaling
0,
0,
output.w,
output.h
);
}
img{
width: 200px;
height: 200px;
}
#rect{
position:absolute;
left: 42px;
top: 50px;
width: 100px;
height: 50px;
z-index: 2;
border: 5px solid rgba(0,255,0,.5);
}
*{
vertical-align: top;
}
<img id="img" src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg">
<div id="rect"></div>
<canvas id="canvas"></canvas>
And one which takes only what is inside the border-area
onload = crop;
function crop() {
// get the rendered bounding box of our elements
var r_bbox = rect.getBoundingClientRect();
var img_bbox = img.getBoundingClientRect();
// the ratio by which our image has been scaled by CSS
var scale_x = img_bbox.width / img.naturalWidth;
var scale_y = img_bbox.height / img.naturalHeight;
// our output coords
var output = {
// rect.clientLeft is the size of the left border
// so add it to 'x'
x: r_bbox.left - img_bbox.left + rect.clientLeft,
// same as for 'x'
y: r_bbox.top - img_bbox.top + rect.clientTop,
// size of padding box
w: rect.clientWidth,
h: rect.clientHeight
};
var ctx = canvas.getContext('2d');
canvas.width = output.w;
canvas.height = output.h;
ctx.drawImage(img,
output.x / scale_x,
output.y / scale_y,
output.w / scale_x,
output.h / scale_y,
0,
0,
output.w,
output.h
);
}
/* scale our image through CSS */
img{
width: 200px;
height: 200px;
}
#rect{
position:absolute;
left: 42px;
top: 50px;
width: 100px;
height: 50px;
z-index: 2;
border: 5px solid rgba(255,0,0,.5);
background: rgba(0,255,0,.5);
}
*{
vertical-align: top;
}
<img id="img" src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg">
<div id="rect"></div>
<canvas id="canvas"></canvas>

Drag, drop and shape rotation with Raphael JS

I'm using RaphaelJS 2.0 to create several shapes in a div. Each shape needs to be able to be dragged and dropped within the bounds of the div, independently. Upon double clicking a shape, that shape needs to rotate 90 degrees. It may then be dragged and dropped and rotated again.
I've loaded some code onto fiddler: http://jsfiddle.net/QRZMS/. It's basically this:
window.onload = function () {
var angle = 0;
var R = Raphael("paper", "100%", "100%"),
shape1 = R.rect(100, 100, 100, 50).attr({ fill: "red", stroke: "none" }),
shape2 = R.rect(200, 200, 100, 50).attr({ fill: "green", stroke: "none" }),
shape3 = R.rect(300, 300, 100, 50).attr({ fill: "blue", stroke: "none" }),
shape4 = R.rect(400, 400, 100, 50).attr({ fill: "black", stroke: "none" });
var start = function () {
this.ox = this.attr("x");
this.oy = this.attr("y");
},
move = function (dx, dy) {
this.attr({ x: this.ox + dx, y: this.oy + dy });
},
up = function () {
};
R.set(shape1, shape2, shape3, shape4).drag(move, start, up).dblclick(function(){
angle -= 90;
shape1.stop().animate({ transform: "r" + angle }, 1000, "<>");
});
}
The drag and drop is working and also one of the shapes rotates on double click. However, there are two issues/questions:
How can I attach the rotation onto each shape automatically without having to hard-code each item reference into the rotate method? I.e. I just want to draw the shapes once, then have them all automatically exposed to the same behaviour, so they can each be dragged/dropped/rotated independently without having to explicitly apply that behaviour to each shape.
After a shape has been rotated, it no longer drags correctly - as if the drag mouse movement relates to the original orientation of the shape rather than updating when the shape is rotated. How can I get this to work correctly so that shapes can just be dragged and rotated many times, seamlessley?
Many thanks for any pointers!
I've tried several times to wrap my head around the new transform engine, to no avail. So, I've gone back to first principles.
I've finally managed to correctly drag and drop an object thats undergone several transformations, after trying to work out the impact of the different transformations - t,T,...t,...T,r,R etc...
So, here's the crux of the solution
var ox = 0;
var oy = 0;
function drag_start(e)
{
};
function drag_move(dx, dy, posx, posy)
{
r1.attr({fill: "#fa0"});
//
// Here's the interesting part, apply an absolute transform
// with the dx,dy coordinates minus the previous value for dx and dy
//
r1.attr({
transform: "...T" + (dx - ox) + "," + (dy - oy)
});
//
// store the previous versions of dx,dy for use in the next move call.
//
ox = dx;
oy = dy;
}
function drag_up(e)
{
// nothing here
}
That's it. Stupidly simple, and I'm sure it's occurred to loads of people already, but maybe someone might find it useful.
Here's a fiddle for you to play around with.
... and this is a working solution for the initial question.
I solved the drag/rotate issue by re-applying all transformations when a value changes. I created a plugin for it.
https://github.com/ElbertF/Raphael.FreeTransform
Demo here:
http://alias.io/raphael/free_transform/
As amadan suggests, it's usually a good idea to create functions when multiple things have the same (initial) attributes/properties. That is indeed the answer to your first question. As for the second question, that is a little more tricky.
When a Rapheal object is rotated, so is the coordinate plane. For some reason, dmitry and a few other sources on the web seem to agree that it's the correct way to implement it. I, like you, disagree. I've not managed to find an all round good solution but I did mange to create a work around. I'll briefly explain and then show the code.
Create a custom attribute to store the current state of rotation
Depending on that attribute you decide how to handle the move.
Providing that you are only going to be rotating shapes by 90 degrees (if not it becomes a lot more difficult) you can determine how the coordinates should be manipulated.
var R = Raphael("paper", "100%", "100%");
//create the custom attribute which will hold the current rotation of the object {0,1,2,3}
R.customAttributes.rotPos = function (num) {
this.node.rotPos = num;
};
var shape1 = insert_rect(R, 100, 100, 100, 50, { fill: "red", stroke: "none" });
var shape2 = insert_rect(R, 200, 200, 100, 50, { fill: "green", stroke: "none" });
var shape3 = insert_rect(R, 300, 300, 100, 50, { fill: "blue", stroke: "none" });
var shape4 = insert_rect(R, 400, 400, 100, 50, { fill: "black", stroke: "none" });
//Generic insert rectangle function
function insert_rect(paper,x,y, w, h, attr) {
var angle = 0;
var rect = paper.rect(x, y, w, h);
rect.attr(attr);
//on createion of the object set the rotation position to be 0
rect.attr({rotPos: 0});
rect.drag(drag_move(), drag_start, drag_up);
//Each time you dbl click the shape, it gets rotated. So increment its rotated state (looping round 4)
rect.dblclick(function(){
var pos = this.attr("rotPos");
(pos++)%4;
this.attr({rotPos: pos});
angle -= 90;
rect.stop().animate({transform: "r" + angle}, 1000, "<>");
});
return rect;
}
//ELEMENT/SET Dragger functions.
function drag_start(e) {
this.ox = this.attr("x");
this.oy = this.attr("y");
};
//Now here is the complicated bit
function drag_move() {
return function(dx, dy) {
//default position, treat drag and drop as normal
if (this.attr("rotPos") == 0) {
this.attr({x: this.ox + dx, y: this.oy + dy});
}
//The shape has now been rotated -90
else if (this.attr("rotPos") == 1) {
this.attr({x:this.ox-dy, y:this.oy + dx});
}
else if (this.attr("rotPos") == 2) {
this.attr({x: this.ox - dx, y: this.oy - dy});
}
else if (this.attr("rotPos") == 3) {
this.attr({x:this.ox+dy, y:this.oy - dx});
}
}
};
function drag_up(e) {
}
I can't really think of clear concise way to explain how the drag_move works. I think it's probably best that you look at the code and see how it works. Basically, you just need to work out how the x and y variables are now treated from this new rotated state. Without me drawing lots of graphics I'm not sure I could be clear enough. (I did a lot of turning my head sideways to work out what it should be doing).
There are a few drawbacks to this method though:
It only works for 90degree rotations (a huge amount more calculations would be needed to do 45degrees, nevermind any given degree)
There is a slight movement upon drag start after a rotation. This is because the drag takes the old x and y values, which have been rotated. This isn't a massive problem for this size of shape, but bigger shapes you will really start to notice shapes jumping across the canvas.
I'm assuming the reason that you are using transform is that you can animate the rotation. If this isn't necessary then you could use the .rotate() function which always rotates around the center of the element and so would eliminate the 2nd drawback I mentioned.
This isn't a complete solution, but it should definitely get you going along the correct path. I would be interested to see a full working version.
I've also created a version of this on jsfiddle which you can view here: http://jsfiddle.net/QRZMS/3/
Good luck.
I usually create an object for my shape and write the event handling into the object.
function shape(x, y, width, height, a)
{
var that = this;
that.angle = 0;
that.rect = R.rect(x, y, width, height).attr(a);
that.rect.dblclick(function() {
that.angle -= 90;
that.rect.stop().animate({
transform: "r" + that.angle }, 1000, "<>");
});
return that;
}
In the above, the constructor not only creates the rectangle, but sets up the double click event.
One thing to note is that a reference to the object is stored in "that". This is because the "this" reference changes depending on the scope. In the dblClick function I need to refer to the rect and angle values from my object, so I use the stored reference that.rect and that.angle
See this example (updated from a slightly dodgy previous instance)
There may be better ways of doing what you need, but this should work for you.
Hope it help,
Nick
Addendum: Dan, if you're really stuck on this, and can live without some of the things that Raphael2 gives you, I'd recommend moving back to Raphael 1.5.x. Transforms were just added to Raphael2, the rotation/translation/scale code is entirely different (and easier) in 1.5.2.
Look at me, updating my post, hoping for karma...
If you don't want to use a ElbertF library, you can transform Cartesian Coordinates in Polar Coordinates.
After you must add or remove the angle and transform again in Cartesian Coordinate.
We can see this example with a rect rotate in rumble and moved.
HTML
<div id="foo">
</div>
JAVASCRIPT
var paper = Raphael(40, 40, 400, 400);
var c = paper.rect(40, 40, 40, 40).attr({
fill: "#CC9910",
stroke: "none",
cursor: "move"
});
c.transform("t0,0r45t0,0");
var start = function () {
this.ox = this.type == "rect" ? this.attr("x") : this.attr("cx");
this.oy = this.type == "rect" ? this.attr("y") : this.attr("cy");
},
move = function (dx, dy) {
var r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
var ang = Math.atan2(dy,dx);
ang = ang - Math.PI/4;
dx = r * Math.cos(ang);
dy = r * Math.sin(ang);
var att = this.type == "rect" ? { x: this.ox + dx, y: this.oy + dy} : { cx: this.ox + dx, cy: this.oy + dy };
this.attr(att);
},
up = function () {
};
c.drag(move, start, up);?
DEMO
http://jsfiddle.net/Ef83k/74/
my first thought was to use getBBox(false) to capture the x,y coordinates of the object after transform, then removeChild() the original Raphael obj from the canvas, then redraw the object using the coordinate data from getBBox( false ). a hack but i have it working.
one note though: since the object the getBBox( false ) returns is the CORNER coordinates ( x, y) of the object you need to calculate the center of the re-drawn object by doing ...
x = box['x'] + ( box['width'] / 2 );
y = box['y'] + ( box['height'] / 2 );
where
box = shapeObj.getBBox( false );
another way to solve the same problem