Animate Raphaël JS based on countdown value - similar to polar clock - javascript

First off, fairly new to JS but getting better :-)
This question is similar to drawing centered arcs in raphael js but a little different due to some specifics.
I am using jQuery Countdown to handle a countdown.
The 'seconds' on the timer need to be animated similar to the Polar Clock Example on the Raphaël demo page.
There is a callback 'onTick' which I think needs to be somehow 'plugged into' Raphaël.
Markup:
<div id="timer"></div>
<div id="pane"></div>
JS:
$(document).ready(function() {
$('#timer').countdown({
until: new Date(2011, 11, 11),
timezone: -5,
layout: '<ul>{d<}<li><em>{dn}</em> {dl}</li>{d>}{h<}<li><em>{hn}</em> {hl}</li>{h>}' +
'{m<}<li><em>{mn}</em> {ml}</li>{m>}{s<}<li><em>{sn}</em> {sl}</li>{s>}</ul>',
onTick: get_seconds
});
var archtype = Raphael("pane", 200, 100)
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [["M", xloc, yloc - R], ["A", R, R, 0, 1, 1, xloc - .01, yloc - R]];
} else {
path = [["M", xloc, yloc - R], ["A", R, R, 0, +(alpha > 180), 1, x, y]];
}
return {path: path};
};
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({"stroke": "#f00", "stroke-width": 6, arc: [50, 50, 0, 100, 30]});
my_arc.animate({arc: [50, 50, timer_radius, 100, 30]}, 1500, "bounce");
function get_seconds () {
var counter_seconds = $('#timer ul li:last em').text();
timer_radius = 100 - (counter_seconds * 1.66666667);
}});
Any help would be appreciated. Thank you!

Here was is final use (splash page will be active until Dec 11, 2011.): http://tomwahlin.com/
And the JavaScript I ended up with:
;(function() {
// ON DOCUMENT.ready fire the primary function
$(function(){ timer.init() })
var timer = {
//
// `create_arc` is used to assign an arc to the Raphael pane created in .init
//
create_arc: function(arch) {
arch.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [["M", xloc, yloc - R], ["A", R, R, 0, 1, 1, xloc - .01, yloc - R]];
} else {
path = [["M", xloc, yloc - R], ["A", R, R, 0, +(alpha > 180), 1, x, y]];
}
return {path: path};
};
},
//
// `get_arc_radius` returns the correct arc radius based on something
//
get_arc_radius: function() {
return ((($('#timer ul li:last em').text()) * 1.66666667));
},
//
// `update_based_on(type increment)
//
update_based_on: function(type, obj) {
var types = {
'seconds': 1000,
'minutes': 60000,
'hours': 3600000
}
var update = setInterval(function() {
obj.animate({arc: [100, 100, timer.get_arc_radius(), 100, 90]}, types[type]);
}, types[type])
},
//
// `create_pieces`
//
create_pieces: function() {
var archtype = Raphael("pane", 200, 200)
timer.create_arc(archtype)
//http://stackoverflow.com/questions/5061318/drawing-centered-arcs-in-raphael-js
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({"stroke": "#5ab4fc", "stroke-width": 18, arc: [100, 100, timer.get_arc_radius(), 100, 90]});
timer.update_based_on('seconds', my_arc)
},
//
// `init` kicks of the main behavior
//
init: function() {
$('#timer').countdown({
until: new Date(2011, 11, 11),
timezone: -5,
layout: '<ul>{d<}<li class="days"><em>{dn}</em> {dl}</li>{d>}{h<}<li class="hours"><em>{hn}</em> {hl}</li>{h>}' +
'{m<}<li class="minutes"><em>{mn}</em> {ml}</li>{m>}{s<}<li class="seconds"><em>{sn}</em> {sl}</li>{s>}</ul>',
onTick: function() {}
});
timer.create_pieces()
} // eo .init
}})();

Related

Using Angle in degrees to draw arrow in the direction in Javascript on Canvas

So I looked at the answer here: Draw arrow head in canvas using Angle
But it didn't seem to do it the way I wanted. I definitely do not want to use rotate, what I would like, is based on an Angle in degrees (0 being up on the screen, 180 being down, etc) is draw an arrow pointing in that direction.
Now I slept through trig in highschool so the correct usage of Rads, Sin and Cos are... well, they elude me :(.
Anyways, I have the angle already computed, and based on that I want to draw like the following:
The top one is at 0 degrees in my computation, the lower one 90 degrees.
I'm using a 2d canvas as my draw surface.
Inspired by the trigonometry of this answer I made a line + two smaller lines for the arrow part.
const size = 200;
var canvas = document.querySelector("canvas")
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, size, size);
ctx.strokeStyle = "red"
function lineToAngle(ctx, x1, y1, length, angle) {
angle = (angle - 90) * Math.PI / 180;
var x2 = x1 + length * Math.cos(angle),
y2 = y1 + length * Math.sin(angle);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.fill();
return {
x: x2,
y: y2
};
}
function draw_arrow(ctx, x1, y1, length, angle) {
var pos = lineToAngle(ctx, x1, y1, length, angle);
lineToAngle(ctx, pos.x, pos.y, 10, angle - 135);
lineToAngle(ctx, pos.x, pos.y, 10, angle + 135);
}
var pos = draw_arrow(ctx, 50, 50, 50, 30);
ctx.strokeStyle = "blue"
for (var angle = 0; angle <= 360; angle += 30) {
draw_arrow(ctx, 100, 100, 60, angle);
}
<canvas></canvas>
Overkill maybe?
This may help or may not.
Rather than create the arrow (shape) with a set of draw calls you can create a
Path2D to hold the shape with the helper function Path (in demo code below) that converts a set of arrays (each array a sub path) to a Path2D object. For example. const arrow = Path([[0, 0, 180, 0], [160, -10, 180, 0, 160, 10]]); a simple line arrow. To draw the path call ctx.stroke(arrow) or ctx.fill(arrow);
Define a style using Style eg const red = Style(...["#F00", , 2, "round"])
Position, rotate, and scale
Then the function drawPath(ctx, path, style, centerX, centerY, deg, scale) where...
ctx context to draw on.
path path to draw
style style to use,
centerX, centerY point to rotate around
deg angle in degrees from 0 deg pointing up, and positive values moving clockwize
scale lets you set the scale and defaults to 1.
Example use
// draw red arrow pointing up
const arrow = Path([[0, 0, 180, 0], [160, -10, 180, 0, 160, 10]]);
const red = Style(...["#F00", , 2, "round"]);
drawPath(ctx, arrow, red, 200, 200, 0);
Demo
Working example draw 6 arrows using 3 styles rotating around center of canvas.
const Path = (paths) => {
var j = 0, xx, yy, path = new Path2D();;
for (const subPath of paths) {
j = 0;
while (j < subPath.length) {
const [x, y] = [subPath[j++], subPath[j++]];
j === 2 ?
path.moveTo(...([xx, yy] = [x, y])) :
xx === x && yy === y ? path.closePath() : path.lineTo(x, y);
}
}
return path;
}
const Style = (strokeStyle, fillStyle, lineWidth, lineJoin = "bevel") => ({strokeStyle, fillStyle, lineWidth, lineJoin});
const styles = {
redLine: Style(...["#F00", , 6, "round"]),
greenFill: Style(...[ , "#0A0"]),
fillAndLine: Style(...["#000", "#0AF", 2, "round"]),
};
const paths = {
lineArrow: Path([ [10, 0, 180, 0], [160, -10, 180, 0, 160, 10] ]),
fatArrow: Path([ [10, -5, 180, -5, 170, -15, 200, 0, 170, 15, 180, 5, 10, 5, 10, -5] ]),
diamondArrow: Path([ [60, 0, 100, -15, 150, 0, 100, 15, 60, 0] ]),
};
requestAnimationFrame(mainLoop);
const [W, H, ctx] = [can.width, can.height, can.getContext("2d")];
const DEG2RAD = Math.PI / 180, DEG_0_OFFSET = -90;
function drawPath(ctx, path, style, centerX, centerY, deg, scale = 1) {
const rad = (deg + DEG_0_OFFSET) * DEG2RAD;
const [ax, ay] = [Math.cos(rad) * scale, Math.sin(rad) * scale];
ctx.setTransform(ax, ay, -ay, ax, centerX, centerY);
Object.assign(ctx, style);
style.fillStyle && ctx.fill(path);
style.strokeStyle && ctx.stroke(path);
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
function mainLoop(time) {
ctx.clearRect(0, 0, W, H);
drawPath(ctx, paths.fatArrow, styles.fillAndLine, W * 0.5, H * 0.5, (time / 15000) * 360, 0.5);
drawPath(ctx, paths.fatArrow, styles.greenFill, W * 0.5, H * 0.5, (time / 20000) * 360);
drawPath(ctx, paths.diamondArrow, styles.fillAndLine, W * 0.5, H * 0.5, (time / 30000) * 360, 0.5);
drawPath(ctx, paths.diamondArrow, styles.greenFill, W * 0.5, H * 0.5, (time / 60000) * 360);
drawPath(ctx, paths.lineArrow, styles.redLine, W * 0.5, H * 0.5, (time / 5000) * 360, 0.9);
drawPath(ctx, paths.lineArrow, styles.redLine, W * 0.5, H * 0.5, (time / 10000) * 360);
requestAnimationFrame(mainLoop);
}
<canvas id="can" width="400" height="400"></canvas>

AABB separating axis theorem, finding overlap depth

I want to detect collision between to aabb's. I want the overlap amount, penetration depth so that I can separate them with this info.
I am trying to understand the below code, It produces wrong results in my case: For example:
for aabb [ x, y, width, height ], [0, 780, 60, 60] and [0, 606, 120, 240] it produces xOverlap 60, yOverlap -23, instead it should have been yOverlap 6.
/*
* Collision Manifold
* https://gamedevelopment.tutsplus.com/tutorials/how-to-create-a-custom-2d-physics-engine-the-basics-and-impulse-resolution--gamedev-6\
331#:~:text=Collision%20resolution%20is%20the%20act,allow%20them%20to%20remain%20intersecting.&text=The%20idea%20behind%20impulse%20res\
olution,to%20separate%20objects%20found%20colliding.
*/
function aabbvsaabb(a, b) {
let normal,
penetration;
let n = [b.pos[0] - a.pos[0], b.pos[1] - a.pos[1]];
let aExtent = a.width / 2,
bExtent = b.width / 2;
let xOverlap, yOverlap;
xOverlap = aExtent + bExtent - Math.abs(n[0]);
xOverlap = Math.min(xOverlap, Math.min(a.width, b.width));
if (xOverlap > 0) {
aExtent = a.height / 2,
bExtent = b.height / 2;
yOverlap = aExtent + bExtent - Math.abs(n[1]);
yOverlap = Math.min(yOverlap, Math.min(a.height, b.height));
if (yOverlap > 0) {
if (xOverlap < yOverlap) {
normal = n[0] < 0 ? [-1, 0]: [1, 0];
penetration = xOverlap;
} else {
normal = n[1] < 0 ? [0, -1]:[0,1];
penetration = yOverlap;
}
}
}
return {
normal,
xOverlap,
yOverlap
};
};
function rect(x, y, w, h) {
return {
x, y, width: w, height: h,
pos: [x, y]
};
}
let manifold = aabbvsaabb(rect(0, 780, 60, 60),
rect(0, 606, 120, 240));
console.log(manifold);

draw Elliptical arc raphael

I am trying to draw an elliptical arc which can animate(bounce) using raphael. So far I have been following http://jsfiddle.net/jonhartmann/vm0etvz9/light/ to draw the arc. This is the output which I am getting from the link above.
But I need to get an output like an shell(below Image) starting the arc from 2nd quadrant with x=-30 and y=0(x can be anything on negative x axis depends on RADIUS and y should be 0).
I have attached my version of code to achieve the result.
http://jsfiddle.net/vssb7n25/
var archtype = Raphael('graph', 100, 100);
archtype.customAttributes.arc = function (value) {
var xloc = 50,
yloc = 50,
total = 100,
R = 30,
alpha = 180 / total * value,
a = (180 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc + R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}else{
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
It works fine but adds an extra arc on the bottom.
Could anyone help me out in drawing raphael elliptical arc.
Thank you
From the original sample you need to change starting and ending points of the arc.
The starting point (-1,0) becomes
["M", xloc - R, yloc],
And the ending point is
x = xloc - R * Math.sin(a),
y = yloc - R * Math.cos(a),

Fill outer-circle with Raphael JS

I’m trying to fill the border of a circle, depending on a variable, like so:
I found this example but it’s not what I want. I want the outer border to fill from bottom-up, on both sides of circle.
Here’s the code that I have:
var completion = '5%';
drawcircle("js-raphael-completion", completion);
function drawcircle(div, rate) {
var archtype = Raphael(document.getElementsByClassName(div)[0], "90%", "90%");
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
// inner ring
var circle = archtype.circle("50%", "50%", "40%").attr({
"stroke": "#2787d3",
"stroke-width": 1
});
// text
var text = archtype.text("95%", "50%", rate).attr({
'font-size': 14, 'font-family': 'Avenir',
"fill": "#2787d3"
});
// outer ring (filling)
var my_arc = archtype.path().attr({
"stroke": "#2787d3",
"stroke-width": 10,
arc: [100, 100, 0, 100, 50]
});
// animation of outer ring
my_arc.animate({
arc: [100, 100, 100, 100, 50]
}, 1000);
}
See the demo: http://jsfiddle.net/SUBn6/
Currently, because I’m not using percentages for the outer ring (filling), it’s not centred with the border circle (inner).
Another issue I have is with the number “5%”. It needs to follow the outer ring (filling) as it fills up from bottom to top…
Any ideas? Thanks.
It looks like Raphael doesn't have such a settings for stroke ending. So another approach to this would be to have two arcs and fill the space between them to simulate the stroke.
That involves a little more geometry :) but this should work:
var completion = '5%';
drawcircle("js-raphael-completion", completion);
// JSFiddle needs not onload
// window.onload = function () {
// drawcircle("js-raphael-completion", completion);
// };
function drawcircle(div, rate) {
var archtype = Raphael(document.getElementsByClassName(div)[0], "90%", "90%");
archtype.customAttributes.arc = function (xloc, yloc, value, total, R,lineWidth) {
var alpha = 360 / total * value,
a = value / total * Math.PI,
w = R * Math.sin(a),
h = R * Math.cos(a),
gamma = Math.asin(R / (R + lineWidth) * Math.sin(Math.PI / 2 + a)),
beta = Math.PI/2 - a - gamma,
xOffset = (R + lineWidth) * Math.sin(beta) / Math.sin(Math.PI / 2 + a),
x = xloc - R * Math.sin(a),
y = yloc + R * Math.cos(a),
path;
if (Math.abs(a - Math.PI/2) < 0.0001) {
xOffset = lineWidth;
}
if (total == value) {
path = [
["M", x, y],
["A", R , R , 1, 1, 1, x - 0.01, y],
["L", x - 0.01,y-lineWidth],
["A", R + lineWidth, R + lineWidth, 1, 1, 0, x, y - lineWidth],
["Z"]
/*
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
*/
];
} else {
path = [
["M", x, y],
["A", R , R , 0, +(alpha > 180), 0, x + 2 * w, y],
["L", x+ 2 * w + xOffset,y],
["A", R + lineWidth, R + lineWidth, 0 , +(alpha > 180) , 1, x - xOffset, y],
["Z"]
];
}
return {
path: path
};
};
// inner ring
var circle = archtype.circle(100, 100, 50).attr({
"stroke": "#2787d3",
"stroke-width": 3
});
// text
var text = archtype.text("95%", "50%", rate).attr({
'font-size': 14,
'font-family': 'Avenir',
"fill": "#2787d3"
});
// outer ring (filling)
var my_arc = archtype.path().attr({
"stroke": "#FF0000",
"fill": "#2787d3",
"stroke-width" : 0,
arc: [100, 100, 0, 100, 51,10]
});
// animation of outer ring
my_arc.animate({
arc: [100, 100, 100, 100, 51,10]
}, 1000);
}
See http://jsfiddle.net/Xp77x/2/
I think you messed around the cos and sin stuff :). Basically the starting point of the arc is variable depending on the ratio (xloc - R * sin(a), yloc + R * cos(a)), and the width is also variable (2 * R *sin(a)).
I modified a little bit your stuff:
var completion = '5%';
drawcircle("js-raphael-completion", completion);
// JSFiddle needs not onload
// window.onload = function () {
// drawcircle("js-raphael-completion", completion);
// };
function drawcircle(div, rate) {
var archtype = Raphael(document.getElementsByClassName(div)[0], "90%", "90%");
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = value / total * Math.PI,
w = R * Math.sin(a),
h = R * Math.cos(a),
x = xloc - R * Math.sin(a),
y = yloc + R * Math.cos(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", x, y],
["A", R, R, 0, +(alpha > 180), 0, x + 2 * w, y]
];
}
return {
path: path
};
};
// inner ring
var circle = archtype.circle(100, 100, 50).attr({
"stroke": "#2787d3",
"stroke-width": 1
});
// text
var text = archtype.text("95%", "50%", rate).attr({
'font-size': 14,
'font-family': 'Avenir',
"fill": "#2787d3"
});
// outer ring (filling)
var my_arc = archtype.path().attr({
"stroke": "#2787d3",
"stroke-width": 10,
arc: [100, 100, 0, 100, 50]
});
// animation of outer ring
my_arc.animate({
arc: [100, 100, 100, 100, 55]
}, 1000);
}
see: http://jsfiddle.net/Xp77x/1/
However I used absolute coordinates. To have relative coordinates you need to parse a little bit the input for % and translate this to a 0-100 canvas.

arcs in raphael js Draw like a gauge

My circle starts from 12 o clock. But I need to start from 7 and end at 5 . Always starts horizontally but I need to start vertically . Also how to add Effects like shadow etc.
var amount = ("80");
var archtype = Raphael("canvas", 600, 400);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
"stroke": "#f00",
"stroke-width": 14,
arc: [200, 200, 0, 100, 60]
});
my_arc.animate({
arc: [200, 200, amount, 100, 60]
}, 1500, "bounce");
i finally found it :D just added my_arc.rotate(215, 100 ,100)
<script >
var amount = ("50");
var archtype = Raphael("canvas", 300, 200);
archtype.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
//make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
var my_arc = archtype.path().attr({
"stroke": "#f00",
"stroke-width": 14,
arc: [100, 100, 0, 100, 60]
});
my_arc.rotate(215, 100 ,100).animate({
arc: [100, 100, 80, 100, 60]
}, 2000, "bounce");
Tiny update to make this work on IE8.
You need to remove the extra comma at the end of the path array else IE8 will give a JS error.
var arcseg = function (cx, cy, radius, start_r, finish_r) {
start_r = Raphael.rad(start_r);
finish_r = Raphael.rad(finish_r);
var start_x = cx + Math.cos( start_r ) * radius,
start_y = cy + Math.sin( start_r ) * radius,
finish_x = cx + Math.cos( finish_r ) * radius,
finish_y = cy + Math.sin( finish_r ) * radius,
path;
path = [
[ "M", start_x, start_y ],
[ "A", radius, radius, finish_r - start_r,
(finish_r - start_r > Raphael.rad( 180 )) ? 1 : 0,
(finish_r > start_r) ? 1 : 0,
finish_x, finish_y ]
];
return { path: path };
};

Categories