Rotating a clock hand in javascript - javascript

I am learning to make a clock using raphael js,
I am using this tutorial to get me started http://www.tuttoaster.com/creating-a-clock-animation-without-css3/
When this is diplayed the second hand doesnt move one second per second. I know one second is 6 degrees, it moves around 45 degrees though!
If someone could please explain what he has done wrong and how to make the hands rotate at appropriate angles that would be great. I am a beginner so plain english please :)
The code is as follows.
window.onload = function(){
var canvas = Raphael("pane",0,0,500,500);
canvas.circle(200,150,100).attr("stroke-width",2);
canvas.circle(200,150,3).attr("fill","#000");
var angleplus = 360,rad = Math.PI / 180,
cx = 200,cy =150 ,r = 90,
startangle = -90,angle=30,x,y, endangle;
for(i=1;i<13;i++)
{
endangle = startangle + angle ;
x = cx + r * Math.cos(endangle * rad);
y = cy + r * Math.sin(endangle * rad);
canvas.text(x,y,i+"");
startangle = endangle;
}
var hand = canvas.path("M200 70L200 150").attr("stroke-width",1);
var minute_hand = canvas.path("M200 100L200 150").attr("stroke-width",2);
var hour_hand = canvas.path("M200 110L200 150").attr("stroke-width",3);
var time = new Date();
angle = time.getSeconds() * 6;
minute_hand.rotate(6 * time.getMinutes(),200,150);
var hr = time.getHours();
if(hr>12)
hr = hr -11;
hour_hand.rotate(30 * hr,200,150);
var minute_angle= 6 + time.getMinutes()*6,hour_angle=0.5+
time.getMinutes()*30;
setInterval(function(){
angle = angle + 6;
if(angle>=360)
{
angle=0;
minute_hand.rotate(minute_angle,200,150);
minute_angle = minute_angle + 6;
hour_hand.rotate(hour_angle,200,150);
hour_angle = hour_angle + 0.5;
}
if(minute_angle>=360)
{
minute_angle=0;
}
hand.rotate(angle,200,150);
},1000);

hand.rotate(6,200,150);
Bernard, you don't need to rotate by the variable angle since you're simply rotating by 6 degrees every second regardless of how many seconds have elapsed.
http://jsbin.com/domoqojipe/1/

So you want to speed up the clock speed by twenty?
It's a long shot, but try changing the 1000 at the bottom to 50. Because 1000 divided by 20 equals 50.
Try that and see if it works...

Related

Drawing animated lines

I'm trying to make the Logo Turtle in HTML 5 with javascript and canvas (I want to study simple algorithms with my students, and i want to make easy instructions).
I successfully made basic instructions, but the lines appear all at the same time, and i want to see them appear one after one.
Here is my code :
var dessin = document.getElementById("dessin")
var ctx = dessin.getContext("2d");
var angle = 0; // angle en degrés
// on donne les coordonnées de départ
var x = dessin.width / 2;
var y = dessin.height / 2;
function forward(distance) {
var iter = 1;
var Angle = angle % 360;
var theta = Angle / 180 * Math.PI;
var vitesse = 10;
var compteur = 1;
var timer = setInterval(function() {
ctx.beginPath();
ctx.moveTo(x, y);
x = Math.cos(theta) * distance / vitesse + x;
y = Math.sin(theta) * distance / vitesse + y;
ctx.lineTo(x, y);
ctx.stroke();
compteur++;
if (compteur > vitesse) {
clearInterval(timer);
}
}, 1000 / vitesse);
//setTimeout(clearInterval(timer),2000);
}
function turn_left(angle_rotation) {
angle = (angle - angle_rotation) % 360;
}
//Firing commands
turn_left(45);
forward(100);
turn_left(45);
forward(100);
<canvas id="dessin" width="400" height="400"></canvas>
I want to have two lines (a diagonal one, and a vertical one), but i have lot of it...
How can i do that ?
Thanks !
PS : I don't speak English very well, my apologies...
Your problem is your code is asynchrone. To achieve what you want to do, you need an animations manager.
Here, our animations manager is just two var : one is a boolean to know if we are moving, another is an array which accumulates queued animations:
var moveManager = [];
var isMoving = false;
I also make timer global because we have only one animation at a time :
var timer;
After you need to make the logic of your animations manager which is : If i'm not moving and I have a queued animation so play it :
function nextMove() {
if (!isMoving && moveManager.length) {
var move = moveManager.shift();
move();
}
}
And, last thing, each animation manages itself start, stop and call to next animation :
function turn_left(angle_rotation) {
moveManager.push(function() {
isMoving = true;
angle = (angle - angle_rotation) % 360 ;
isMoving = false;
nextMove();
});
}
With all this, you can continue your turtle.
Working jsFiddle => https://jsfiddle.net/y9efewqb/5/
PS : I use your code to explain how to make your turle but some part should be optimized (use requestAnimationFrame instead of using setInterval, make all this in classes to avoid global var, ...)

Rotating One Object Around Another In createJS/easelJS

In easelJS, what is the best way to rotate an object around another? What I'm trying to accomplish is a method to rotate the crosshair around the circle pictured below, just like a planet orbits the sun:
I've been able to rotate objects around their own center point, but am having a difficult time devising a way to rotate one object around the center point of a second object. Any ideas?
Might make sense to wrap content in a Container. Translate the coordinates so the center point is where you want it, and then rotate the container.
To build on what Lanny is suggesting, there may be cases where you don't want to rotate the entire container. An alternative would be to use trigonometric functions and an incrementing angle to calculate the x/y position of the crosshair. You can find the x/y by using an angle (converted to radians) and Math.cos(angleInRadians) for x and Math.sin(angleInRadians) for y, the multiply by the radius of the orbit.
See this working example for reference.
Here's a complete snippet.
var stage = new createjs.Stage("stage");
var angle = 0;
var circle = new createjs.Shape();
circle.graphics.beginFill("#FF0000").drawEllipse(-25, -25, 50, 50).endFill();
circle.x = 100;
circle.y = 100;
var crosshair = new createjs.Shape();
crosshair.graphics.setStrokeStyle(2).beginStroke("#FF0000").moveTo(5, 0).lineTo(5, 10).moveTo(0, 5).lineTo(10, 5).endStroke();
stage.addChild(circle);
stage.addChild(crosshair);
createjs.Ticker.addEventListener("tick", function(){
angle++;
if(angle > 360)
angle = 1;
var rads = angle * Math.PI / 180;
var x = 100 * Math.cos(rads);
var y = 100 * Math.sin(rads);
crosshair.x = x + 100;
crosshair.y = y + 100;
stage.update();
});
Put another point respect to origin point with the same direction
var one_meter = 1 / map_resolution;
// get one meter distance from pointed points
var extra_x = one_meter * Math.cos(temp_rotation);
var extra_y = one_meter * Math.sin(-temp_rotation);
var new_x = mapXY.x + extra_x;
var new_y = mapXY.y + extra_y;
var home_point = new createjs.Shape().set({ x: new_x, y: new_y });
home_point.graphics.beginFill("Blue").drawCircle(0, 0, 10);
stage.addChild(home_point);
stage.update();

JavaScript canvas, scale between two points on chart/graph?

So I've built a small graph application with JavaScript to help me practice using the canvas. I've spent the last 10 hours trying to scale between two points on the X-Axis and can't for the life of me figure it out. I've learned that to scale you need to translate > scale > translate. This works fine when I scale to the far left/right using the following type code.
let x = 0;
let y = this.getCanvasHeight() / 2;
this.getCanvasContext().clearRect(0, 0, this.getCanvas().width, this.getCanvas().height);
this.setCanvas();
ctx.translate(x, y);
ctx.scale(scale, 1);
ctx.translate(-x, -y);
this.resetCanvasLines();
this.renderGraph(this.state.points, scale);
This piece of code simply allows me to zoom into the far left of the graph. So now I'm trying to pick two points on this graph and zoom in on top of them, so that they fit evenly on the screen. The Y-Axis will always be the same.
My thinking was to get the midpoint between the two points and zoom in on that location, which I feel should work but I just can't get it working. My graph width is 3010px and split into 5 segments of 602px. I want to zoom let's say from x1 = 602 and x2 = 1806, which has the midpoint of 1204. Is there a technique to properly calculating the scale amount?
rangeFinder(from, to) {
let points = this.state.points;
if (points.length === 0) {
return;
}
let ctx = this.getCanvasContext();
let canvasWidth = this.getCanvasWidth();
let canvasHeight = this.getCanvasHeight() / 2;
let seconds = this.state.seconds;
let second = canvasWidth / seconds;
let scale = 1;
// My graph starts from zero, goes up to 5 and the values are to represent seconds.
// This gets the pixel value for the fromX value.
let fromX = from * second;
to = isNaN(to) ? 5 : to;
// Get the pixel value for the to location.
let toX = parseInt(to) * second;
let y = canvasHeight / 2;
// get the midpoint between the two points.
let midpoint = fromX + ((toX - fromX) / 2);
// This is where I really go wrong. I'm trying to calculate the scale amount
let zoom = canvasWidth - (toX - fromX);
let zoomPixel = (zoom / 10) / 1000;
let scaleAmount = scale + ((zoom / (canvasWidth / 100)) / 100) + zoomPixel;
ctx.clearRect(0, 0, this.getCanvas().width, this.getCanvas().height);
this.setCanvas();
// translate and scale.
ctx.translate(midpoint, y);
ctx.scale(scaleAmount, 1);
ctx.translate(-midpoint, -y);
this.resetCanvasLines();
this.renderGraph(points);
}
Any help would be great, thanks.
Scale = 5/3 = total width / part width.
After scale, x = 602 should have moved to 602 * 5/3 ~ 1000. Translate the new image by -1000. There is no need to find mid-point.

How to position these numbers around a clock?

Evidentially my trig is a bit rusty. How can I get the minutes to wrap around the clock?
codepen [update: forgot codepen isn't versioned like jsfiddle; this 'pen' is a work in progress and no longer represents the problem I had at the time of this question]
Javascript:
var r = $('.face').width()/2;
var j = r-18;
for(var min=0; min<60; min += 5) {
var str = min < 10 ? '0'+min : String(min);
var x = j*Math.sin(Math.PI*2*(min/60));
var y = j*Math.cos(Math.PI*2*(min/60));
console.log(x);
$('<div>',{'class':'time'})
.text(str)
.css({
marginLeft: (x+r-12)+'px',
marginTop: (-y+r-12)+'px'
})
.appendTo('.face');
}
All your .time divs are below one another. Give the .time class absolute positioning.
Assuming min is between 0-60, and bearing in mind that there are 360 degrees in a circle:
var degToMin = 360/60; // 6
var minDeg = min * degToMin; // this will tell us how many degrees around the circle the minutes are
var minRad = minDeg * (Math.PI/180); // convert to radians
var x = j*Math.cos(minRad); // cos is the x coord, while sin is the y coord
var y = j*Math.sin(minRad);
Going from 0 to 360 degrees (0 to 2 pi radians) normally starts at 3 o'clock, and goes counterclockwise.
So we need to reverse the direction so that it goes clockwise, and rotate it counterclockwise 90 degrees (pi/2 radians) so that it starts at 12 o'clock.
Each hour runs through pi/6 radians, so the position at hour h would be:
x = x_c + r cos (-h*pi/6 + pi/2)
y = y_c + r sin (-h*pi/6 + pi/2)
where the center of your clock is (x_c, y_c) and the radius of your clock is r.

How to bind a timer to a progress ring with JS?

I'm playing around with a progress ring, and I can't seem to get it to run on a timer. I am trying to make the progress ring propagate automatically and take, say, 0.5 seconds to go from 0% to whatever percent I set (65% in this case).
I used this progress ring as a base: http://llinares.github.io/ring-progress-bar/
This is my fiddle: http://jsfiddle.net/gTtGW/
I tried using a timer function, but I may not have been integrating that properly. In the fiddle, I have added:
for (var i = 0; i< 65; i++){
range += i;
setTimeout(timer,800);
}
However, this breaks the progress ring. I thought that any time the range is updated (with the += i), the draw function would be called. What am I doing wrong? Thank you so much in advance.
If you're not planning to use the input[type=range] element, you can change your code to this:
(function (window) {
'use strict';
var document = window.document,
ring = document.getElementsByTagName('path')[0],
range = 0,
text = document.getElementsByTagName('text')[0],
Math = window.Math,
toRadians = Math.PI / 180,
r = 100;
function draw() {
// Update the wheel giving to it a value in degrees, getted from the percentage of the input value a.k.a. (value * 360) / 100
var degrees = range * 3.5999,
// Convert the degrees value to radians
rad = degrees * toRadians,
// Determine X and cut to 2 decimals
x = (Math.sin(rad) * r).toFixed(2),
// Determine Y and cut to 2 decimals
y = -(Math.cos(rad) * r).toFixed(2),
// The another half ring. Same as (deg > 180) ? 1 : 0
lenghty = window.Number(degrees > 180),
// Moveto + Arcto
descriptions = ['M', 0, 0, 'v', -r, 'A', r, r, 1, lenghty, 1, x, y, 'z'];
// Apply changes to the path
ring.setAttribute('d', descriptions.join(' '));
// Update the numeric display
text.textContent = range;
range++;
if(range > 100) {
clearInterval(timer);
}
}
// Translate the center axis to a half of total size
ring.setAttribute('transform', 'translate(' + r + ', ' + r + ')');
var timer = setInterval(draw,100);
}(this));
Basically changing range to a simple variable starting at 0, and increasing its value every time draw() is called. Creating an interval (named timer) to run every 0.1 seconds in this case (of course it's up to you), and clearing that interval from draw() when appropriate...
JSFiddle Demo
I think you want something like:
function inc() {
var val = parseInt(range.value, 10)
range.value = val + 1;
draw(); // updating the value doesn't cause `onchange`.
if (val < 100) { // don't go over 100
setTimeout(inc, 100);
}
}
inc();

Categories