I am trying to position a set of elements including text, path and circle in raphael. Is there a way to position the entire set in the svg? The SVG itself covers the entire height and width of the window and the set needs to be positioned in the center of the SVG.
The code is as follows:
var r = Raphael("holder", windowWidth, windowHeight);
var set = r.set(
(r.text(100,250,"ab").attr({...})),
(r.text(295,250,"c").attr({..})),
(r.path("M400,217L400,317").attr({...})),
(r.circle(190, 290, 13).attr({...})));
for (var i=0; i<set.length; i++) {
set[i].node.setAttribute("class", "set");
}
I've added the class in case the set could be manipulated using css, but I havent found any method to do so.
Any ideas on how to solve this problem?
You can do a transform on a set, like http://jsbin.com/EzUFiD/1/edit?html,js,output (fiddle isn't working for me)
var svgWidth = 600;
var svgHeight = 600;
var r = Raphael("holder", svgWidth, svgHeight);
var set = r.set(
(r.text(50,50,"ab")),
(r.text(50,60,"c")),
(r.path("M100,17L40,31")),
(r.circle(80, 80, 13))
);
var bbox = set.getBBox(); //find surrounding rect of the set
var posx = svgWidth / 2 - bbox.width / 2 - bbox.x; // find new pos depending on where it is initially
var posy = svgHeight / 2 - bbox.height / 2 - bbox.y;
set.transform('t' + posx + ',' + posy);
Related
I am generating 5 circles with a for loop in a canvas and I want to give them a class so I can control them with jquery, but I am doing something wrong. Can you guys figure out what's happening?
var stage;
var quantity = 6,
width = 60,
height = 60,
circles = [];
function init(){
stage = new createjs.Stage("myCanvas");
stage.width = 500;
stage.height = 600;
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", onTick);
setupGame();
}
function setupGame() {
for(var i = 0; i < quantity; i++) {
var circle = document.createElement("img");
circle.setAttribute('src', 'images/circles/circle'+i+'.png');
circle.className = "circle";
circle.style.position = "absolute";
circle.style.left = Math.floor((Math.random() * 100)) + "%";
circle.style.top = Math.floor((Math.random() * 100)) + "%";
circle.style.width = width + "px";
circle.style.height = height + "px";
document.body.appendChild(circle);
circles.push(circle);
}
}
function onTick(e){
stage.update(e);
}
NEW VERSION. With the help from JonnyD, I now have a functional loop. The only problem is that the images get appended to the body, and not to my stage. I have tried stage.appendChild(circle), but it's not working.
Here is a link to an online source so you guys can check it out = LINK
A lot is wrong with your code.
You are trying to add properties to strings within an array which is not possible. Properties are added to objects using dot or bracket notation..
Dot notation
foo.bar.baz
Square bracket notation
foo['bar']['baz']
What I think you want to do is create five circles on the 'screen' or more technically correct DOM (https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) at random positions with set H&W of 60px with classnames of myClass..
I have rewritten your code for you, you can remove the style javascript lines and add them in the CSS if you wish.. All you were really doing wrong was attempting to add properties to array values, wrong technique for the code and missing off .style before width, height. Note You add className's and width and height attributes to DOM elements only.
You can now access the individual circles through a for loop and the circles array or by using the nth-child selector with CSS. e.g .circle:nth-child(1) {animation/transition}
var quantity = 5,
width = 60,
height = 60
circles = [];
function setUp() {
for(var i = 0; i < quantity; i++) {
var circle = document.createElement("div");
circle.className = "circle";
circle.style.position = "absolute";
circle.style.left = Math.floor((Math.random() * 100)) + "%";
circle.style.top = Math.floor((Math.random() * 100)) + "%";
circle.style.backgroundColor = "black";
circle.style.width = width + "px";
circle.style.height = height + "px";
circles.push(circle);
document.body.appendChild(circle);
}
}
setUp();
.circle {
border-radius: 50%;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
}
I didn't see you were using CreateJS.. in that case using the notation like so is okay..
var circle = new createjs.Shape();
circle.graphics.beginFill("DeepSkyBlue").drawCircle(0, 0, 50);
circle.x = 100;
circle.y = 100;
stage.addChild(circle);
ensure that you update the stage as well.
stage.update();
I realize this question has been answered, but since I clicked on this for trying to find out how to add a class to a canvas object in jquery, I'll post how to do that.
var thing = canvas_object;
$('body').append(thing);
var canvas = $('canvas');
canvas.addClass('test');
Things inside canvas are not in DOM, but elements in Scalable Vector Graphics images are, and can be manipulated this way.
Try using SVG if convenient. svg.js is a lightweight library to manipulate SVG.
I want to display random numbers inside a div at random positions without overlapping.
I am able to display random number at random position but its going outside the box and overlapping each other.
Here is my code:
JS Fiddle
var width = $('.container').innerWidth();
var height = $('.container').innerHeight();
(function generate() { // vary size for fun
for (i = 0; i < 15; i++) {
var divsize = 12;
var color = '#' + Math.round(0xffffff * Math.random()).toString(16);
$newdiv = $('<div/>').css({
'width': divsize + 'px',
'height': divsize + 'px'
});
// make position sensitive to size and document's width
var posx = (Math.random() * (width - divsize)).toFixed();
var posy = (Math.random() * (height - divsize)).toFixed();
$newdiv.css({
'position': 'absolute',
'left': posx + 'px',
'top': posy + 'px',
'float': 'left'
}).appendTo('.container').html(Math.floor(Math.random() * 9));
}
})();
How can I do this?
You've got most of it figured out. You just need to think of the .container div as a grid to avoid any overlap or outlying items.
Just check out this fiddle.
Here's what the code looks like:
var tilesize = 18, tilecount = 15;
var gRows = Math.floor($(".container").innerWidth()/tilesize);
var gCols = Math.floor($('.container').innerHeight()/tilesize);
var vals = _.shuffle(_.range(tilecount));
var xpos = _.shuffle(_.range(gRows));
var ypos = _.shuffle(_.range(gCols));
_.each(vals, function(d,i){
var $newdiv = $('<div/>').addClass("tile");
$newdiv.css({
'position':'absolute',
'left':(xpos[i] * tilesize)+'px',
'top':(ypos[i] * tilesize)+'px'
}).appendTo( '.container' ).html(d);
});
PS:I have used underscore in my fiddle to make things easier for me and because I personally hate writing for loops.
If the number of divs you need to create is small enough (i.e. you're not risking that they won't fit) then a simple algorithm is:
pick a random position (x0, y0)-(x1, y1)
check if any previously selected rect overlaps
if none overlaps then add the rect, otherwise loop back and choose another random position
in code
var selected = [];
for (var i=0; i<num_divs; i++) {
while (true) {
var x0 = Math.floor(Math.random() * (width - sz));
var y0 = Math.floor(Math.random() * (height - sz));
var x1 = x0 + sz;
var y1 = y0 + sz;
var i = 0;
while (i < selected.length &&
(x0 >= selected[i].x1 ||
y0 >= selected[i].y1 ||
x1 <= selected[i].x0 ||
y1 <= selected[i].y0)) {
i++;
}
if (i == selected.length) {
// Spot is safe, add it to the selection
selected.push({x0:x0, y0:y0, x1:x1, y1:y1});
break;
}
// The choice collided with a previously added div
// just remain in the loop so a new attempt is done
}
}
In case the elements are many and it's possible to place n-1 of them so that there's no position where to put n-th element then things are a lot more complex.
For the solution of the 1-dimensional version of this problem see this answer.
You can add to array position of each number. And then when ou generate new position for digit you should check if posx posy in array, if false place number there, if true generate new posx and posy
I want to display random numbers inside a div at random positions without overlapping.
I am able to display random number at random position but its going outside the box and overlapping each other.
Here is my code:
JS Fiddle
var width = $('.container').innerWidth();
var height = $('.container').innerHeight();
(function generate() { // vary size for fun
for (i = 0; i < 15; i++) {
var divsize = 12;
var color = '#' + Math.round(0xffffff * Math.random()).toString(16);
$newdiv = $('<div/>').css({
'width': divsize + 'px',
'height': divsize + 'px'
});
// make position sensitive to size and document's width
var posx = (Math.random() * (width - divsize)).toFixed();
var posy = (Math.random() * (height - divsize)).toFixed();
$newdiv.css({
'position': 'absolute',
'left': posx + 'px',
'top': posy + 'px',
'float': 'left'
}).appendTo('.container').html(Math.floor(Math.random() * 9));
}
})();
How can I do this?
You've got most of it figured out. You just need to think of the .container div as a grid to avoid any overlap or outlying items.
Just check out this fiddle.
Here's what the code looks like:
var tilesize = 18, tilecount = 15;
var gRows = Math.floor($(".container").innerWidth()/tilesize);
var gCols = Math.floor($('.container').innerHeight()/tilesize);
var vals = _.shuffle(_.range(tilecount));
var xpos = _.shuffle(_.range(gRows));
var ypos = _.shuffle(_.range(gCols));
_.each(vals, function(d,i){
var $newdiv = $('<div/>').addClass("tile");
$newdiv.css({
'position':'absolute',
'left':(xpos[i] * tilesize)+'px',
'top':(ypos[i] * tilesize)+'px'
}).appendTo( '.container' ).html(d);
});
PS:I have used underscore in my fiddle to make things easier for me and because I personally hate writing for loops.
If the number of divs you need to create is small enough (i.e. you're not risking that they won't fit) then a simple algorithm is:
pick a random position (x0, y0)-(x1, y1)
check if any previously selected rect overlaps
if none overlaps then add the rect, otherwise loop back and choose another random position
in code
var selected = [];
for (var i=0; i<num_divs; i++) {
while (true) {
var x0 = Math.floor(Math.random() * (width - sz));
var y0 = Math.floor(Math.random() * (height - sz));
var x1 = x0 + sz;
var y1 = y0 + sz;
var i = 0;
while (i < selected.length &&
(x0 >= selected[i].x1 ||
y0 >= selected[i].y1 ||
x1 <= selected[i].x0 ||
y1 <= selected[i].y0)) {
i++;
}
if (i == selected.length) {
// Spot is safe, add it to the selection
selected.push({x0:x0, y0:y0, x1:x1, y1:y1});
break;
}
// The choice collided with a previously added div
// just remain in the loop so a new attempt is done
}
}
In case the elements are many and it's possible to place n-1 of them so that there's no position where to put n-th element then things are a lot more complex.
For the solution of the 1-dimensional version of this problem see this answer.
You can add to array position of each number. And then when ou generate new position for digit you should check if posx posy in array, if false place number there, if true generate new posx and posy
I am using Kinetic.js for this, but I figure this is not Kinetic specific. The problem is as follows:
I am loading an image in a canvas, and I then use Kinetic to rotate this image. How do I get the x and y of, for example, leftmost point of this image?
Try doing the calculations manually:
var theta = image.getRotation*Math.PI/180.;
// Find the middle rotating point
var midX = image.getX() + image.getWidth()/2;
var midY = image.getY() + image.getHeight()/2;
// Find all the corners relative to the center
var cornersX = [image.getX()-midX, image.getX()-midX, image.getX()+image.getWidth()-midX, image.getX()+image.getWidth()-midX];
var cornersY = [image.getY()-midY, image.getY()+image.getHeight()-midY, midY-image.getY(), image.getY()+image.getHeight()-midY];
// Find new the minimum corner X and Y by taking the minimum of the bounding box
var newX = 1e10;
var newY = 1e10;
for (var i=0; i<4; i=i+1) {
newX = min(newX, cornersX[i]*Math.cos(theta) - cornersY[i]*Math.sin(theta) + midX);
newY = min(newY, cornersX[i]*Math.sin(theta) + cornersY[i]*Math.cos(theta) + midY);
}
// new width and height
newWidth = midX - newX;
newHeight = midY - newY;
I'm trying to implement pinch-to-zoom gestures exactly as in Google Maps. I watched a talk by Stephen Woods - "Creating Responsive HTML5 Touch Interfaces” - about the issue and used the technique mentioned. The idea is to set the transform origin of the target element at (0, 0) and scale at the point of the transform. Then translate the image to keep it centered at the point of transform.
In my test code scaling works fine. The image zooms in and out fine between subsequent translations. The problem is I am not calculating the translation values properly. I am using jQuery and Hammer.js for touch events. How can I adjust my calculation in the transform callback so that the image stays centered at the point of transform?
The CoffeeScript (#test-resize is a div with a background image)
image = $('#test-resize')
hammer = image.hammer ->
prevent_default: true
scale_treshold: 0
width = image.width()
height = image.height()
toX = 0
toY = 0
translateX = 0
translateY = 0
prevScale = 1
scale = 1
hammer.bind 'transformstart', (event) ->
toX = (event.touches[0].x + event.touches[0].x) / 2
toY = (event.touches[1].y + event.touches[1].y) / 2
hammer.bind 'transform', (event) ->
scale = prevScale * event.scale
shiftX = toX * ((image.width() * scale) - width) / (image.width() * scale)
shiftY = toY * ((image.height() * scale) - height) / (image.height() * scale)
width = image.width() * scale
height = image.height() * scale
translateX -= shiftX
translateY -= shiftY
css = 'translateX(' + #translateX + 'px) translateY(' + #translateY + 'px) scale(' + scale + ')'
image.css '-webkit-transform', css
image.css '-webkit-transform-origin', '0 0'
hammer.bind 'transformend', () ->
prevScale = scale
I managed to get it working.
jsFiddle demo
In the jsFiddle demo, clicking on the image represents a pinch gesture centred at the click point. Subsequent clicks increase the scale factor by a constant amount. To make this useful, you would want to make the scale and translate computations much more often on a transform event (hammer.js provides one).
The key to getting it to work was to correctly compute the point of scale coordinates relative to the image. I used event.clientX/Y to get the screen coordinates. The following lines convert from screen to image coordinates:
x -= offset.left + newX
y -= offset.top + newY
Then we compute a new size for the image and find the distances to translate by. The translation equation is taken from Stephen Woods' talk.
newWidth = image.width() * scale
newHeight = image.height() * scale
newX += -x * (newWidth - image.width) / newWidth
newY += -y * (newHeight - image.height) / newHeight
Finally, we scale and translate
image.css '-webkit-transform', "scale3d(#{scale}, #{scale}, 1)"
wrap.css '-webkit-transform', "translate3d(#{newX}px, #{newY}px, 0)"
We do all our translations on a wrapper element to ensure that the translate-origin stays at the top left of our image.
I successfully used that snippet to resize images on phonegap, using hammer and jquery.
If it interests someone, i translated this to JS.
function attachPinch(wrapperID,imgID)
{
var image = $(imgID);
var wrap = $(wrapperID);
var width = image.width();
var height = image.height();
var newX = 0;
var newY = 0;
var offset = wrap.offset();
$(imgID).hammer().on("pinch", function(event) {
var photo = $(this);
newWidth = photo.width() * event.gesture.scale;
newHeight = photo.height() * event.gesture.scale;
// Convert from screen to image coordinates
var x;
var y;
x -= offset.left + newX;
y -= offset.top + newY;
newX += -x * (newWidth - width) / newWidth;
newY += -y * (newHeight - height) / newHeight;
photo.css('-webkit-transform', "scale3d("+event.gesture.scale+", "+event.gesture.scale+", 1)");
wrap.css('-webkit-transform', "translate3d("+newX+"px, "+newY+"px, 0)");
width = newWidth;
height = newHeight;
});
}
I looked all over the internet, and outernet whatever, until I came across the only working plugin/library - http://cubiq.org/iscroll-4
var myScroll;
myScroll = new iScroll('wrapper');
where wrapper is your id as in id="wrapper"
<div id="wrapper">
<img src="smth.jpg" />
</div>
Not a real answer, but a link to a plug=in that does it all for you. Great work!
https://github.com/timmywil/jquery.panzoom
(Thanks 'Timmywil', who-ever you are)
This is something I wrote a few years back in Java and recently converted to JavaScript
function View()
{
this.pos = {x:0,y:0};
this.Z = 0;
this.zoom = 1;
this.scale = 1.1;
this.Zoom = function(delta,x,y)
{
var X = x-this.pos.x;
var Y = y-this.pos.y;
var scale = this.scale;
if(delta>0) this.Z++;
else
{
this.Z--;
scale = 1/scale;
}
this.zoom = Math.pow(this.scale, this.Z);
this.pos.x+=X-scale*X;
this.pos.y+=Y-scale*Y;
}
}
The this.Zoom = function(delta,x,y) takes:
delta = zoom in or out
x = x position of the zoom origin
y = y position of the zoom origin
A small example:
<script>
var view = new View();
var DivStyle = {x:-123,y:-423,w:300,h:200};
function OnMouseWheel(event)
{
event.preventDefault();
view.Zoom(event.wheelDelta,event.clientX,event.clientY);
div.style.left = (DivStyle.x*view.zoom+view.pos.x)+"px";
div.style.top = (DivStyle.y*view.zoom+view.pos.y)+"px";
div.style.width = (DivStyle.w*view.zoom)+"px";
div.style.height = (DivStyle.h*view.zoom)+"px";
}
function OnMouseMove(event)
{
view.pos = {x:event.clientX,y:event.clientY};
div.style.left = (DivStyle.x*view.zoom+view.pos.x)+"px";
div.style.top = (DivStyle.y*view.zoom+view.pos.y)+"px";
div.style.width = (DivStyle.w*view.zoom)+"px";
div.style.height = (DivStyle.h*view.zoom)+"px";
}
</script>
<body onmousewheel="OnMouseWheel(event)" onmousemove="OnMouseMove(event)">
<div id="div" style="position:absolute;left:-123px;top:-423px;width:300px;height:200px;border:1px solid;"></div>
</body>
This was made with the intention of being used with a canvas and graphics, but it should work perfectly for normal HTML layout