Raphael JS - Animating a semi-circle along its path - javascript

I have a gauge / dial type level to be animated in my app. The needle and dial will automatically get updated when values change. The code I have got to works OK for small changes, but when it has bigger changes the animation does not go along the path of the semi-circle.
The code I have is like this: (the interval is just for testing purposes)
var rad = Math.PI / 180;
var getCurvePath = function(cx, cy, r, startAngle, endAngle) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
return ["M", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2];
};
var zeroAngle = 197, fullAngle = -15,baseAngle = 199,
r = Raphael('level');
var level = r
.path(getCurvePath(150, 150, 75, zeroAngle, baseAngle))
.attr({
'stroke': '#FF0000',
'stroke-width': '11px'
});
window.setInterval(function() {
var ratio = Math.random();
var newLevelAngle = (zeroAngle - fullAngle) * ratio;
level.animate({
'path': getCurvePath(150, 150, 75, newLevelAngle, baseAngle)
}, 1000, 'bounce');
}, 2000);
I have set up a JS Fiddle here so you can see this in action: http://jsfiddle.net/Rfd3v/1/

The accepted answer here sorted my problem out:
drawing centered arcs in raphael js
I needed to adapt a few things from #genkilabs's answer to suit the level/gauge application. Hopefully this might be helpful to someone else:
var arcCentre = { x: 100, y: 100 },
arcRadius = 50,
arcMin = 1, // stops the arc disappearing for 0 values
baseRotation = -100, // this starts the arc from a different point
circleRatio = 220/360, // I found this helpful as my arc is never a full circle
maxValue = 1000; // arbitrary
// starts the arc at the minimum value
var myArc = r.path()
.attr({
"stroke": "#f00",
"stroke-width": 14,
arc: [arcCentre.x, arcCentre.y, arcMin, 100, arcRadius],
})
.transform('r'+ baseRotation + ',' + arcCentre.x + ',' + arcCentre.y);
// set a new value
var newValue = 234; // pick your new value
// convert value to ratio of the arc to complete
var ratio = newValue / maxValue;
// set level
var newLevelValue = Math.max(arcMin, ratio * 100 * circleRatio);
myArc.animate({
arc: [arcCentre.x, arcCentre.y, newLevelValue, 100, arcRadius]
}, 750, 'bounce');

Related

Continuous gradient along a HTML5 canvas path

I am trying to draw a continous gradient along a path of points, where each point has a it's own color, using the HTML5 canvas API.
See http://bl.ocks.org/rveciana/10743959 for inspiration, where that effect is achieved with D3.
There doesn't seem to be a way to add multiple linear gradients for a single canvas path, so I resorted to something like this: http://jsfiddle.net/51toapv2/
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var pts = [[100, 100, "red"], [150, 150, "green"], [200, 100, "yellow"]];
ctx.lineWidth = 20;
ctx.lineJoin = "round";
ctx.lineCap = "round";
for (var i = 0; i < pts.length - 1; i++) {
var begin = pts[i];
var end = pts[i + 1];
ctx.beginPath();
var grad = ctx.createLinearGradient(begin[0], begin[1], end[0], end[1]);
grad.addColorStop(0, begin[2]);
grad.addColorStop(1, end[2]);
ctx.strokeStyle = grad;
ctx.moveTo(begin[0], begin[1]);
ctx.lineTo(end[0], end[1]);
ctx.stroke();
}
As you can see it produces a subpar effect as the paths aren't merged and the "line joins" are clearly visible.
Is it possible to achieve the effect I'm looking for with the canvas API?
Here's a slight modification of your original idea that makes the joins blend nicely.
Original: Draw a gradient line from the start to end of a line segment.
This causes the line joins to overlap and produces a noticeable & undesired transition.
Modification: Draw a gradient line that doesn't extend to the start / endpoints.
With this modification, the line joins will always be solid colors rather than be partially gradiented. As a result, the line joins will transition nicely between line segments.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var lines = [
{x:100, y:050,color:'red'},
{x:150, y:100,color:'green'},
{x:200, y:050,color:'gold'},
{x:275, y:150,color:'blue'}
];
var linewidth=20;
ctx.lineCap='round';
ctx.lineJoint='round';
for(var i=1;i<lines.length;i++){
// calculate the smaller part of the line segment over
// which the gradient will run
var p0=lines[i-1];
var p1=lines[i];
var dx=p1.x-p0.x;
var dy=p1.y-p0.y;
var angle=Math.atan2(dy,dx);
var p0x=p0.x+linewidth*Math.cos(angle);
var p0y=p0.y+linewidth*Math.sin(angle);
var p1x=p1.x+linewidth*Math.cos(angle+Math.PI);
var p1y=p1.y+linewidth*Math.sin(angle+Math.PI);
// determine where the gradient starts and ends
if(i==1){
var g=ctx.createLinearGradient(p0.x,p0.y,p1x,p1y);
}else if(i==lines.length-1){
var g=ctx.createLinearGradient(p0x,p0y,p1.x,p1.y);
}else{
var g=ctx.createLinearGradient(p0x,p0y,p1x,p1y);
}
// add the gradient color stops
// and draw the gradient line from p0 to p1
g.addColorStop(0,p0.color);
g.addColorStop(1,p1.color);
ctx.beginPath();
ctx.moveTo(p0.x,p0.y);
ctx.lineTo(p1.x,p1.y);
ctx.strokeStyle=g;
ctx.lineWidth=linewidth;
ctx.stroke();
}
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=350 height=200></canvas>
You can do a simple approach interpolating two colors along a line. If you need smooth/shared gradients where two lines joins at steeper angles, you would need to calculate and basically implement a line drawing algorithm from (almost) scratch. This would be out of scope for SO, so here is a simpler approach.
That being said - the example in the link is not actually a line but several plots of squares of different colors. The issues it would have too is "hidden" by its subtle variations.
Example
This approach requires two main functions:
Line interpolate function which draws each segment in a line from previous mouse position to current position
Color interpolate function which takes an array of colors and interpolate between two current colors depending on length, position and segment size.
Tweak parameters such as segment size, number of colors in the array etc. to get the optimal result.
Line interpolate function
function plotLine(ctx, x1, y1, x2, y2) {
var diffX = Math.abs(x2 - x1), // get line length
diffY = Math.abs(y2 - y1),
dist = Math.sqrt(diffX * diffX + diffY * diffY),
step = dist / 10, // define some resolution
i = 0, t, b, x, y;
while (i <= dist) { // render circles along the line
t = Math.min(1, i / dist);
x = x1 + (x2 - x1) * t;
y = y1 + (y2 - y1) * t;
ctx.fillStyle = getColor(); // get current color
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI*2);
ctx.fill();
i += step;
}
Color interpolate function
function getColor() {
var r, g, b, t, c1, c2;
c1 = colors[cIndex]; // get current color from array
c2 = colors[(cIndex + 1) % maxColors]; // get next color
t = Math.min(1, total / segment); // calculate t
if (++total > segment) { // rotate segment
total = 0;
if (++cIndex >= maxColors) cIndex = 0; // rotate color array
}
r = c1.r + (c2.r - c1.r) * t; // interpolate color
g = c1.g + (c2.g - c1.g) * t;
b = c1.b + (c2.b - c1.b) * t;
return "rgb(" + (r|0) + "," + (g|0) + "," + (b|0) + ")";
}
Demo
Putting it all together will allow you to draw gradient lines. If you don't want to draw them manually simply call the plotLine() function whenever needed.
// Some setup code
var c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
colors = [
{r: 255, g: 0, b: 0},
{r: 255, g: 255, b: 0},
{r: 0, g: 255, b: 0},
{r: 0, g: 255, b: 255},
{r: 0, g: 0, b: 255},
{r: 255, g: 0, b: 255},
{r: 0, g: 255, b: 255},
{r: 0, g: 255, b: 0},
{r: 255, g: 255, b: 0},
],
cIndex = 0, maxColors = colors.length,
total = 0, segment = 500,
isDown = false, px, py;
setSize();
c.onmousedown = c.ontouchstart = function(e) {
isDown = true;
var pos = getPos(e);
px = pos.x;
py = pos.y;
};
window.onmousemove = window.ontouchmove = function(e) {if (isDown) plot(e)};
window.onmouseup = window.ontouchend = function(e) {
e.preventDefault();
isDown = false
};
function getPos(e) {
e.preventDefault();
if (e.touches) e = e.touches[0];
var r = c.getBoundingClientRect();
return {
x: e.clientX - r.left,
y: e.clientY - r.top
}
}
function plot(e) {
var pos = getPos(e);
plotLine(ctx, px, py, pos.x, pos.y);
px = pos.x;
py = pos.y;
}
function plotLine(ctx, x1, y1, x2, y2) {
var diffX = Math.abs(x2 - x1),
diffY = Math.abs(y2 - y1),
dist = Math.sqrt(diffX * diffX + diffY * diffY),
step = dist / 50,
i = 0,
t, b, x, y;
while (i <= dist) {
t = Math.min(1, i / dist);
x = x1 + (x2 - x1) * t;
y = y1 + (y2 - y1) * t;
ctx.fillStyle = getColor();
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI*2);
ctx.fill();
i += step;
}
function getColor() {
var r, g, b, t, c1, c2;
c1 = colors[cIndex];
c2 = colors[(cIndex + 1) % maxColors];
t = Math.min(1, total / segment);
if (++total > segment) {
total = 0;
if (++cIndex >= maxColors) cIndex = 0;
}
r = c1.r + (c2.r - c1.r) * t;
g = c1.g + (c2.g - c1.g) * t;
b = c1.b + (c2.b - c1.b) * t;
return "rgb(" + (r|0) + "," + (g|0) + "," + (b|0) + ")";
}
}
window.onresize = setSize;
function setSize() {
c.width = window.innerWidth;
c.height = window.innerHeight;
}
document.querySelector("button").onclick = function() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
};
html, body {background:#777; margin:0; overflow:hidden}
canvas {position:fixed;left:0;top:0;background: #333}
button {position:fixed;left:10px;top:10px}
<canvas></canvas>
<button>Clear</button>
TIPS:
The gradient values can be pre-populated / cached beforehand
The step for position in gradient can be bound to length to get even spread independent of draw speed
You can easily replace the brush with other path/figures/shapes, even combine image based brushes which is composited with current color

Snap SVG change line x,y coordinates using JavaScript

I want to change the x and y coordinates of the svg line, but it keeps drawing multiple lines itself over and over again. Here is the result: http://prntscr.com/6tdexj
How do I draw only one line every 1 second ?
var paper = Snap('#one');
var timer = setInterval(function() {my_var()}, 1000);
function my_var() {
x = Math.floor((Math.random() * 100) + 1);
y = Math.floor((Math.random() * 100) + 1);
console.log(x, y);
// SVG LINE
var line = paper.line(10, 10, x, y).attr({
fill: 'f44',
stroke: '#000'
});
};
You're creating a new line in each interval. You should create a single line and change its properties instead:
var paper = Snap('#one');
var line = paper.line(10, 10, 10, 10)
.attr({
fill: 'f44',
stroke: '#000'
})
setInterval(function() {
var x = Math.floor((Math.random() * 100) + 1),
y = Math.floor((Math.random() * 100) + 1);
line.attr({ x2: x, y2: y })
}, 1000);
You could make the line transition smoothly by calling line.animate instead of line.attr:
line.animate({ x2: x, y2: y }, 500);
http://jsbin.com/josuyo/2/edit

kineticjs performance lag

I am working on a radial control similar to the HTML5 wheel of fortune example. I've modified the original here with an example of some additional functionality I require: http://jsfiddle.net/fEm9P/ When you click on the inner kinetic wedges they will shrink and expand within the larger wedges. Unfortunately when I rotate the wheel it lags behind the pointer. It's not too bad here but it's really noticeable on a mobile.
I know this is due to the fact that I'm not caching the wheel. When I do cache the wheel (uncomment lines 239-249) the inner wedges no longer respond to mouse/touch but the response on rotation is perfect. I have also tried adding the inner wedges to a separate layer and caching the main wheel only. I then rotate the inner wheel with the outer one. Doing it this way is a little better but still not viable on mobile.
Any suggestions would be greatly appreciated.
Stephen
//constants
var MAX_ANGULAR_VELOCITY = 360 * 5;
var NUM_WEDGES = 25;
var WHEEL_RADIUS = 410;
var ANGULAR_FRICTION = 0.2;
// globals
var angularVelocity = 360;
var lastRotation = 0;
var controlled = false;
var target, activeWedge, stage, layer, wheel,
pointer, pointerTween, startRotation, startX, startY;
var currentVolume, action;
function purifyColor(color) {
var randIndex = Math.round(Math.random() * 3);
color[randIndex] = 0;
return color;
}
function getRandomColor() {
var r = 100 + Math.round(Math.random() * 55);
var g = 100 + Math.round(Math.random() * 55);
var b = 100 + Math.round(Math.random() * 55);
var color = [r, g, b];
color = purifyColor(color);
color = purifyColor(color);
return color;
}
function bind() {
wheel.on('mousedown', function(evt) {
var mousePos = stage.getPointerPosition();
angularVelocity = 0;
controlled = true;
target = evt.targetNode;
startRotation = this.rotation();
startX = mousePos.x;
startY = mousePos.y;
});
// add listeners to container
document.body.addEventListener('mouseup', function() {
controlled = false;
action = null;
if(angularVelocity > MAX_ANGULAR_VELOCITY) {
angularVelocity = MAX_ANGULAR_VELOCITY;
}
else if(angularVelocity < -1 * MAX_ANGULAR_VELOCITY) {
angularVelocity = -1 * MAX_ANGULAR_VELOCITY;
}
angularVelocities = [];
}, false);
document.body.addEventListener('mousemove', function(evt) {
var mousePos = stage.getPointerPosition();
var x1, y1;
if(action == 'increase') {
x1 = (mousePos.x-(stage.getWidth() / 2));
y1 = (mousePos.y-WHEEL_RADIUS+20);
var r = Math.sqrt(x1 * x1 + y1 * y1);
if (r>500){
r=500;
} else if (r<100){
r=100;
};
currentVolume.setRadius(r);
layer.draw();
} else {
if(controlled && mousePos && target) {
x1 = mousePos.x - wheel.x();
y1 = mousePos.y - wheel.y();
var x2 = startX - wheel.x();
var y2 = startY - wheel.y();
var angle1 = Math.atan(y1 / x1) * 180 / Math.PI;
var angle2 = Math.atan(y2 / x2) * 180 / Math.PI;
var angleDiff = angle2 - angle1;
if ((x1 < 0 && x2 >=0) || (x2 < 0 && x1 >=0)) {
angleDiff += 180;
}
wheel.setRotation(startRotation - angleDiff);
}
};
}, false);
}
function getRandomReward() {
var mainDigit = Math.round(Math.random() * 9);
return mainDigit + '\n0\n0';
}
function addWedge(n) {
var s = getRandomColor();
var reward = getRandomReward();
var r = s[0];
var g = s[1];
var b = s[2];
var angle = 360 / NUM_WEDGES;
var endColor = 'rgb(' + r + ',' + g + ',' + b + ')';
r += 100;
g += 100;
b += 100;
var startColor = 'rgb(' + r + ',' + g + ',' + b + ')';
var wedge = new Kinetic.Group({
rotation: n * 360 / NUM_WEDGES,
});
var wedgeBackground = new Kinetic.Wedge({
radius: WHEEL_RADIUS,
angle: angle,
fillRadialGradientStartRadius: 0,
fillRadialGradientEndRadius: WHEEL_RADIUS,
fillRadialGradientColorStops: [0, startColor, 1, endColor],
fill: '#64e9f8',
fillPriority: 'radial-gradient',
stroke: '#ccc',
strokeWidth: 2,
rotation: (90 + angle/2) * -1
});
wedge.add(wedgeBackground);
var text = new Kinetic.Text({
text: reward,
fontFamily: 'Calibri',
fontSize: 50,
fill: 'white',
align: 'center',
stroke: 'yellow',
strokeWidth: 1,
listening: false
});
text.offsetX(text.width()/2);
text.offsetY(WHEEL_RADIUS - 15);
wedge.add(text);
volume = createVolumeControl(angle, endColor);
wedge.add(volume);
wheel.add(wedge);
}
var activeWedge;
function createVolumeControl(angle, colour){
var volume = new Kinetic.Wedge({
radius: 100,
angle: angle,
fill: colour,
stroke: '#000000',
rotation: (90 + angle/2) * -1
});
volume.on("mousedown touchstart", function() {
currentVolume = this;
action='increase';
});
return volume;
}
function animate(frame) {
// wheel
var angularVelocityChange = angularVelocity * frame.timeDiff * (1 - ANGULAR_FRICTION) / 1000;
angularVelocity -= angularVelocityChange;
if(controlled) {
angularVelocity = ((wheel.getRotation() - lastRotation) * 1000 / frame.timeDiff);
}
else {
wheel.rotate(frame.timeDiff * angularVelocity / 1000);
}
lastRotation = wheel.getRotation();
// pointer
var intersectedWedge = layer.getIntersection({x: stage.width()/2, y: 50});
if (intersectedWedge && (!activeWedge || activeWedge._id !== intersectedWedge._id)) {
pointerTween.reset();
pointerTween.play();
activeWedge = intersectedWedge;
}
}
function init() {
stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 500
});
layer = new Kinetic.Layer();
wheel = new Kinetic.Group({
x: stage.getWidth() / 2,
y: WHEEL_RADIUS + 20
});
for(var n = 0; n < NUM_WEDGES; n++) {
addWedge(n);
}
pointer = new Kinetic.Wedge({
fillRadialGradientStartPoint: 0,
fillRadialGradientStartRadius: 0,
fillRadialGradientEndPoint: 0,
fillRadialGradientEndRadius: 30,
fillRadialGradientColorStops: [0, 'white', 1, 'red'],
stroke: 'white',
strokeWidth: 2,
lineJoin: 'round',
angle: 30,
radius: 30,
x: stage.getWidth() / 2,
y: 20,
rotation: -105,
shadowColor: 'black',
shadowOffset: {x:3,y:3},
shadowBlur: 2,
shadowOpacity: 0.5
});
// add components to the stage
layer.add(wheel);
layer.add(pointer);
stage.add(layer);
pointerTween = new Kinetic.Tween({
node: pointer,
duration: 0.1,
easing: Kinetic.Easings.EaseInOut,
y: 30
});
pointerTween.finish();
var radiusPlus2 = WHEEL_RADIUS + 2;
wheel.cache({
x: -1* radiusPlus2,
y: -1* radiusPlus2,
width: radiusPlus2 * 2,
height: radiusPlus2 * 2
}).offset({
x: radiusPlus2,
y: radiusPlus2
});
layer.draw();
// bind events
bind();
var anim = new Kinetic.Animation(animate, layer);
//document.getElementById('debug').appendChild(layer.hitCanvas._canvas);
// wait one second and then spin the wheel
setTimeout(function() {
anim.start();
}, 1000);
}
init();
I made a couple of changes to the script which greatly improved the response time. The first was replacing layer.draw() with layer.batchDraw(). As the draw function was being called on each touchmove event it was making the interaction clunky. BatchDraw on the other hand will stack up draw requests internally "limit the number of redraws per second based on the maximum number of frames per second" (http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-batch-draw).
The jumping around of the canvas I seeing originally when I cached/cleared the wheel was due to the fact that I wasn't resetting the offset on the wheel when I cleared the cache.
http://jsfiddle.net/leydar/a7tkA/5
wheel.clearCache().offset({
x: 0,
y: 0
});
I hope this is of benefit to someone else. It's still not perfectly responsive but it's at least going in the right direction.
Stephen

Raphael pie chart - draw paths anticlockwise

I'm trying to take the demo pie from the Raphael.js (http://raphaeljs.com/pie.html) and draw it clockwise - currently it's being drawn anticlockwise which doesn't make an awful lot of sense. This jsfiddle is the code from the demo:
http://jsfiddle.net/6bWuT/
On line 14 (beginning 'return paper.path' below) changing the second 0 to a 1 should change the rotation to clockwise but actually just screws the plot. Any help would be massively appreciated :) thanks in advance.
Code from the fiddle for reference:
Raphael.fn.pieChart = function (cx, cy, r, values, labels, stroke) {
var paper = this,
rad = Math.PI / 180,
chart = this.set();
function sector(cx, cy, r, startAngle, endAngle, params) {
var x1 = cx + r * Math.cos(-startAngle * rad),
x2 = cx + r * Math.cos(-endAngle * rad),
y1 = cy + r * Math.sin(-startAngle * rad),
y2 = cy + r * Math.sin(-endAngle * rad);
return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
}
var angle = 0,
total = 0,
start = 0,
process = function (j) {
var value = values[j],
angleplus = 360 * value / total,
popangle = angle + (angleplus / 2),
color = Raphael.hsb(start, .75, 1),
ms = 500,
delta = 30,
bcolor = Raphael.hsb(start, 1, 1),
p = sector(cx, cy, r, angle, angle + angleplus, {fill: "90-" + bcolor + "-" + color, stroke: stroke, "stroke-width": 3}),
txt = paper.text(cx + (r + delta + 55) * Math.cos(-popangle * rad), cy + (r + delta + 25) * Math.sin(-popangle * rad), labels[j]).attr({fill: bcolor, stroke: "none", opacity: 0, "font-size": 20});
p.mouseover(function () {
p.stop().animate({transform: "s1.1 1.1 " + cx + " " + cy}, ms, "elastic");
txt.stop().animate({opacity: 1}, ms, "elastic");
}).mouseout(function () {
p.stop().animate({transform: ""}, ms, "elastic");
txt.stop().animate({opacity: 0}, ms);
});
angle += angleplus;
chart.push(p);
chart.push(txt);
start += .1;
};
for (var i = 0, ii = values.length; i < ii; i++) {
total += values[i];
}
for (i = 0; i < ii; i++) {
process(i);
}
return chart;
};
$(function () {
var values = [],
labels = [];
$("tr").each(function () {
values.push(parseInt($("td", this).text(), 10));
labels.push($("th", this).text());
});
$("table").hide();
Raphael("holder", 700, 700).pieChart(350, 350, 200, values, labels, "#fff");
});
Oop, found it.. change the sector function:
function sector(cx, cy, r, startAngle, endAngle, params) {
var x1 = cx + r * Math.cos(startAngle * rad),
x2 = cx + r * Math.cos(endAngle * rad),
y1 = cy + r * Math.sin(startAngle * rad),
y2 = cy + r * Math.sin(endAngle * rad);
return paper.path(["M", cx, cy, "L", x1, y1, "A", r, r, 1, +(endAngle - startAngle > 180), 0, x2, y2, "z"]).attr(params);
}
The cos and sin are now being made to positive numbers instead of negative, and the 0 is changed to a 1 on the paper.path line.
Win! :)

Javascript - Rotating a rectangle before writing to canvas

I've been having an enormous amount of difficulty getting a set of rectangles to face a certain point on a canvas block. I'm iterating through a set of points, and drawing rectangles, which are supposed to all face a certain point.
First I set up the Radius, Focus point and Rectangle
var Angle = Math.PI;
_.each(Points, function(Point) {
var Radius = 10;
var Focus = {
X: 500,
Y: 1000
}
var Rect = [
{
X: (Point.X - Radius/2),
Y: (Point.Y - Radius/2)
},
{
X: (Point.X + Radius/2),
Y: (Point.Y - Radius/2)
},
{
X: (Point.X + Radius/2),
Y: (Point.Y + Radius/2)
},
{
X: (Point.X - Radius/2),
Y: (Point.Y + Radius/2)
}
];
Then, using underscore, I iterate through and used the method I read about on another response.
var index = 0;
_.each(Rect, function (V) {
var dx2 = Focus.X - V.X;
var dy2 = Focus.Y - V.Y;
var dx2_ = dx2 * Math.cos(Angle) - dy2 * Math.sin(Angle);
var dy2_ = dy2 * Math.sin(Angle) + dy2 * Math.cos(Angle);
V.X = dx2_ + V.X;
V.Y = dy2_ + V.Y;
Rect[index] = V;
index++;
});
Then its all set up in the normal canvas stuff. The context and the fillStyle are defined beforehand.
ctx.beginPath();
ctx.moveTo(Rect[0].X, Rect[0].Y);
ctx.lineTo(Rect[1].X, Rect[1].Y);
ctx.lineTo(Rect[2].X, Rect[2].Y);
ctx.lineTo(Rect[3].X, Rect[3].Y);
ctx.lineTo(Rect[0].X, Rect[0].Y);
ctx.fill();
});
These are the initial squares (without the rotation each loop)
Without rotation
This is what I get instead (seems like the rectangles are being rotated around the point instead of rotating towards the point)
With angle set to pi
The behavior that I'm trying to get is to have them facing towards the bottom middle. (500,1000) if the canvas is 1000x1000.
JSFiddle: http://jsfiddle.net/nmartin413/cMwTn/7/
I can't for the life of me figure out whats going wrong. If there's anything that stands out to you, please let me know :). Sorry for the long question, but this has really frustrated me.
Thanks in advance!
Try not to draw the rectangles yourself, use ctx.rotate(angle); and ctx.fillRect(-Radius/2, -Radius/2, width, height); as it is more simple, and code-wise - much easier to read and manage. To accomplish this rotation, save and redraw the canvas every time you apply a transformation to a rectangle, using ctx.save(); and ctx.restore();
JS:
$(document).ready(function() {
var Canvas = $('canvas')[0];
var Focus = {
X: 500,
Y: 1000
};
var f = Focus;
var angle = 0;
var Radius = 10;
_.each(Points, function($Point) {
var ctx = Canvas.getContext('2d');
var x = $Point.X;
var y = $Point.Y;
var width = Radius;
var height = Radius;
var cx = x - 0.5 * width;
var cy = y - 0.5 * height;
ctx.save();
ctx.translate(x,y);
var angle = (Math.atan2(f.Y-y,f.X-x));
ctx.rotate(angle);
ctx.fillStyle = "#ED1B24";
ctx.fillRect(-Radius/2, -Radius/2, width, height);
ctx.restore();
});
});
JSFiddle: http://jsfiddle.net/mJNas/

Categories

Resources