Get the pixel from coordinates - javascript

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!

Related

Numbers that result in a more rounded corner when graphing in Javascript

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>

Javascript create SVG path bottom up vs top down

I have a page that shows a grid of job positions and I am showing the progression from one to another by using SVG + paths to draw the connection between boxes.
My code is working just fine when I am connecting an element at the top to one at the bottom. It is finding the XY of the top box and the XY of the bottom box and connects the two.
My issue is I want to flip this code and go from the bottom up. This means I need the top XY of the bottom element and the bottom XY of the top element and draw the path.
I have been trying to flip offsets around and basically do the opposite of what is working but I think my math is wrong somewhere.
Here is what the top down approach looks like. Works just fine.
The bottom up approach however is not correct. Theres some math errors somewhere and the calculations are causing the SVG to be cut off.
I believe the answer lies within the connectElements() function as that is where the coordinates are determined.
Any thoughts on how I can get these calculations corrected?
Fiddle: http://jsfiddle.net/Ly59a2hf/2/
JS Code:
function getOffset(el) {
var rect = el.getBoundingClientRect();
return {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
width: rect.width || el.offsetWidth,
height: rect.height || el.offsetHeight
};
}
function drawPath(svg, path, startX, startY, endX, endY) {
// get the path's stroke width (if one wanted to be really precize, one could use half the stroke size)
var style = getComputedStyle(path)
var stroke = parseFloat(style.strokeWidth);
// check if the svg is big enough to draw the path, if not, set heigh/width
if (svg.getAttribute("height") < endY) svg.setAttribute("height", endY);
if (svg.getAttribute("width") < (startX + stroke)) svg.setAttribute("width", (startX + stroke));
if (svg.getAttribute("width") < (endX + stroke * 3)) svg.setAttribute("width", (endX + stroke * 3));
var deltaX = (endX - startX) * 0.15;
var deltaY = (endY - startY) * 0.15;
// for further calculations which ever is the shortest distance
var delta = deltaY < absolute(deltaX) ? deltaY : absolute(deltaX);
// set sweep-flag (counter/clock-wise)
// if start element is closer to the left edge,
// draw the first arc counter-clockwise, and the second one clock-wise
var arc1 = 0;
var arc2 = 1;
if (startX > endX) {
arc1 = 1;
arc2 = 0;
}
// draw tha pipe-like path
// 1. move a bit down, 2. arch, 3. move a bit to the right, 4.arch, 5. move down to the end
path.setAttribute("d", "M" + startX + " " + startY +
" V" + (startY + delta) +
" A" + delta + " " + delta + " 0 0 " + arc1 + " " + (startX + delta * signum(deltaX)) + " " + (startY + 2 * delta) +
" H" + (endX - delta * signum(deltaX)) +
" A" + delta + " " + delta + " 0 0 " + arc2 + " " + endX + " " + (startY + 3 * delta) +
" V" + (endY - 30));
}
function connectElements(svg, path, startElem, endElem, type, direction) {
// Define our container
var svgContainer = document.getElementById('svgContainer'),
svgTop = getOffset(svgContainer).top,
svgLeft = getOffset(svgContainer).left,
startX,
startY,
endX,
endY,
startCoord = startElem,
endCoord = endElem;
console.log(svg, path, startElem, endElem, type, direction)
/**
* bottomUp - This means we need the top XY of the starting box and the bottom XY of the destination box
* topDown - This means we need the bottom XY of the starting box and the top XY of the destination box
*/
switch (direction) {
case 'bottomUp': // Not Working
// Calculate path's start (x,y) coords
// We want the x coordinate to visually result in the element's mid point
startX = getOffset(startCoord).left + 0.5 * getOffset(startElem).width - svgLeft; // x = left offset + 0.5*width - svg's left offset
startY = getOffset(startCoord).top + getOffset(startElem).height - svgTop; // y = top offset + height - svg's top offset
// Calculate path's end (x,y) coords
endX = endCoord.getBoundingClientRect().left + 0.5 * endElem.offsetWidth - svgLeft;
endY = endCoord.getBoundingClientRect().top - svgTop;
break;
case 'topDown': // Working
// If first element is lower than the second, swap!
if (startElem.offsetTop > endElem.offsetTop) {
var temp = startElem;
startElem = endElem;
endElem = temp;
}
// Calculate path's start (x,y) coords
// We want the x coordinate to visually result in the element's mid point
startX = getOffset(startCoord).left + 0.5 * getOffset(startElem).width - svgLeft; // x = left offset + 0.5*width - svg's left offset
startY = getOffset(startCoord).top + getOffset(startElem).height - svgTop; // y = top offset + height - svg's top offset
// Calculate path's end (x,y) coords
endX = endCoord.getBoundingClientRect().left + 0.5 * endElem.offsetWidth - svgLeft;
endY = endCoord.getBoundingClientRect().top - svgTop;
break;
}
// Call function for drawing the path
drawPath(svg, path, startX, startY, endX, endY, type);
}
function connectAll(direction) {
var svg = document.getElementById('svg1'),
path = document.getElementById('path1');
// This is just to help with example.
if (direction == 'topDown') {
var div1 = document.getElementById('box_1'),
div2 = document.getElementById('box_20');
} else {
var div1 = document.getElementById('box_20'),
div2 = document.getElementById('box_1');
}
// connect all the paths you want!
connectElements(svg, path, div1, div2, 'line', direction);
}
//connectAll('topDown'); // Works fine. Path goes from the bottom of box_1 to the top of box_20
connectAll('bottomUp'); // Doesn't work. I expect path to go from top of box_20 to the bottom of box_1
IMO, you can simplify things by making the SVG the exact right size. Ie. fit it between the two elements vertically, and have it start at the leftmost X coord.
If you do that, the path starts and ends at either:
X: 0 or svgWidth
Y: 0 or svgHeight.
Then as far as drawing the path goes, it's just a matter of using the relative directions (startX -> endX and startY -> endY) in your calculations. I've called these variables xSign and ySign. If you are consistent with those, everything works out correctly.
The last remaining complication is working out which direction the arcs for the rounded corners have to go - clockwise or anticlockwise. You just have to work out the first one, and the other one is the opposite.
function getOffset(el) {
var rect = el.getBoundingClientRect();
return {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
width: rect.width || el.offsetWidth,
height: rect.height || el.offsetHeight
};
}
function drawPath(svg, path, start, end) {
// get the path's stroke width (if one wanted to be really precise, one could use half the stroke size)
var style = getComputedStyle(path)
var stroke = parseFloat(style.strokeWidth);
var arrowHeadLength = stroke * 3;
var deltaX = (end.x - start.x) * 0.15;
var deltaY = (end.y - start.y) * 0.15;
// for further calculations which ever is the shortest distance
var delta = Math.min(Math.abs(deltaX), Math.abs(deltaY));
var xSign = Math.sign(deltaX);
var ySign = Math.sign(deltaY);
// set sweep-flag (counter/clock-wise)
// If xSign and ySign are opposite, then the first turn is clockwise
var arc1 = (xSign !== ySign) ? 1 : 0;
var arc2 = 1 - arc1;
// draw tha pipe-like path
// 1. move a bit vertically, 2. arc, 3. move a bit to the horizontally, 4.arc, 5. move vertically to the end
path.setAttribute("d", ["M", start.x, start.y,
"V", start.y + delta * ySign,
"A", delta, delta, 0, 0, arc1, start.x + delta * xSign, start.y + 2 * delta * ySign,
"H", end.x - delta * xSign,
"A", delta, delta, 0, 0, arc2, end.x, start.y + 3 * delta * ySign,
"V", end.y - arrowHeadLength * ySign].join(" "));
}
function connectElements(svg, path, startElem, endElem, type, direction) {
// Define our container
var svgContainer = document.getElementById('svgContainer');
// Calculate SVG size and position
// SVG is sized to fit between the elements vertically, start at the left edge of the leftmost
// element and end at the right edge of the rightmost element
var startRect = getOffset(startElem),
endRect = getOffset(endElem),
pathStartX = startRect.left + startRect.width / 2,
pathEndX = endRect.left + endRect.width / 2,
startElemBottom = startRect.top + startRect.height,
svgTop = Math.min(startElemBottom, endRect.top + endRect.height),
svgBottom = Math.max(startRect.top, endRect.top),
svgLeft = Math.min(pathStartX, pathEndX),
svgHeight = svgBottom - svgTop;
// Position the SVG
svg.style.left = svgLeft + 'px';
svg.style.top = svgTop + 'px';
svg.style.width = Math.abs(pathEndX - pathStartX) + 'px';
svg.style.height = svgHeight + 'px';
// Call function for drawing the path
var pathStart = {x: pathStartX - svgLeft, y: (svgTop === startElemBottom) ? 0 : svgHeight};
var pathEnd = {x: pathEndX - svgLeft, y: (svgTop === startElemBottom) ? svgHeight : 0};
drawPath(svg, path, pathStart, pathEnd);
}
function connectAll(direction) {
var svg = document.getElementById('svg1'),
path = document.getElementById('path1');
// This is just to help with example.
if (direction == 'topDown') {
var div1 = document.getElementById('box_1'),
div2 = document.getElementById('box_20');
} else {
var div1 = document.getElementById('box_20'),
div2 = document.getElementById('box_1');
}
// connect all the paths you want!
connectElements(svg, path, div1, div2, 'line');
}
//connectAll('topDown');
connectAll('bottomUp');
http://jsfiddle.net/93Le85tk/3/

Rotate evenly-distributed elements placed on a circle with jQuery

I have a JavaScript function which allows me to generate DOM elements and plot them on a circle with (good enough) even distribution around the circle. The code is as follows (I'm using jQuery):
function createFields(numberOfItems, className, radius) {
var container = $('#container');
for(var i = 0; i < +numberOfItems; i++) {
$('<div/>', {
'class': 'field ' + className,
'text': i + 1
}).appendTo(container);
}
var fields = $('.' + className),
container = $('#container'),
width = container.width(),
height = container.height(),
angle = 0,
step = (2*Math.PI) / fields.length;
fields.each(function() {
var x = Math.round(width/2 + radius * Math.cos(angle) - $(this).width()/2);
var y = Math.round(height/2 + radius * Math.sin(angle) - $(this).height()/2);
if(window.console) {
console.log($(this).text(), x, y);
}
$(this).css({
left: x + 'px',
top: y + 'px'
});
angle += step;
});
}
createFields(5, 'outer', 200);
createFields(4, 'inner', 120);
Fiddle: http://jsfiddle.net/z79gj8a7/
You'll notice that the generated elements begin at 90 degrees to the vertical. I'd like to plot them so that they begin at 0 degrees. Essentially, if you imagine this as a clock, I want to plot all of the items 3hrs earlier. I've tried modifying the angle in the script to -90 and also subtracting 90 from the angle += step line but it's not having the desired effect.
Could anyone who's better at maths than I suggest a way to get the elements to be plotted -90 degrees from where they are now? (I'm aware I could just rotate the #container but that seems like a hack as I'd have to rotate the elements to compensate to keep their content in the correct orientation).
Many thanks.
The script is working in radians not degrees :) here's what you want (I think) http://jsfiddle.net/z79gj8a7/1/
You need to shift the angle by pi/2
var x = Math.round(width/2 + radius * Math.cos(angle - (Math.PI/2)) - $(this).width()/2);
var y = Math.round(height/2 + radius * Math.sin(angle - (Math.PI/2)) - $(this).height()/2);
Or even better (having read the script properly) don't change the calculation of x and y but change the angle to start at -pi/2: http://jsfiddle.net/z79gj8a7/2/
angle = -Math.PI/2,
jsFiddle demo
function createFields(numberOfItems, className, radius) {
var container = $('#container'),
centerX = container.width()/2,
centerY = container.height()/2,
angle = 0;
for(var i = 0; i < +numberOfItems; i++) {
$('<div/>', {
'class': 'field ' + className,
'text': i + 1
}).appendTo(container);
}
var fields = $('.' + className),
tot = fields.length;
fields.each(function(i, e) {
var w2 = $(e).outerWidth(true)/2,
h2 = $(e).outerHeight(true)/2,
angle = 360/tot*i,
x = Math.round(centerX+radius * Math.sin(angle*Math.PI/180)),
y = Math.round(centerY+radius * -Math.cos(angle*Math.PI/180));
$(e).css({left:x-w2, top:y-h2}).text( i+1 );
});
}
createFields(5, 'outer', 200);
createFields(4, 'inner', 120);

Classifying a Raphael paper into 20x20 divisions

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);
}​

javascript variable scope/closure in loop after timeout

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.

Categories