I have a paper with 400 x 500 in size. I am trying to divide this into 20 x 20 pixels with below code
var dpH = 500, dpW = 400, drawPad = new Raphael(document.getElementById('p'),
dpW, dpH);
for ( var i = 0; i <= dpH / 20; i++) {
drawPad.path("M" + 1 + " " + ((i * 20) + 1) + "L" + dpW + " "
+ ((i * 20) + 1));
}
for ( var j = 0; j <= (dpW / 20); j++) {
drawPad.path("M" + ((j * 20) + 1) + " " + 1 + "L" + ((j * 20) + 1) + " "
+ dpH);
}
And HTML markup is like below
<div id="p" style="background-image:url(image.png)"> </div>
with same height and width of Background Image.
My original requirement was making the image.png as Rapheal paper. But I was failed to do that. So I made it as background-image to the DIV#P. Then converted the DIv to Paper.
Here are my questions related to above
Does all the pixels of Background-Image and DIV match with each other?
The way I did above is to classify the total paper into 20x20 pixel divisions. Is that correct way of doing?
What is the width of the drawn line?
Please help me on this.
Ok, so if I understand you correctly; What you really want is to get the raw image data for 20x20 squares of the image.
Here's how you can extract image data with Canvas (also on jsFiddle):
var dpH = 500,
dpW = 400,
ctx = document.getElementById('p').getContext('2d'),
exportData = function() {
var data;
for (var y=0, yl=dpH/20; y<yl; y++) {
for (var x=0, xl=dpW/20; x<xl; x++) {
imgData = ctx.getImageData(x*20, y*20, 20, 20).data;
console.log("Image data for " + x*20 + ", " + y*20, imgData);
// data is an array with 4 values pr pixel
// Top left pixel in the 20x20 square
r = imgData[0]; // red
g = imgData[1]; // green
b = imgData[2]; // blue
a = imgData[3]; // alpha
console.log("RGBa of " + x*20 + ", " + y*20 + ": ", r, g, b, a);
}
}
},
drawImage = function() {
ctx.drawImage(this, 0, 0);
exportData(this);
};
var img = new Image();
img.onload = drawImage;
img.src = "image.png"; // has to be on the same domain
** Original answer **
The result is a DIV with an SVG-element inside, and a background image behind it. The browser (if it supports SVG) will render them on top of each other. Do you want to extract pixel values? If so, you have to do this through HTML5 Canvas instead of SVG.
Sorry, I don't understand. What are you trying to accomplish? Do you want the pixel data for 20x20 squares? With Raphael you are just drawing lines on top of the picture.
The defaut with of a path is 1 pixels. You can change this by setting an attribute on the path. Example (also on jsfiddle.net):
var dpH = 500,
dpW = 400,
drawPad = Raphael(document.getElementById('p'), dpW, dpH),
style = {
"stroke" : "#fff", // white
"stroke-width" : 2 // default 1
};
for ( var i = 0; i <= dpH / 20; i++) {
drawPad.path("M" + 1 + " " + ((i * 20) + 1) + "L" + dpW + " "
+ ((i * 20) + 1)).attr(style);
}
for ( var j = 0; j <= (dpW / 20); j++) {
drawPad.path("M" + ((j * 20) + 1) + " " + 1 + "L" + ((j * 20) + 1) + " "
+ dpH).attr(style);
}
Related
I have a for loop that returns a decimal between 0 and 1. I'd like to make a curve that appears more like a rounded corner than it is now. I'd also like to have it start ramping up only after 0.25. I can't quite figure out how to do it with the math I have now. I'm using Math.log and a linear conversion function, but maybe I need something more related to a parabolic curve.
for (i = 1; i < 101; ++i) {
var dec = i / 100
if (dec >= 0.25) {
console.log("dec = " + dec);
var large = i * 100
var log = Math.log(large);
console.log("log = " + log);
var linCon = applyLinearConversion(log, 2.8, 9.2104, -2.7, 1)
console.log("linCon " + i + " = " + linCon);
document.getElementById("graph").innerHTML += "<div style='background-color:#000000; width:" + (linCon * 1000) + "px; height:5px;'></div>";
}
}
function applyLinearConversion(OldValue, OldMin, OldMax, NewMin, NewMax) {
OldRange = (OldMax - OldMin)
if (OldRange == 0)
NewValue = NewMin
else {
NewRange = (NewMax - NewMin)
NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
}
return NewValue
}
<div id="graph"></div>
I have it populating a div with more styled divs.
Mine is like this:
I want mine more like this:
You can use the formula of the half circle graph which is:
It results in the following graph:
Since you are using horizontal divs that are stacked vertically to draw the graph, the x and y coordinates will be reversed and the left quarter of the circle will be used from the above graph.
var width = 200; // the width of the graph
var height = 200; // the height of the graph
var xOffset = 0.25 * width; // the x offset at which the graph will start ramping up (this offset is added to the graph width)
var html = ""; // to accumulate the generated html before assigning it to innerHTML (it's not a good idea to constantly update innerHTML)
for (i = 1; i < 101; ++i) {
var x = 1 - i / 100; // the x coordinate, we are using the left side of the graph so x should be negative going from -1 to 0
var y = Math.sqrt(1 - x * x); // the y coordinate as per the formula (this will be used for the width)
html += "<div style='background-color:#000000; width:" + (xOffset + y * width) + "px; height:" + (height / 100) + "px;'></div>";
}
document.getElementById("graph").innerHTML = html;
<div id="graph"></div>
I'm attempting to generate a path around 2 circles that should follow them as I move them around. I've based this on an example that I found and built a prototype of what I'm expecting to achieve example
I've started including this in my application, but for some reason I can't seem to get the green path to draw around the correct positions, and I can't figure out why.
I've put together a code example to illustrate:
function generatePath(planet, moon, join) {
function distanceBetween(x1, y1, x2, y2) {
var a = (x2 - x1) * (x2 - x1);
var b = (y2 - y1) * (y2 - y1);
return Math.sqrt(a + b);
};
function circleYFromX(circle, x) {
return Math.sqrt(Math.pow(circle.r, 2) - Math.pow(x - circle.x, 2));
};
function calculateAngle(origin, point) {
var tan = (point.y - origin.y) / (point.x - origin.x);
var angle = Math.atan(tan) / Math.PI * 180 + 90;
if (point.x < origin.x) angle += 180;
return angle;
};
// Work out the distance between the moon and planet
var distance = distanceBetween(planet.x, planet.y, moon.x, moon.y);
var originDistance = planet.r - moon.r;
var distanceDiff = distance - originDistance;
if (distanceDiff < 1) {
distanceDiff = 1;
}
console.log(distance);
console.log(planet.r);
console.log(moon.r);
console.log(join.r);
console.log(planet.r + moon.r + 2 * join.r);
// Determine if the moon has moved out of the planet's gravitational pull
if (distance > 2 * join.r + planet.r + moon.r) {
return;
}
moon.h = 0;
moon.k = 0 - planet.r + moon.r - distanceDiff;
var triangleA = planet.r + join.r; // Side planet
var triangleB = moon.r + join.r; // Side moon
var triangleC = Math.abs(moon.k - 0); // Side c
var triangleP = (triangleA + triangleB + triangleC) / 2; // Triangle half perimeter
var triangleArea = Math.sqrt(triangleP * (triangleP - triangleA) * (triangleP - triangleB) * (triangleP - triangleC)); // Triangle area
var triangleH;
var triangleD;
if (triangleC >= triangleA) {
var triangleH = 2 * triangleArea / triangleC; // Triangle height
var triangleD = Math.sqrt(Math.pow(triangleA, 2) - Math.pow(triangleH, 2)); // Big circle bisection of triangleC
} else {
var triangleH = 2 * triangleArea / triangleA; // Triangle height
var triangleD = Math.sqrt(Math.pow(triangleC, 2) - Math.pow(triangleH, 2)); // Small circle bisection of triangleA
}
planet.tan = triangleH / triangleD;
planet.angle = Math.atan(planet.tan);
planet.sin = Math.sin(planet.angle);
planet.intersectX = planet.sin * planet.r;
planet.cos = Math.cos(planet.angle);
planet.intersectY = planet.cos * planet.r;
join.x = 0 + planet.sin * (planet.r + join.r);
join.y = 0 - planet.cos * (planet.r + join.r);
var coord1 = {
x: -planet.intersectX,
y: -planet.intersectY
};
var coord2 = {
x: planet.intersectX,
y: -planet.intersectY
}
moon.tan = (moon.k - join.y) / (moon.h - join.x);
moon.angle = Math.atan(moon.tan);
moon.intersectX = join.x - Math.cos(moon.angle) * (join.r);
moon.intersectY = join.y - Math.sin(moon.angle) * (join.r);
// If we have any bad values then just return no path
if (isNaN(coord1.x) || isNaN(coord1.y) || isNaN(coord2.x) || isNaN(coord2.y)) {
return;
}
var pathD = "M " + coord1.x + " " + coord1.y + " A " + planet.r + " " + planet.r + " 0 1 0 " + coord2.x + " " + coord2.y;
if (join.x - join.r <= 0 && moon.k < join.y) {
var crossOverY = circleYFromX(join, 0);
pathD += "A " + join.r + " " + join.r + " 0 0 1 0 " + (join.y + crossOverY);
pathD += "m 0 -" + (crossOverY * 2);
}
pathD += "A " + join.r + " " + join.r + " 0 0 1 " + moon.intersectX + " " + moon.intersectY;
var largeArcFlag = 1;
if (join.y < moon.k) {
largeArcFlag = 0;
}
pathD += "a " + moon.r + " " + moon.r + " 0 " + largeArcFlag + " 0 " + (moon.intersectX * -2) + " 0";
if (join.x - join.r <= 0 && moon.k < join.y) {
pathD += "A " + join.r + " " + join.r + " 0 0 1 0 " + (join.y - crossOverY);
pathD += "m 0 " + (crossOverY * 2);
}
pathD += "A " + join.r + " " + join.r + " 0 0 1 " + coord1.x + " " + coord1.y;
pathD += "A " + join.r + " " + join.r + " 0 0 1 " + coord1.x + " " + coord1.y;
return pathD;
};
var container = d3.select(".planet");
var moon = d3.select(".moon");
var tempPlanet = { x: -181.77581967381693, y: -144.9613789321555, r: 152 };
var tempMoon = { x: 0, y: 0, r: 32 };
var link = { r: 7.9 };
var pathD = generatePath(tempPlanet, tempMoon, { r: 31 });
if (pathD) {
moon.append("path")
.attr("d", pathD)
.attr("transform", "translate(" + [-181.77581967381693, 144.9613789321555] + ")")
.attr("class", "gravity")
.style("fill", "none")
.style("stroke", "red")
.style("stroke-linecap", "round")
.style("stroke-width", 2);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="1680" height="523">
<g width="1680" height="523">
<g class="galaxy-main" width="1680" height="523">
<g class="planet selected" transform="translate(341,300) scale(0.5,0.5)">
<circle r="150" style="fill: rgb(72, 119, 159); stroke-dasharray: 944.477796076938px; stroke-dashoffset: 0px; stroke-width: 8px; stroke: rgb(255, 255, 255);"></circle>
<g class="moon" transform="translate(181.77581967381693,-144.96137893215555)">
<circle r="30" class="moon-circle" id="3" style="fill: rgb(72, 119, 159);"></circle>
</g>
</g>
</g>
</g>
</svg>
Instead of drawing a green fill I'm currently drawing a red outline. What you should be able to see is that the red outline correctly surrounds the larger circle (planet) but goes vertically up instead of around the smaller circle (moon).
It appears as thought we're just missing a rotation, but the original prototype I built doesn't know about a rotation, just the center of each circle. In this case this should be really simple:
Moon
var tempMoon = { x: 0, y: 0, r: 32 };
Always located at (0, 0) as this circle sits in the center of the group which the path will be appended to
Planet
var tempPlanet = { x: -181.77581967381693, y: -144.9613789321555, r: 152 };
The planet is at the center of the group, which also contains the moon group. Therefore it's location is always just an inverse translation which positions the group containing the moon
I believe the locations are correct (I've tried adding circles on the moon layer to confirm they are in the correct place - which they are). I feel that this must be somehow down to the groups but I still can't pinpoint why this isn't rendering with the correct orientation.
Unfortunately it seems that I was missing a translate and rotate in the code toward the end that I'd missed previously for some reason. Including this in made it work as expected.
I have 2 coordinates x and y of a point. I want to calculate the angle between three points, say A,B,C.
Now for the B point I do not have a pixel which contains the 2 coordinates instead I have the pixel, how can I get a single pixel which I can use in my formula.
function find_angle(A,B,C) {
var AB = Math.sqrt(Math.pow(B.x-A.x,2)+ Math.pow(B.y-A.y,2));
var BC = Math.sqrt(Math.pow(B.x-C.x,2)+ Math.pow(B.y-C.y,2));
var AC = Math.sqrt(Math.pow(C.x-A.x,2)+ Math.pow(C.y-A.y,2));
var abc = (BC*BC)+ (AB*AB)-(AC*AC);
var x = abc/(2*BC*AB);
var Angle = FastInt((Math.acos(x) * 180/3.14159));
document.getElementById("Angle").value = Angle;
}
How to proceed with this.
A is changing every time I move the point and I have the updated coordinates as well but I am not able to get the whole pixel I can use in the formula to calculate the new angle.
If I understand what you are asking - you want to create a calculator for the angle formed between
3 dots (A, B middle, C).
Your function should work for the final calculation but you need to recall the function every time
a point has moved.
I created a nice fiddle to demonstrate how you can achieve it with : jQuery, jQuery-ui, html.
I used the draggable() plugin of the UI library to allow the user to manually drag the dots around
And I'm recalculating the angle while dragging.
Take a look: COOL DEMO JSFIDDLE
The CODE ( you will find all HTML & CSS in the demo):
$(function(){
//Def Position values:
var defA = { top:20, left:220 };
var defB = { top:75, left:20 };
var defC = { top:200, left:220 };
//Holds the degree symbol:
var degree_symbol = $('<div>').html('゜').text();
//Point draggable attachment.
$(".point").draggable({
containment: "parent",
drag: function() {
set_result(); //Recalculate
},
stop: function() {
set_result(); //Recalculate
}
});
//Default position:
reset_pos();
//Reset button click event:
$("#reset").click(function(){ reset_pos(); });
//Calculate position of points and updates:
function set_result() {
var A = get_middle("A");
var B = get_middle("B");
var C = get_middle("C");
angle = find_angle(A,B,C);
$("#angle").val(angle + degree_symbol);
connect_line("AB");
connect_line("CB");
}
//Angle calculate:
function find_angle(A,B,C) {
var AB = Math.sqrt(Math.pow(B.x-A.x,2)+ Math.pow(B.y-A.y,2));
var BC = Math.sqrt(Math.pow(B.x-C.x,2)+ Math.pow(B.y-C.y,2));
var AC = Math.sqrt(Math.pow(C.x-A.x,2)+ Math.pow(C.y-A.y,2));
radians = Math.acos((BC*BC+AB*AB-AC*AC)/(2*BC*AB)); //Radians
degree = radians * (180/Math.PI); //Degrees
return degree.toFixed(3);
}
//Default position:
function reset_pos() {
$("#A").css(defA);
$("#B").css(defB);
$("#C").css(defC);
set_result();
}
//Add lines and draw them:
function connect_line(points) {
var off1 = null;
var offB = get_middle("B");
var thickness = 4;
switch (points) {
case "AB": off1 = get_middle("A"); break;
case "CB": off1 = get_middle("C"); break;
}
var length = Math.sqrt(
((offB.x-off1.x) * (offB.x-off1.x)) +
((offB.y-off1.y) * (offB.y-off1.y))
);
var cx = ((off1.x + offB.x)/2) - (length/2);
var cy = ((off1.y + offB.y)/2) - (thickness/2);
var angle = Math.atan2((offB.y-off1.y),(offB.x-off1.x))*(180/Math.PI);
var htmlLine = "<div id='" + points + "' class='line' " +
"style='padding:0px; margin:0px; height:" + thickness + "px; " +
"line-height:1px; position:absolute; left:" + cx + "px; " +
"top:" + cy + "px; width:" + length + "px; " +
"-moz-transform:rotate(" + angle + "deg); " +
"-webkit-transform:rotate(" + angle + "deg); " +
"-o-transform:rotate(" + angle + "deg); " +
"-ms-transform:rotate(" + angle + "deg); " +
"transform:rotate(" + angle + "deg);' />";
$('#testBoard').find("#" + points).remove();
$('#testBoard').append(htmlLine);
}
//Get Position (center of the point):
function get_middle(el) {
var _x = Number($("#" + el).css("left").replace(/[^-\d\.]/g, ''));
var _y = Number($("#" + el).css("top").replace(/[^-\d\.]/g, ''));
var _w = $("#" + el).width();
var _h = $("#" + el).height();
return {
y: _y + (_h/2),
x: _x + (_w/2),
width: _w,
height: _h
};
}
});
This Code requires jQuery & jQuery-UI. Don't forget to include them if you test it locally.
Have fun!
It's late and the part of my brain where Douglas Crockford lives is closed. Ive tried a few things but nothing's doing as expected.
I've got a canvas where I draw a 2 lines, then fade them out on a timer but only the last line in the loop is being faded out. Here's my fiddle, look down to line 50ish in the JS, to see it in action drag your mouse around in the bottom right pane:
http://jsfiddle.net/mRsvc/4/
this is the function, basically the timeout only gets the last value in the loop, I've seen this before and I'm sure if I wasn't so delirious it might be simpler. Here's the function in particular:
function update()
{
var i;
this.context.lineWidth = BRUSH_SIZE;
this.context.strokeStyle = "rgba(" + COLOR[0] + ", " + COLOR[1] + ", " + COLOR[2] + ", " + BRUSH_PRESSURE + ")";
for (i = 0; i < scope.painters.length; i++)
{
scope.context.beginPath();
var dx = scope.painters[i].dx;
var dy = scope.painters[i].dy;
scope.context.moveTo(dx, dy);
var dx1 = scope.painters[i].ax = (scope.painters[i].ax + (scope.painters[i].dx - scope.mouseX) * scope.painters[i].div) * scope.painters[i].ease;
scope.painters[i].dx -= dx1;
var dx2 = scope.painters[i].dx;
var dy1 = scope.painters[i].ay = (scope.painters[i].ay + (scope.painters[i].dy - scope.mouseY) * scope.painters[i].div) * scope.painters[i].ease;
scope.painters[i].dy -= dy1;
var dy2 = scope.painters[i].dy;
scope.context.lineTo(dx2, dy2);
scope.context.stroke();
for(j=FADESTEPS;j>0;j--)
{
setTimeout(function()
{
var x=dx,y=dy,x2=dx2,y2=dy2;
scope.context.beginPath();
scope.context.lineWidth=BRUSH_SIZE+1;
scope.context.moveTo(x, y);
scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
scope.context.lineTo(x2, y2);
scope.context.stroke();
scope.context.lineWidth=BRUSH_SIZE;
},
DURATION/j);
}
}
}
The problem is that the variables dx, dy, etc that you refer to in the function you pass to setTimeout() are defined in the surrounding scope and by the time any of the timeouts actually runs these variables all hold the values from the last iteration of the loop(s).
You need to create an extra containing function to close over the values from each iteration. Try something like the following:
for(j=FADESTEPS;j>0;j--) {
(function(x,y,x2,y2) {
setTimeout(function() {
scope.context.beginPath();
scope.context.lineWidth=BRUSH_SIZE+1;
scope.context.moveTo(x, y);
scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
scope.context.lineTo(x2, y2);
scope.context.stroke();
scope.context.lineWidth=BRUSH_SIZE;
},
DURATION/j);
})(dx, dy, dx2, dy2);
}
This creates a new anonymous function for each iteration of the j=FADESTEPS loop, executing it immediately and passing the dx, etc. values as they were at the time each iteration of the loop ran, and moving the x, y, etc. variables out of your existing function and making them parameters of the new one so then by the time the timeout runs it will use the correct values.
You can try something like this:
`<script>
for(j=10;j>0;j--)
{
var fn = function(ind){return function()
{
console.log(ind);
};
}(j);
setTimeout(fn,
1000);
}
</script>`
Or another way (as soon as you do not use IE, but let it learn canvas at first :))
for(j=FADESTEPS;j>0;j--)
{
setTimeout(function(x,y,x2,y2)
{
scope.context.beginPath();
scope.context.lineWidth=BRUSH_SIZE+1;
scope.context.moveTo(x, y);
scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
scope.context.lineTo(x2, y2);
scope.context.stroke();
scope.context.lineWidth=BRUSH_SIZE;
},
DURATION/j,dx,dy,dx2,dy2);
}
ps: there is no need in set of extra functions (the reasons are clear)
First of all j is a global.
Second of all, you never close the paths that you begin, which can cause memory leaks. It seems really slow and this may be why. You need to call closePath() whenever you're done with the paths you start with beginPath()
Next, I think there's some general funniness with how this works. You're fading out by drawing over the last thing with white. I've done something similar to this before, but instead I cleared the whole screen and kept drawing things over and over again. It worked okay for me.
Explanation
The other answers about dx and dy being passed from the higher scope are the right answers though. Async functions defined in synchronous for loops will take the last version of the state.
for (var i = 0; i < 10; i++) setTimeout(function() { console.log(i)}, 10 )
10
10
// ...
I would suggest you to use an array and store the points avoiding setTimeOut call in a loop. Somewhat like this.
this.interval = setInterval(update, REFRESH_RATE);
var _points = [];
function update() {
var i;
this.context.lineWidth = BRUSH_SIZE;
this.context.strokeStyle = "rgba(" + COLOR[0] + ", " + COLOR[1] + ", " + COLOR[2] + ", " + BRUSH_PRESSURE + ")";
for (i = 0; i < scope.painters.length; i++) {
scope.context.beginPath();
var dx = scope.painters[i].dx;
var dy = scope.painters[i].dy;
scope.context.moveTo(dx, dy);
var dx1 = scope.painters[i].ax = (scope.painters[i].ax + (scope.painters[i].dx - scope.mouseX) * scope.painters[i].div) * scope.painters[i].ease;
scope.painters[i].dx -= dx1;
var dx2 = scope.painters[i].dx;
var dy1 = scope.painters[i].ay = (scope.painters[i].ay + (scope.painters[i].dy - scope.mouseY) * scope.painters[i].div) * scope.painters[i].ease;
scope.painters[i].dy -= dy1;
var dy2 = scope.painters[i].dy;
scope.context.lineTo(dx2, dy2);
scope.context.stroke();
_points.push([dx, dy, dx2, dy2]);
clear();
}
}
function clear(){
if(_points.length < FADESTEPS){
return;
}
var p = _points.shift();
if(!p){
return;
}
var x = p[0],
y = p[1],
x2 = p[2],
y2 = p[3];
scope.context.beginPath();
scope.context.lineWidth = BRUSH_SIZE + 1;
scope.context.moveTo(x, y);
scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
scope.context.lineTo(x2, y2);
scope.context.stroke();
scope.context.lineWidth = BRUSH_SIZE;
}
I know this is not exactly what you need, but I think this can be modified to get it.
I am working on a graphics demo using RaphaelJS and jQuery.SVG. Right now I have a dynamically created 8x8 grid of rectangles that users are able to click to select. Once two of the rects are selected, then the selected rects are supposed to switch places with each other (like in Bejeweled.)
I have gotten to the point where I can get the rects to swap, but they are not ending up in the correct positions. According to the code, I am swapping out the x and y coordinates of each selected rect in an animate function. The rects always end up "off the grid" in testing. The visual effect is pretty neat, but I really want the rects to stay lined up.
http://img407.imageshack.us/img407/2222/raphaelerror.jpg
http://imageshack.us/photo/my-images/593/raphaelerror2.jpg/
The debug strings that I am building tell me that the positions of the rects have not changed. Obviously that is not true...what is going on here?
Here's the relevant code for this issue:
// swapTiles
/*
Swaps two tiles on the board that have been set to 'selected' with an animation.
*/
function swapTiles(toSwap) {
var holdX = $(toSwap[0]).attr('x');
var holdY = $(toSwap[0]).attr('y');
// Pre-swap debugging
statusString += "<br><b>BEFORE SWAP</b><br>";
statusString += "<br>First cell: " + selectedCellNames[0];
statusString += "<br>2nd cell: " + selectedCellNames[1];
statusString += "<br>$(toSwap[0]).attr('x'): " + $(toSwap[0]).attr('x').toString();
statusString += "<br>$(toSwap[0]).attr('y'): " + $(toSwap[0]).attr('y').toString();
statusString += "<br>$(toSwap[1]).attr('x'): " + $(toSwap[1]).attr('x').toString();
statusString += "<br>$(toSwap[1]).attr('y'): " + $(toSwap[1]).attr('y').toString();
statusString += "<br>";
/*
This line is using jQuery SVG to grab the DOM elements. This is being used because trying to grab the elements through
raphael was returning undefined errors. There seems to be some DOM scope issue that is causing me to use this and the temp.node
workarounds in order to reference SVG objects created through raphael.
What did not work:
$(toSwap[0]).animate({ x: 50, y: 100, 'stroke-width': 3 }, 250, '<'); --raphael animate function
$(toSwap[0]).node.animate({ x: 50, y: 100, 'stroke-width': 3 }, 250, '<');
When I tried to access the tiles using jQuery and the cellName user-defined attribute, I got '0' added to my debug output.
*/
$(toSwap[0]).animate({ svgX: $(toSwap[1]).attr('x'), svgY: $(toSwap[1]).attr('y'), svgStrokeWidth: 2, svgStroke: "#000" }, 250);
$(toSwap[1]).animate({ svgX: holdX, svgY: holdY, svgStrokeWidth: 2, svgStroke: "#000" }, 250);
// Post-swap debugging
statusString += "<br><b>SWAP COMPLETE</b><br>";
statusString += "<br>$(toSwap[0]).attr('x'): " + $(toSwap[0]).attr('x').toString();
statusString += "<br>$(toSwap[0]).attr('y'): " + $(toSwap[0]).attr('y').toString();
statusString += "<br>$(toSwap[1]).attr('x'): " + $(toSwap[1]).attr('x').toString();
statusString += "<br>$(toSwap[1]).attr('y'): " + $(toSwap[1]).attr('y').toString();
statusString += "<br>";
}
// drawRect
/*
returns a Raphael rectangle object with the inputted options
color = the value that the color attr will be set to for this rect
x, y = x & y position of the top-left corner of the rect
w, h = width and height of the rect
corner = radius for the rounded corners
*/
function drawRect(color, x, y, w, h, corner) {
var temp = canvas.rect(x, y, w, h, corner);
temp.attr("fill", color);
temp.node.setAttribute("selected", "false"); // Will be used to check for selected tiles in main loop
temp.node.setAttribute("cellName", cellName); // Give this rect a name
// Add the mouseover/hover events
temp.node.onmouseover = function () {
temp.animate({ scale: 1.5, stroke: "#FFF", 'stroke-width': 3 }, 250, 'bounce').toFront();
}; // end temp.node.onmouseover
temp.node.onmouseout = function () {
if (temp.node.getAttribute("selected") == "true") {
temp.animate({ scale: 1, stroke: "#FFF", 'stroke-width': 4 }, 250, 'elastic').toFront();
}
else {
temp.animate({ scale: 1, stroke: "#000", 'stroke-width': 2 }, 250, 'elastic').toBack();
}
// Update the status debugging
statusString += "<br>(x: " + x.toString() + ", y: " + y.toString() + ") temp.node.getAttribute('selected') after .node.onmouseout = " + temp.node.getAttribute("selected");
statusString += "<br>selectedCount = " + selectedCount.toString();
statusString += "<br>cellName = " + temp.node.getAttribute("cellName");
statusString += "<br>selectedCells[0] = " + selectedCells[0].toString();
statusString += "<br>selectedCells[1] = " + selectedCells[1].toString();
$("#status").html(statusString);
statusString = "";
}; // end temp.node.onmouseout
// Toggle between selected / not-selected
temp.node.onclick = function () {
// if not selected yet
if (temp.node.getAttribute("selected") != "true") {
temp.animate({ scale: 1, stroke: "#FFF", 'stroke-width': 4 }, 250, 'elastic').toFront();
temp.node.setAttribute("selected", "true");
selectedCount += 1;
// Place the current cell into our array
if (selectedCount == 1) {
selectedCells[0] = temp.node;
selectedCellNames[0] = temp.node.getAttribute("cellName");
}
// We have two cells selected: time to swap
else if (selectedCount == 2) {
selectedCells[1] = temp.node;
selectedCellNames[1] = temp.node.getAttribute("cellName");
swapTiles(selectedCells);
// Clean up selectedCells and the selected attributes
/*
Since selectedCells already contains references to the temp.nodes of each SVG object,
use this format to set the attributes. No jQuery SVG call or additional .node is needed.
*/
selectedCells[0].setAttribute("selected", "false");
selectedCells[1].setAttribute("selected", "false");
selectedCells[0] = "";
selectedCells[1] = "";
selectedCellNames[0] = "";
selectedCellNames[1] = "";
selectedCount = 0;
}
else {
// Something went wrong
statusString += "<br><b>ERROR: selectedCells out of range!</b>";
}
}
// if already selected de-select
else {
temp.animate({ scale: 1, stroke: "#000", 'stroke-width': 2 }, 250, 'elastic').toBack();
temp.node.setAttribute("selected", "false");
selectedCount -= 1;
// Remove the current cell name from our array
if (selectedCount == 0) {
selectedCells[0] = "";
selectedCellNames[0] = "";
}
else if (selectedCount == 1) {
selectedCells[1] = "";
selectedCellNames[1] = "";
}
else {
// Something went wrong
statusString += "<br><b>ERROR: selectedCells out of range!</b>";
}
}
// Update the status debugging
statusString += "<br>(x: " + x.toString() + ", y: " + y.toString() + ") temp.node.getAttribute('selected') after .node.onclick = " + temp.node.getAttribute("selected");
statusString += "<br>selectedCount = " + selectedCount.toString();
statusString += "<br>selectedCells[0] = " + selectedCells[0].toString();
statusString += "<br>selectedCells[1] = " + selectedCells[1].toString();
$("#status").html(statusString);
statusString = "";
}; // end temp.node.onclick
return temp;
/*
NOTES:
temp.node.[function] - the node raphael property lets me directly access the DOM element that I just created
I will need to add all of my onclick and other direct access events for my tiles in this function.
I will get errors if I try to reference the DOM elements from another part of this program, probably because it is only
in this function that I actually create the DOM object. Once this function finishes, then the temp object reference is passed
back into my initialization loop, where at that point I cannot add any more DOM elements.
.animate - Raphael's animation function. Note that the properties that are animated are defined in the first argument,
between a set of {}. The second argument is the time in milliseconds that the animation will take.
.toFront() & .toBack() - These functions move the object to the top or bottom of the drawing stack. In other words, if I use
toFront on an object, it should be drawn on top of everything else, and vice-versa. This eliminates a graphical issue where the
tiles overrlap while expanded.
.node.setAttribute / .getAttribute(key, value) - These are the DOM functions to set and get attributes of specific DOM elements.
I am using these instead of .attr(attr, value) because I can't perform jQuery operations on the DOM elements at this point in the script.
*/
} // end drawRect
// Initialize the grid of rectangles
for (var i = 0; i < grid.length; i++) {
for (var j = 0; j < grid[i].length; j++) {
cellName = "cell " + i + ":" + j;
// drawRect(color, x, y, width, height, corner radius)
grid[i][j] = drawRect(colors[Math.floor(Math.random() * colors.length)], startx + (width * j), starty + (height * i), width, height, cornerRadius);
// used for debugging
debugString += "<br>grid[" + i + "][" + j + "] x: " + (startx + (width * j)) + " y: " + (starty + (height * i));
} // end inner for loop
} // end outer for loop
I'm not a Javascript expert so any help would be appreciated. Thanks!