How to change path's width and height drawn by Raphael JS? - javascript

I have path that I then draw with Raphael JS. Code looks like this:
var paper = Raphael($('#draw_here')[0], '32', '32');
var star = paper.path("M 22.137,19.625 32,12 20,12 16,0 12,12 0,12 9.875,19.594 6,32 16.016,24.32 26.008,32 z");
star.attr({
fill: 'white',
stroke: 'black',
title: 'This is a STAAAAAAAR!'
});
It works and it draws 32 x 32 pixels big star. How to change it's width and height?
I tried to do this:
var paper = Raphael($('#draw_here')[0], 64, 64);
var star = paper.path("M 22.137,19.625 32,12 20,12 16,0 12,12 0,12 9.875,19.594 6,32 16.016,24.32 26.008,32 z");
star.attr({
fill: 'white',
stroke: 'black',
title: 'This is a STAAAAAAAR!',
width: 64,
height: 64
});
...but all that changes is “paper element's“ width and height, not star's.
The thing I want to do is put effect that star gets bigger when mouse's cursor is on it.
Thanks in any advice!
P.S. I didn't write the path myself, it's get from Iconic set.

You have to adjust the path too!
Just try it on the Raphaël Playground.
The width and height attributes are just the size of the element to draw on, but the path coordinates are still the same.
For a simple resize you may also use
star.transform("s2");
to resize it to the 2x as before.
If don't want to edit the path and simple scale it, you should use the transform method to resize it and translate the full path by a certain value.
star.transform("t32,32 s2");
This for example will move the object by 32px from the top and 32px from the left and resize it to the 2x as before.

Related

how to store drawing rectangle coordinates in percentage in Fabricjs

In my project I am working on dynamically zone creation and deletion on floor of particular building. To do that would using Raphealjs, using rapheal I am able to create zone on floor. I am storing height, width, startx and stary to database.
But the problem is, it is stores th coordinates in pixels. When I make my screen smaller, the zone going out of my floor. Solution for this I need to store the coordinates in percentage.
Is there any way to do it, Please help me to come out.
Below are my images it is showing clear picture.
When Small screen:
Please give me solution, Thanks in advance..
Did you try to scale down/up your fabric objects whenever you are making your screen smaller? My recommendation would be to store a logical points. For example, your canvas size is 500x600 px, and you have rectangle at position x=100, y=100, width=50, and height=50. Logical coordinate will be your actual coordinate in pixels divided by dimension of the canvas. In other words, your
logicalX = x/canvas.width (100/500=0.2)
logicalY = y/canvas.height (100/600=0.16666666666666)
width = width/canvas.width (50/500=0.1)
height = height/canvas.height (50/600=0.833333)
I this case you will have dynamic coordinates, even if you will load your app with different canvas size from previous session. All the time, whenever you resize your canvas you should recalculate your physical points and render all canvas again. You can assign logical coordinates object to your fabricjs object:
var rect1 = new fabric.Rect({
left: 100,
top: 100,
fill: 'green',
width: 50,
height: 50,
id : someid,
logicalCoords: {x: 0.2,
y: 0.16666666666666,
width: 0.1,
height: 0.833333}
});
Recalculating physical points will be easy, you should loop through all fabricjs objects and multiply your canvas dimension by logical points:
canvas.forEachObject(function(o){
o.x = logicalCoords.x * canvas.width;
o.x = logicalCoords.y * canvas.height;
o.x = logicalCoords.width * canvas.width;
o.x = logicalCoords.height * canvas.height;
});
canvas.renderAll();
WARNING: it is not working code, it just an idea how to fix your problem.
UPDATE 1:
Fabricjs requires pixel data for drawing shapes. You can do something like this:
var rect1 = new fabric.Rect({
left: toPixels(percentValueLeftFromDB),
top: toPixels(percentValueTopFromDB),
fill: 'green',
width: toPixels(percentValueWidthFromDB),
height: toPixels(percentValueHeightFromDB),
});
function toPixels(value){
//do conversion here..
//you should take care of left and top coordinates
pixelValue = canvas.width * value / 100; //calculates only width and top
return pixelValue;
There is no automation like percentage. You have to do your conversion by yourself. I showed you 2 variations Logical Points to Physical Points, or Percentage points to Physical Points. All the time you need to path Physical points to the Fabricjs. In my opinion, Logical points is more accurate then percentage.
UPDATE 2:
Why logical points will be responsive, because it will need all the time viewer size. For example, as you mentioned in comment for different physical screen sizes (in inch), you will have different screen resolutions. Ideally, you will have dynamic canvas size for each resolution, which can be calculated on a fly or set as percentage in CSS. Let's assume you set width = 75% and height = 60% for canvas. If you will open this canvas on different screens or devices your physical width and height of the canvas in pixels will be different. In this case logicalPoint will be on the same spot on each device, because you will need to use canvas dimensions to convert them into physicalPoint.
Let's say your canvas has dimensions 1000x500 px, your rectangle has logical (top,left) coordinate (0.5,0.5), and width = 0.5, and height = 0.5. Physical result will be rectangle at position (500,250) with width = 500 and height = 250. If you will take the same logical coordinates by tries to show them on smaller canvas it will be the same position of the rectangle.
Please, check this simple fiddle which shows logical coordinated for 2 canvases.
ATTENTION: This logic will work only if you will keep you canvas size with the same aspect ratio. If you will resize canvas width by 50%, you must resize your height by 50% as well.
Also, fiddle code is here:
HTML:
<canvas id="canvas1" width="200" height="150" style="border:1px solid #ccc"></canvas>
<canvas id="canvas2" width="400" height="350" style="border:1px solid #ccc"></canvas>
JavaScript:
(function() {
var canvas1 = this.__canvas = new fabric.Canvas('canvas1');
var canvas2 = this.__canvas = new fabric.Canvas('canvas2');
var rect = new fabric.Rect({
left: logicalToPhysical(0.5, canvas1.width),
top: logicalToPhysical(0.5, canvas1.height),
width: logicalToPhysical(0.5, canvas1.width),
height: logicalToPhysical(0.5, canvas1.height),
fill: 'rgba(255,127,39,1)'
});
canvas1.add(rect);
canvas1.renderAll();
var rect = new fabric.Rect({
left: logicalToPhysical(0.5, canvas2.width),
top: logicalToPhysical(0.5, canvas2.height),
width: logicalToPhysical(0.5, canvas2.width),
height: logicalToPhysical(0.5, canvas2.height),
fill: 'rgba(255,127,39,1)'
});
canvas2.add(rect);
canvas2.renderAll();
})();
function logicalToPhysical(value, dimension){
return value * dimension;
}

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.

How to add a background color to Kinetic JS stage or layer?

I have a Kinetic JS stage and a layer
var stage = new Kinetic.Stage({
container: 'container',
width : STAGE_WIDTH,
height : STAGE_HEIGHT
});
var layer = new Kinetic.Layer();
I've set the page color to be #bbb.
body {
background: #bbb;
}
I'd like to set the canvas color to be white. But I can't seem to find a method or a way to add a background color to the stage itself or the layer that I add all the object on.
You can also set the background color of your container element through CSS. That's essentially the same as setting the background color of the stage. If you want a background at the layer level, you'll need to add a filled rectangle or similar, as previously mentioned.
I had the same problem, I wanted to add a "background". I added a rectagle with the 100% height and width, with this code:
var rect = new Kinetic.Rect({
x: 0,
y: 0,
width: stageDimensions.width, //full width
height: stageDimensions.height, //full height
fill: 'white', //background color
});
layer.add(rect);
Since I wanted to be able to remove the "background", this is how I manage to solve my problem.
Hope it helps you.
You can change the background color with JavaScript...
document.getElementById('container').style.background = '#fff';
There isn't an API method to add a background color.
Instead add a colored rectangle that covers the layer.
Of course, add the background rectangle before all other shapes.
Old question, but you can use the get properties of the stage, and fill a full rectangle, adding it to the layer before anything else. Sample code:
var stage = new Kinetic.Stage({
container: 'canvas-container',
width: 900,
height: 450
});
// create background
var stageBg = new Kinetic.Rect({
x: 0,
y: 0,
width: stage.getWidth(),
height: stage.getHeight(),
fill: "rgb(40,40,40)"
});
layer.add(stageBg);
stage.add(layer);

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.

Scale path to paper size or set path dimensions in pixels with RaphaelJS

I'm trying to scale down icons that I'm rendering with RaphaelJS. Using "transform": "s.5,.5,0,0" works just fine, but I'd much prefer to be able to give absolute pixel values, e.g. 20px. A nice alternative would be to get the path(s) to automatically scale to the paper dimensions. Are either of these possible?
My current code that uses transform:
$('.cog-menu-options-icon').each(function (i) {
paper = Raphael($(this)[0], 20, 20);
paper.path(window.icons.wrench).attr({ "fill": "#333", "transform": "s.5,.5,0,0" });
});
Can the path be set to automatically scale to fit the paper? Can the height/width of the path be set in pixels?
I am not super-familiar with Raphael, but could you use the bounding box function getBBox() to get the dimensions, and use those to calculate the correct scale?
$('.cog-menu-options-icon').each(function (i) {
paper = Raphael($(this)[0], 20, 20);
var icon = paper.path(window.icons.wrench);
var max = icon.getBBox().width;
var h = icon.getBBox().width;
if(h>max) {
max = h;
}
var scale = 20/max;
icon.attr({ "fill": "#333", "transform": "s"+scale+","+scale+",0,0" });
});
Here is an example: http://jsfiddle.net/xUyRS/
I added code to translate the icon if the path does not have a 0,0 origin, and to center the icon.
I also found that Raphael has a bug with getBBox which returns bad results on multiple calls to the same path, so I am using a forked version with the fix.

Categories

Resources