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.
Related
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;
}
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/
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);
Is there a way one could zoom and pan on a canvas using KineticJS? I found this library kineticjs-viewport, but just wondering if there is any other way of achieving this because this library seems to be using so many extra libraries and am not sure which ones are absolutely necessary to get the job done.
Alternatively, I am even open to the idea of drawing a rectangle around the region of interest and zooming into that one particular area. Any ideas on how to achieve this? A JSFiddle example would be awesome!
You can simply add .setDraggable("draggable") to a layer and you will be able to drag it as long as there is an object under the cursor. You could add a large, transparent rect to make everything draggable. The zoom can be achieved by setting the scale of the layer. In this example I'm controlling it though the mousewheel, but it's simply a function where you pass the amount you want to zoom (positive to zoom in, negative to zoom out). Here is the code:
var stage = new Kinetic.Stage({
container: "canvas",
width: 500,
height: 500
});
var draggableLayer = new Kinetic.Layer();
draggableLayer.setDraggable("draggable");
//a large transparent background to make everything draggable
var background = new Kinetic.Rect({
x: -1000,
y: -1000,
width: 2000,
height: 2000,
fill: "#000000",
opacity: 0
});
draggableLayer.add(background);
//don't mind this, just to create fake elements
var addCircle = function(x, y, r){
draggableLayer.add(new Kinetic.Circle({
x: x*700,
y: y*700,
radius: r*20,
fill: "rgb("+ parseInt(255*r) +",0,0)"
})
);
}
var circles = 300
while (circles) {
addCircle(Math.random(),Math.random(), Math.random())
circles--;
}
var zoom = function(e) {
var zoomAmount = e.wheelDeltaY*0.001;
draggableLayer.setScale(draggableLayer.getScale().x+zoomAmount)
draggableLayer.draw();
}
document.addEventListener("mousewheel", zoom, false)
stage.add(draggableLayer)
http://jsfiddle.net/zAUYd/
Here's a very quick and simple implementation of zooming and panning a layer. If you had more layers which would need to pan and zoom at the same time, I would suggest grouping them and then applying the on("click")s to that group to get the same effect.
http://jsfiddle.net/renyn/56/
If it's not obvious, the light blue squares in the top left are clicked to zoom in and out, and the pink squares in the bottom left are clicked to pan left and right.
Edit: As a note, this could of course be changed to support "mousedown" or other events, and I don't see why the transformations couldn't be implemented as Kinetic.Animations to make them smoother.
this is what i have done so far.. hope it will help you.
http://jsfiddle.net/v1r00z/ZJE7w/
I actually wrote kineticjs-viewport. I'm happy to hear you were interested in it.
It is actually intended for more than merely dragging. It also allows zooming and performance-focused clipping. The things outside of the clip region aren't rendered at all, so you can have great rendering performance even if you have an enormous layer with a ton of objects.
That's the use case I had. For example, a large RTS map which you view via a smaller viewport region -- think Starcraft.
I hope this helps.
As I was working with Kinetic today I found a SO question that might interest you.
I know it would be better as a comment, but I don't have enough rep for that, anyway, I hope that helps.
These answers seems not to work with the KineticJS 5.1.0. These do not work mainly for the signature change of the scale function:
stage.setScale(newscale); --> stage.setScale({x:newscale,y:newscale});
However, the following solution seems to work with the KineticJS 5.1.0:
JSFiddle: http://jsfiddle.net/rpaul/ckwu7u86/3/
Unfortunately, setting state or layer draggable prevents objects not draggable.
Duopixel's zooming solution is good, but I would rather set it for stage level, not layer level.
Her is my solution
var stage = new Kinetic.Stage({
container : 'container',
width: $("#container").width(),
height: $("#container").height(),
});
var layer = new Kinetic.Layer();
//layer.setDraggable("draggable");
var center = { x:stage.getWidth() / 2, y: stage.getHeight() / 2};
var circle = new Kinetic.Circle({
x: center.x-100,
y: center.y,
radius: 50,
fill: 'green',
draggable: true
});
layer.add(circle);
layer.add(circle.clone({x: center.x+100}));
// zoom by scrollong
document.getElementById("container").addEventListener("mousewheel", function(e) {
var zoomAmount = e.wheelDeltaY*0.0001;
stage.setScale(stage.getScale().x+zoomAmount)
stage.draw();
e.preventDefault();
}, false)
// pan by mouse dragging on stage
stage.on("dragstart dragmove", function(e) {window.draggingNode = true;});
stage.on("dragend", function(e) { window.draggingNode = false;});
$("#container").on("mousedown", function(e) {
if (window.draggingNode) return false;
if (e.which==1) {
window.draggingStart = {x: e.pageX, y: e.pageY, stageX: stage.getX(), stageY: stage.getY()};
window.draggingStage = true;
}
});
$("#container").on("mousemove", function(e) {
if (window.draggingNode || !window.draggingStage) return false;
stage.setX(window.draggingStart.stageX+(e.pageX-window.draggingStart.x));
stage.setY(window.draggingStart.stageY+(e.pageY-window.draggingStart.y));
stage.draw();
});
$("#container").on("mouseup", function(e) { window.draggingStage = false } );
stage.add(layer);
http://jsfiddle.net/bighostkim/jsqJ2/
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.