Raphael JS position rectangle based on center coordinates - javascript

Is there a way to position a rectangle in Raphael.js based on the center coordinates (much like positioning a circle)?

You can do it by simply creating your own custom element, here is an example on how it can be done :
Raphael.fn.MyRect = function( cx, cy, width, height ) {
var xLeftTop = cx - (width / 2);
var yLeftTop = cy - (height / 2);
this.rectObj = paper.rect( xLeftTop, yLeftTop, width, height );
return this;
};
var paper = Raphael(10, 50, 320, 200);
paper.MyRect( 95, 35, 50, 50 ); // 95 is the center in x and 35 the center in y
A live example : http://jsfiddle.net/7QMbH/
This way, you can create as many rectangle you want, and it makes your code understandable.

The easiest way is to create your rectangle with coordinates x - rect_width/2 and y - rect_heigth/2:
Example:
var rect_w = 180,
rect_h = 80;
var paper = Raphael(10, 10, 500, 500);
paper.rect(100 - rect_w/2, 100 - rect_h/2, rect_w, rect_h, 5);
// The last 5 gives curved edges to you rectangle (if you need it of course).
Basically, instead of 100,100 for top-left, you give 10,60. Good Luck

Related

How do I rotate a html canvas shape by only using a transform?

I am wondering how you can rotate an image by only using the transform function. From my understanding this is not possible, since the only things you can do with transform are the following:
Horizontal scaling
Horizontal skewing
Vertical skewing
Vertical scaling
Horizontal moving
Vertical moving
Source: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations
And I don't see how any of these would be able to rotate the shape, is this even possible. I assume it must be possible, since rotate is in-fact a type transformation.
2D transform basics
6 values as 3 vectors
The transform is a set of 6 numbers. The 6 numbers as 3 pairs represent the direction and scale of the x axis, the direction and scale of the y axis, and the position of the origin.
Default transform AKA Identity matrix
The default transform (called the identity matrix) has the values ctx.setTransform(1, 0, 0, 1, 0, 0) meaning that
the x axis is 1 transformed pixel per CSS pixel in the direction {x: 1, y: 0} left to right
the y axis is 1 transformed pixel per CSS pixel in the direction {x: 0, y: 1} top to bottom
the origin is at pixel location {x: 0, y: 0} top left corner
Scaling
If we scale the transform we increase the length of the first two vectors. To scale by 2 the transform is ctx.setTransform(2, 0, 0, 2, 0, 0);
the x axis is 1 transformed pixel for every 2 CSS pixel in the x direction {x: 2, y: 0} left to right
the y axis is 1 transformed pixel for every 2 CSS pixel in the y direction {x: 0, y: 2} top to bottom
the origin is still top left {x: 0, y: 0}
Rotate and translation
If we want to rotate by 90deg a square 256 by 256 image then the transform is ctx.setTransform(0, 1, -1, 0, 256, 0)
the x axis is 1 transformed pixel per CSS pixel down in the y direction {x: 0, y: 1}
the y axis is 1 transformed pixel per CSS pixel across in the negative x direction {x: -1, y: 0} right to left
the origin (where the image 0, 0 will be on the canvas) is {x: 256, y: 0}
Thus if we run
ctx.setTransform(0, 1, -1, 0, 256, 0);
ctx.drawImage(myImage, 0, 0, 256, 256); // will draw image rotated 90deg CW
We get a rotated image.
A vector
A vector is two values that have a x and y value. The vector defines a direction and length.
Create a rotated unit vector
To convert a direction to a vector we use sin and cos
const myDirection = angle;
const myDirectionAsRadians = angle * (Math.PI / 180); // convert angle to radians
const x = Math.cos(myDirectionAsRadians)
const y = Math.sin(myDirectionAsRadians)
If we set myDirection to 90 (deg) then x = 0 and y = 1 pointing down the canvas
Using sin and cos creates a vector in any direction. It has a special property in that its length is always 1. We call such a vector a Unit vector. You may sometimes see a vector being normalized. This converts a vector of any length to a unit vector. It is done by dividing the vector x and y by its length.
function normalize(vector) {
const length = Math.hypot(vector.x, vector.y);
vector.x /= length;
vector.y /= length;
}
NOTE a vector with zero length eg x: 0, y:0 can not be normalized. Not because it has no length (the length is 0) but because it has no direction.
Scale a rotated vector
We can define an angle and a scale
const myDirection = -90;
const myDirectionAsRadians = -90 * (Math.PI / 180); // -90 as radians
const myScale = 2;
const x = Math.cos(myDirectionAsRadians) * myScale
const y = Math.sin(myDirectionAsRadians) * myScale
Now for -90 deg the vector is x = 0 and y = -2 pointing up and two CSS pixels long.
Quick rotate vector 90deg CW
For a uniform scale and rotation (the image is always square) all we need is a single vector. For example from the above. x = 0 and y = -2 (pointing up) can be rotated 90 CW by swapping the two components and negating the new x. eg xx = -y and y = x to get xx = 2 and y = 0 2 CSS pixels from left two right. Thus we have the direction and scale of both the x and y axis. With the y axis always 90 CW from the x.
Using the transform to draw
Create rotated and scaled transform
To create a transform that rotates any angle and scales by any amount
function scaleAndRotate(scale, rotate) { // rotate is in radians
// get direction and length of x axis
const xAX = Math.cos(rotate) * scale;
const xAY = Math.sin(rotate) * scale;
// get direction and length of y axis that is 90 deg CW of x axis and same length
const [yAX, yAY] = [-xAY, xAX]; // swap and negate new x
// set the transform
ctx.setTransform(xAX, xAY, yAX, yAY, 0, 0);
}
Drawing an image
Lets create a function that will draw an image anywhere on the canvas that is rotated and scaled uniformly. We will use the center of the image as the reference point
function drawImageScaleRotate(img, x, y, scale, rotate) {
// define the direction and scale of x axis
const xAX = Math.cos(rotate) * scale;
const xAY = Math.sin(rotate) * scale;
// create the transform with yaxis at 90 CW of x axis and origin at x, y
ctx.setTransform(xAX, xAY, -xAY, xAX, x, y);
// Draw the image so that its center is at the new origin x, y
ctx.drawImage(img, -img.width / 2, -img.height / 2);
}
There is much more
When we set the transform with ctx.setTranform we replace the existing transform. This transform remains current. If we use ctx.transform, ctx.rotate, ctx.scale, ctx.translate the transforms are applied to the current transform, you build a transform in stages.
The transform functions are relatively expensive in terms of CPU cycles. That is way using sin and cos to build the matrix is much faster than using ctx.scale, ctx.rotate, ctx.translate to do the same thing starting from default.
Building transforms can become tricky as we need to keep track of what stage we are at.
We generally only use these function not to transform a single image (text, path, or what ever) but to create linked transforms.
For example a game object like a tank. The body of the tank is transformed (rotated and positioned) then the turret which is rotated with the body but has an additional independent rotation by using ctx.rotate. Full explanation is beyond the scope of this question.
The final function
From all this we can create a simplified function that will draw an image with its center at any location, that is uniformly scaled and rotated
function drawImageScaleRotate(img, x, y, scale, rotate) {
const xAX = Math.cos(rotate) * scale;
const xAY = Math.sin(rotate) * scale;
ctx.setTransform(xAX, xAY, -xAY, xAX, x, y);
ctx.drawImage(img, -img.width / 2, -img.height / 2);
}
To reset the transform to the default use ctx.resetTransform NOTE not fully supported yet or use ctx.setTransform(1,0,0,1,0,0);
Animated transforms faster than CSS + HTML or SVG
Using the above function is the 2nd fastest way to draw animated rotated scaled images, faster than CSS + HTML or SVG. You can literally fill the screen with animated images.
Demo
var w,h;
var image = new Image;
image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
document.body.appendChild(canvas);
const resize = () => { w = canvas.width = innerWidth; h = canvas.height = innerHeight;}
const rand = (min,max) => Math.random() * (max ?(max-min) : min) + (max ? min : 0);
const DO = (count,callback) => { while (count--) { callback(count) } }
resize();
addEventListener("resize",resize);
const sprites = [];
DO(500,()=>{
sprites.push({
xr : rand(w), yr : rand(h),
x : 0, y : 0, // actual position of sprite
r : rand(Math.PI * 2),
scale : rand(0.1,0.25),
dx : rand(-2,2), dy : rand(-2,2),
dr : rand(-0.2,0.2),
});
});
function drawImage(image, spr){
const xAX = Math.cos(spr.r) * spr.scale;
const xAY = Math.sin(spr.r) * spr.scale;
ctx.setTransform(xAX, xAY, -xAY, xAX, spr.x, spr.y);
ctx.drawImage(image, -image.width / 2, -image.height / 2);
}
function update(){
var ihM,iwM;
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,w,h);
if(image.complete){
var iw = image.width;
var ih = image.height;
for(var i = 0; i < sprites.length; i ++){
var spr = sprites[i];
spr.xr += spr.dx;
spr.yr += spr.dy;
spr.r += spr.dr;
// keeps images in canvas adds space to all sides so that image
// can move completely of the canvas befor warping to other side
// I do this to prevent images visualy popping in and out at edges
iwM = iw * spr.scale * 2 + w;
ihM = ih * spr.scale * 2 + h;
spr.x = ((spr.xr % iwM) + iwM) % iwM - iw * spr.scale;
spr.y = ((spr.yr % ihM) + ihM) % ihM - ih * spr.scale;
drawImage(image,spr);
}
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
If you are wondering which is the fastest way to draw animated content. That is via webGL. The above can draw 1000 scaled rotated images on most devices at a good frame rate. WebGL can easily draw 10000 (with extra features eg colored) in the same time.
You can rotate your image easily. You can specify angles by which you wants to apply rotation.
Source : https://www.w3schools.com/cssref/css3_pr_transform.asp
<style>
img.a {
transform: rotate(180deg);
}
</style>
<img class="a" src="https://picsum.photos/id/237/200/300"/>
use transform "rotate".and use it as below
p{
color:red;
font-size:12px;
text-align:center;
}
.rotate1{
transform:rotate(45deg);
margin-top:40px;
}
.rotate2{
transform:rotate(90deg);
margin-top:40px;
}
.rotate3{
transform:rotate(180deg);
margin-top:40px;
}
<p class="rotate1">ROTATE1</p>
<p class="rotate2">ROTATE2</p>
<p class="rotate3">ROTATE3</p>
You have ctx.rotate(radians) function.
Read below:
https://www.w3schools.com/tags/canvas_rotate.asp

Insert child element after another in Pixi.js

Scenario
I making a 2d visualization of two round rectangles as following:
const app = new PIXI.Application({ backgroundColor: 0xffffff} );
const roundBox = new PIXI.Graphics();
roundBox.lineStyle(4, 0x99CCFF, 1);
roundBox.beginFill(0xffffff);
roundBox.drawRoundedRect(0, 0, 200, 100, 10);
roundBox.endFill();
const roundBox2 = new PIXI.Graphics();
roundBox2.lineStyle(4, 0x99CCFF, 1);
roundBox2.beginFill(0xffffff);
roundBox2.drawRoundedRect(0, 0, 200, 100, 10);
roundBox2.endFill();
app.stage.addChild(roundBox, roundBox2);
I want to append horizontally the second rectangle without calculating the horizontal position of the second child.
Question
Is there a way to insert horizontally a graphic element in pixi.js?
You can rotate the second rectangle 90 degrees, like this:
// Center the point to rotate this rectangle at its center
roundBox2.pivot = new PIXI.Point(100, 50);
// Math.PI / 2 is equal to 90 degrees in radians.
roundBox2.rotation = Math.PI / 2;

How to find the angle from the center of a rectangle to its vertices

I'm developing a collision detection system in Javascript, and I need to find from which side of the rectangle a ball collided.
Anyway, what I need right now is to find the angle from the center of a rectangle to its vertices. Like this:
As you can see in the image, I want to find that angle, but also the rest of the angles to the bottom left and top left vertices.
I know this is math, but I need to code the formula in Javascript anyway.
Let's say I have this:
var box = {
width : 200,
height : 100
};
var boxCenter = {x : box.width / 2, y : box.height / 2 };
var angleRight = // ... ;
var angleBottom = // ... ;
And so on
The angle (red) may be calculated with:
var angle = 2* Math.atan(height/width);

Change start point of circle in Canvas?

I found this code to type on an arc in a Canvas element. I've modified it basically to what I need, but I'm not sure how to change the "starting point" of it?
I've got a demo here
circle = {
x: cnv.width / 2, // x coordinate of center
y: cnv.height / 2, // y coordinate of center
r: 150,
lw: 75
};
for example, giving as given below will plot it at the origin of the canvas
circle = {
x: 0, // x coordinate of center
y: 0, // y coordinate of center
r: 150,
lw: 75
};

How to flip images horizontally with HTML5

In IE, I can use:
<img src="http://example.com/image.png" style="filter:FlipH">
to implement an image flip horizontally.
Is there any way to flip horizontally in HTML5? (maybe by using canvas?)
thanks all :)
canvas = document.createElement('canvas');
canvasContext = canvas.getContext('2d');
canvasContext.translate(width, 0);
canvasContext.scale(-1, 1);
canvasContext.drawImage(image, 0, 0);
Here's a snippet from a sprite object being used for testing and it produces the results you seem to expect.
Here's another site with more details. http://andrew.hedges.name/widgets/dev/
You don't need HTML5, it can be done with CSS same as in IE:
-moz-transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
-o-transform: scale(-1, 1);
transform: scale(-1, 1);
filter: FlipH;
I like Eschers function above. I have made it a little neater and better. I have added flop (vertically) besides flip. Also a possibility to draw/rotate around the center of the image instead of top left. Finally, the function does not require all arguments. img, x and y are required but the rest are not.
If you were using something like context.drawImage(...), you can now just use drawImage(...) and add the rotate/flip/flop functionality explained here:
function drawImage(img, x, y, width, height, deg, flip, flop, center) {
context.save();
if(typeof width === "undefined") width = img.width;
if(typeof height === "undefined") height = img.height;
if(typeof center === "undefined") center = false;
// Set rotation point to center of image, instead of top/left
if(center) {
x -= width/2;
y -= height/2;
}
// Set the origin to the center of the image
context.translate(x + width/2, y + height/2);
// Rotate the canvas around the origin
var rad = 2 * Math.PI - deg * Math.PI / 180;
context.rotate(rad);
// Flip/flop the canvas
if(flip) flipScale = -1; else flipScale = 1;
if(flop) flopScale = -1; else flopScale = 1;
context.scale(flipScale, flopScale);
// Draw the image
context.drawImage(img, -width/2, -height/2, width, height);
context.restore();
}
Examples:
var myCanvas = document.getElementById("myCanvas");
var context = myCanvas.getContext("2d"); // i use context instead of ctx
var img = document.getElementById("myImage"); // your img reference here!
drawImage(img, 100, 100); // just draw it
drawImage(img, 100, 100, 200, 50); // draw it with width/height specified
drawImage(img, 100, 100, 200, 50, 45); // draw it at 45 degrees
drawImage(img, 100, 100, 200, 50, 0, true); // draw it flipped
drawImage(img, 100, 100, 200, 50, 0, false, true); // draw it flopped
drawImage(img, 100, 100, 200, 50, 0, true, true); // draw it flipflopped
drawImage(img, 100, 100, 200, 50, 45, true, true, true); // draw it flipflopped and 45 degrees rotated around the center of the image :-)
Mirror an image or rendering using the canvas.
Note. This can be done via CSS as well.
Mirroring
Here is a simple utility function that will mirror an image horizontally, vertically or both.
function mirrorImage(ctx, image, x = 0, y = 0, horizontal = false, vertical = false){
ctx.save(); // save the current canvas state
ctx.setTransform(
horizontal ? -1 : 1, 0, // set the direction of x axis
0, vertical ? -1 : 1, // set the direction of y axis
x + (horizontal ? image.width : 0), // set the x origin
y + (vertical ? image.height : 0) // set the y origin
);
ctx.drawImage(image,0,0);
ctx.restore(); // restore the state as it was when this function was called
}
Usage
mirrorImage(ctx, image, 0, 0, true, false); // horizontal mirror
mirrorImage(ctx, image, 0, 0, false, true); // vertical mirror
mirrorImage(ctx, image, 0, 0, true, true); // horizontal and vertical mirror
Drawable image.
Many times you will want to draw on images. I like to call them drawable images. To make an image drawable you convert it to a canvas
To convert an image to canvas.
function makeImageDrawable(image){
if(image.complete){ // ensure the image has loaded
var dImage = document.createElement("canvas"); // create a drawable image
dImage.width = image.naturalWidth; // set the resolution
dImage.height = image.naturalHeight;
dImage.style.width = image.style.width; // set the display size
dImage.style.height = image.style.height;
dImage.ctx = dImage.getContext("2d"); // get drawing API
// and add to image
// for possible later use
dImage.ctx.drawImage(image,0,0);
return dImage;
}
throw new ReferenceError("Image is not complete.");
}
Putting it all together
var dImage = makeImageDrawable(image); // convert DOM img to canvas
mirrorImage(dImage.ctx, dImage, 0, 0, false, true); // vertical flip
image.replaceWith(dImage); // replace the DOM image with the flipped image
More mirrors
If you wish to be able to mirror along an arbitrary line see the answer Mirror along line
One option is to horizontally flip the pixels of images stored in ImageData objects directly, e.g.
function flip_image (canvas) {
var context = canvas.getContext ('2d') ;
var imageData = context.getImageData (0, 0, canvas.width, canvas.height) ;
var imageFlip = new ImageData (canvas.width, canvas.height) ;
var Npel = imageData.data.length / 4 ;
for ( var kPel = 0 ; kPel < Npel ; kPel++ ) {
var kFlip = flip_index (kPel, canvas.width, canvas.height) ;
var offset = 4 * kPel ;
var offsetFlip = 4 * kFlip ;
imageFlip.data[offsetFlip + 0] = imageData.data[offset + 0] ;
imageFlip.data[offsetFlip + 1] = imageData.data[offset + 1] ;
imageFlip.data[offsetFlip + 2] = imageData.data[offset + 2] ;
imageFlip.data[offsetFlip + 3] = imageData.data[offset + 3] ;
}
var canvasFlip = document.createElement('canvas') ;
canvasFlip.setAttribute('width', width) ;
canvasFlip.setAttribute('height', height) ;
canvasFlip.getContext('2d').putImageData(imageFlip, 0, 0) ;
return canvasFlip ;
}
function flip_index (kPel, width, height) {
var i = Math.floor (kPel / width) ;
var j = kPel % width ;
var jFlip = width - j - 1 ;
var kFlip = i * width + jFlip ;
return kFlip ;
}
For anyone stumbling upon this.
If you want to do more complex drawing, the other scale-based answers don't all work. By 'complex' i mean situations where things are more dynamic, like for games.
The problem being that the location is also flipped. So if you want to draw a small image in the top left corner of the canvas and then flip it horizontally, it will relocate to the top right.
The fix is to translate to the center of where you want to draw the image, then scale, then translate back. Like so:
if (flipped) {
ctx.translate(x + width/2, y + width/2);
ctx.scale(-1, 1);
ctx.translate(-(x + width/2), -(y + width/2));
}
ctx.drawImage(img, x, y, width, height);
Here x and y are the location you want to draw the image, and width and height are the width and height you want to draw the image.
I came across this page, and no-one had quite written a function to do what I wanted, so here's mine. It draws scaled, rotated, and flipped images (I used this for rending DOM elements to canvas that have these such transforms applied).
var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
var img = document.getElementById("myimage.jpg"); //or whatever
var deg = 13; //13 degrees rotation, for example
var flip = "true";
function drawImage(img, x, y, width, height, deg, flip){
//save current context before applying transformations
ctx.save();
//convert degrees to radians
if(flip == "true"){
var rad = deg * Math.PI / 180;
}else{
var rad = 2*Math.PI - deg * Math.PI / 180;
}
//set the origin to the center of the image
ctx.translate(x + width/2, y + height/2);
//rotate the canvas around the origin
ctx.rotate(rad);
if(flip == "true"){
//flip the canvas
ctx.scale(-1,1);
}
//draw the image
ctx.drawImage(img, -width/2, -height/2, width, height);
//restore the canvas
ctx.restore();
}

Categories

Resources