I need to draw a perfect curve / parabola in an html5 canvas. The 3 {x;y} coordinates of the points are given. I tried with a bezierCurveTo or a quadraticCurveTo, but the curve won't go in the middle point.
The result I want ( the blue doted curve, and the blue one ) : Wanted result
The result I have : Current result
Code :
ctx.strokeStyle = '#00478a';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(x1AtRatio, 30);
ctx.quadraticCurveTo(criticalSectionAtRatio, 100, x2AtRatio, 30);
ctx.stroke();
Where x1AtRatio - x2AtRatio - criticalSectionAtRatio are the given x by user input and 30 - 100 - 30 are the y
Control points define slopes
You will need to use two beziers as the curve will not pass through the control point.
The control points set the slopes of the curve out from the last point and in to the next point.
The example (below) draws a parabola using two curves. The function drawCurve()
I draw it twice once scaled and once normal.
Control points are blue.
The three points on the curve are red
The slopes are in green.
For example
const ctx = canvas.getContext("2d");
// drawScaled
ctx.setTransform(2,0,0,2,-50, -55);
drawAll();
// without scale
ctx.setTransform(1,0,0,1,-50, 0);
drawAll();
function drawCurve() {
ctx.beginPath();
ctx.strokeStyle = "#000";
ctx.moveTo(100 - 30, 30); // start left side
ctx.quadraticCurveTo(
100 - 30 / 2, 100, // controls point sets slope out from start and into center
100, 100 // center point
);
ctx.quadraticCurveTo(
100 + 30 / 2, 100, // control point sets slope out from center and into last point
100 + 30, 30 // last point
);
ctx.stroke();
}
function drawAll() {
// points on curve
drawPoint(100-30, 30);
drawPoint(100, 100);
drawPoint(100+30, 30);
// Control points
drawPoint(100 - 30 / 2, 100, "#00F");
drawPoint(100 + 30 / 2, 100, "#00F");
// Draw line through all points to show slopes
drawLine(100-30, 30, 100 - 30 / 2, 100);
drawLine(100 - 30 / 2, 100, 100, 100);
drawLine(100, 100, 100 + 30 / 2, 100);
drawLine(100 + 30 / 2, 100, 100 + 30, 30);
// Draw curve
drawCurve();
}
function drawPoint(x,y,col = "red") {
ctx.fillStyle = col;
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI * 2);
ctx.fill();
}
function drawLine(x,y, x1, y1, col = "#0A08") {
ctx.strokeStyle = col;
ctx.beginPath();
ctx.lineTo(x, y);
ctx.lineTo(x1, y1);
ctx.stroke();
}
canvas {
border: 1px solid black;
}
<canvas id="canvas"></canvas>
Related
I am creating an inner solar sytem model from the percpective of Earth for creating astology birthcharts references. I am able to draw an optical axis line for the Earth - Sun axis, and the Earth - Moon axis but I can not for the life of me figure the trigonometry for the remaining inner planets.
Here is a working pen.
https://codepen.io/pjdroopypants/pen/wvEKeOm
window.onload = function(){
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
cw = canvas.width,
ch = canvas.height,
time = 1;
function circle(radius,color,x,y){
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x,y,radius,0,2*Math.PI,true);
ctx.fill();
ctx.closePath();
}
function line(color,ax,ay,bx,by){
ctx.beginPath();
ctx.moveTo(ax*2,ay);
ctx.lineTo(bx+0.5,by+0.5);
ctx.strokeStyle = color;
ctx.stroke();
ctx.closePath();
}
var sunDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var merDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var venDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var marDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var mooDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var interval = 570.0930061551268;
function animate(){
ctx.save();
ctx.clearRect(0, 0, 1100, 1100);
ctx.translate(cw/2,ch/2);
//Earth
ctx.rotate(-(time / interval)+ Math.PI);
circle(15,"blue",0,0);
ctx.translate(480,0);
line("blue",-240,0,0,0);
ctx.translate(-480,0);
ctx.rotate((time / interval)+ Math.PI);
//Moon
var moontime = (time / (interval / 13.36996336996337)+mooDegree);
ctx.rotate(-moontime);
ctx.translate(23,0);
circle(3,"black",0,0);
ctx.translate(457,0);
line("#6a6a6a",-230,0,0,0);
ctx.translate(-480,0);
ctx.rotate(moontime);
//Sun
var suntime = time / interval;
ctx.rotate(-suntime);
ctx.translate(120,0);
circle(15,"yellow",0,0);
ctx.translate(360,0);
line("yellow",-182,0,0,0);
ctx.translate(-360,0);
ctx.rotate(suntime);
//Mercury
var mertime = (time / (interval / 4.150568181818182))+merDegree;
ctx.rotate(-(time / (interval / 4.150568181818182))+merDegree-suntime);
ctx.translate(40,0);
circle(15,"#898989",0,0);
ctx.translate(-40,0);
ctx.rotate((time / (interval / 4.150568181818182))+merDegree+suntime);
//Venus
ctx.rotate(-(time / (interval / 1.625500667556742))+venDegree-suntime);
ctx.translate(80,0);
circle(15,"#b9955b",0,0);
ctx.translate(-80,0);
ctx.rotate((time / (interval / 1.625500667556742))+venDegree);
//Mars
ctx.rotate(-(time / (interval / 0.5316593886462882))+marDegree);
ctx.translate(160,0);
circle(15,"#9f5e13",0,0);
ctx.translate(-160,0);
ctx.rotate((time / (interval / 0.5316593886462882))+marDegree);
ctx.restore();
time++;
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
}
I attempted to reverse rotate each animation iteration by subtracting the suns rotating angle but that just makes the axis line horizontal and not back to center.
Encapsulate using objects
Your code is a mess
You need to encapsulate the various parts so you can work with them individually.
The example below defines planets using a factory pattern to create planets as objects.
Each planet gets orbital ang, rate, and distance from the planet they orbit. Eg Mercury, Venus, Earth, Mars all orbit the Sun, and the Moon orbits the Earth.
Planets are created in orbital order. IE can not create planet before you create the planet it orbits.
On each animation frame each planet's position is calculated relative to the planet it orbits in the same order as they were created.
Before rendering the planet we want to center the view on a selected planet (in this case the Earth). The centered planets position is used to create a global matrix that will move all planets to the correct position.
Then when drawing each planet we multiply the global matrix by the planet's matrix ctx.setTransform(...globalMat); ctx.transform(this.cos, this.sin, -this.sin, this.cos, this.x, this.y); and draw the result
requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d");
const TAU = Math.PI * 2;
const centerOnPlanetName = "Earth"; // Planet name to center view on
const globalMatrix = [1,0,0,1,0,0]; // holds position of planet to track
const solSystem = {}; // holds planets by name
const planets = []; // holds planets in order of created
const planetCommon = { // common properties of planets
x: 0, y: 0, cos: 1, sin: 0,
update(time) {
const ang = this.startAng + this.orbitalSpeed * time;
this.cos = Math.cos(ang);
this.sin = Math.sin(ang);
if (this.orbits) { // Check if orbiting something
this.x = this.orbits.x + this.cos * this.dist;
this.y = this.orbits.y + this.sin * this.dist;
}
},
draw(ctx, globalMat) {
ctx.setTransform(...globalMat);
ctx.transform(this.cos, this.sin, -this.sin, this.cos, this.x, this.y);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.radius, 0, TAU);
ctx.fill();
ctx.beginPath();
ctx.lineTo(this.radius * 1.5, 0);
ctx.lineTo(300, 0);
ctx.stroke();
}
};
const planet = (name, orbitsName, dist, orbitalSpeed, startAng, radius, color) => {
planets.push(solSystem[name] = {
orbits: solSystem[orbitsName], // Set planet new planet orbits
dist, // dist from orbiting body
orbitalSpeed, // in radians per time unit
startAng, // starting angle in radians
radius,
color,
...planetCommon,
});
};
planet("Sun", undefined, 0, 0.8, - Math.PI, 20, "yellow");
planet("Mercury", "Sun", 45, 2, 0, 6, "#888");
planet("Venus", "Sun", 70, 1.2, 0, 15, "#CC8");
planet("Earth", "Sun", 120, 0.8, 0, 16, "#39B");
planet("Mars", "Sun", 175, 0.4, 0, 10, "#B43");
planet("Moon", "Earth", 30, 5, 0, 3, "#444");
function centerOn(cx, cy, planet) {
globalMatrix[4] = cx - planet.x;
globalMatrix[5] = cy - planet.y;
}
function mainLoop(time) {
time /= 1000;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
planets.forEach(planet => planet.update(time));
centerOn(ctx.canvas.width * 0.5, ctx.canvas.height * 0.5, solSystem[centerOnPlanetName]);
planets.forEach(planet => planet.draw(ctx, globalMatrix));
requestAnimationFrame(mainLoop);
}
canvas {
border: 1px solid black;
}
<canvas id="canvas" width="600" height="600"></canvas>
I want draw something like this in javascript:
Example:
The bigger circle: r = 5
What do I want to do? to position circles by forming a circle
My questions are (I would like to know):
How much points can I draw on a circle if I know the ray,
the distance between each circle and the radius of each circle?
How can I find the position of each circle (to draw automatically)?
Like the question 1 but the circles do not have the same radius.
Thank you!
Displace points around a circle edge equidistantly
Using HTML Canvas and a bit of trigonometry
Create a reusable circles() function and pass the desired arguments for Number of circles, Size, Radius, Color:
const ctx = document.getElementById("canvas").getContext("2d");
const cvsSize = 400;
ctx.canvas.width = cvsSize;
ctx.canvas.height = cvsSize;
function circles(tot, rad, dist, color) {
const arc = Math.PI * 2 / tot; // Arc in Radians
let ang = 0; // Start at angle 0 (East)
for (let i = 0; i < tot; i++) {
const x = dist * Math.cos(ang) + (cvsSize / 2);
const y = dist * Math.sin(ang) + (cvsSize / 2);
ctx.beginPath();
ctx.arc(x, y, rad, 0, Math.PI * 2, false);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
ang += arc;
}
}
// Circles, Radius, Distance, Color
circles(3, 5, 10, "#f0b");
circles(10, 8, 50, "#0bf");
circles(17, 10, 90, "#bf0");
circles(21, 15, 140, "#b0f");
<canvas id="canvas"></canvas>
I would like to create a circle with a special shadow effect.
Like this one:
.
It should look like a cone in wood or metal.
I tried to do something with the radial gradiant in canvas but i can't creat that special Effekt.
I donĀ“t know how to create the this shadow effect.
Can somebody give me a tip or help me?
This is what I've tried: JSFiddle
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 100,
y = 75,
innerRadius = 1,
outerRadius = 70,
radius = 60;
ctx.arc(x, y, radius, 0, 2 * Math.PI);
var gradient = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
gradient.addColorStop(0, '#FF9900');
gradient.addColorStop(1, '#FFFFFF');
ctx.fillStyle = gradient;
ctx.fill();
Greetings from Germany
Matzuman
There is unfortunately no gradient type in canvas which allow you to specify a radiant gradient. You have to provide a mechanism to do so manually.
You could use a shadow approach drawing the object off-screen while offsetting the shadow so it overlapped the cone base. One for light and one for dark side.
You can achieve a better effect though by drawing a "light/dark stripe" rotated around the center at varying opacity levels depending on angle.
Example "rendering" the cone
This example allow you to adjust parameters like how visible the reflections should be, what colors, size of cone etc. Experiment with the values to find what you're after.
To offset the "light source", just rotate one time initially with the angle you need before rendering the overlapping stripes.
var ctx = document.querySelector("canvas").getContext("2d"),
cx = 75, cy = 75, radius = 70, // for arc/cone
maxOpacity = 1, // max opacity (will accumulate)
angleStep = 0.01, // "resolution"
angle = 0, t; // current angle and opacity
// draw base of cone
ctx.fillStyle = "rgb(139, 108, 33)";
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
// now rotate around center drawing a white stripe at varying opacity
// depending on angle
ctx.fillStyle = "rgb(181, 159, 109)";
ctx.translate(cx, cy); // pivot for rotation = center of cone
// half of the cone is done with white overlay
for(angle = 0; angle < Math.PI; angle += angleStep) {
// calculate t [0,1] based on angle. Multiply with max opacity
t = (angle < Math.PI * 0.5 ? angle : Math.PI - angle) / Math.PI * maxOpacity;
ctx.rotate(angleStep); // increase angle by step
ctx.globalAlpha = t; // set opacity to t
drawStripe(); // draw a small segment / "stripe"
}
// the other half of the cone is done with dark overlay
ctx.fillStyle = "rgb(95, 54, 5)";
for(angle = 0; angle < Math.PI; angle += angleStep) {
t = (angle < Math.PI * 0.5 ? angle : Math.PI - angle) / Math.PI * maxOpacity;;
ctx.rotate(angleStep);
ctx.globalAlpha = t;
drawStripe();
}
function drawStripe() {
ctx.beginPath();
ctx.lineTo(0, 0);
ctx.arc(0, 0, radius, 0, angleStep*5);
ctx.fill();
}
// top off by drawing a smaller circle on top
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.globalAlpha = 1; // reset alpha
ctx.fillStyle = "rgb(130, 97, 32)"; // draw in a topping
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.25, 0, 2*Math.PI);
ctx.fill();
<canvas></canvas>
Example approximating a cone shape with shadows
var ctx = document.querySelector("canvas").getContext("2d"),
cx = 75, cy = 75, radius = 70, offset = radius * 2;
// draw base of cone
ctx.fillStyle = "rgb(139, 108, 33)";
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
// offset next shape, couter-offset its shadow
ctx.translate(cx, offset*2); // make sure shape is drawn outside
ctx.scale(0.75, 1); // make shadow more narrow
ctx.globalCompositeOperation = "source-atop"; // comp. on top of existing pixels
ctx.shadowOffsetY = -offset * 1.1; // counter-offset shadow
ctx.shadowBlur = 25; // some blur
ctx.shadowColor = "rgba(181, 159, 109, 1)"; // highlight color
ctx.beginPath(); // draw new shape
ctx.arc(0, 0, radius * 0.6, 0, 2*Math.PI); // reduce radius ~50%
ctx.fill();
ctx.shadowOffsetY = -offset * 1.8; // counter-offset shadow
ctx.shadowColor = "rgba(95, 54, 5, 0.7)"; // shadow
ctx.beginPath(); // draw new shape
ctx.arc(0, 0, radius * 0.6, 0, 2*Math.PI); // reduce radius ~50%
ctx.fill();
// top off by drawing a smaller circle on top
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.globalCompositeOperation = "source-over"; // reset comp. mode
ctx.fillStyle = "rgb(130, 97, 32)"; // draw in a topping
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.25, 0, 2*Math.PI);
ctx.fill();
<canvas></canvas>
Try using
ctx.shadowBlur = 40;
ctx.shadowColor = "#FF9900";
before drawing the circle.
shadowBlur sets the size of the shadow, you can set it to 0 if you want to disable it.
shadowColor is pretty self explanatory.
I'm trying to create an Arc in canvas, and I want to use degrees, instead of radians.
The problem is that it is not starting at 12 o'clock, but at 3 o'clock, just like the documentation has said, but how can I force it to 12?
JsFiddle: http://jsfiddle.net/C8CXz/
function degreesToRadians (degrees) {
return degrees * (Math.PI/180);
}
function radiansToDegrees (radians) {
return radians * (180/Math.PI);
}
var canvas = document.getElementById('circle');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(80, 80, 50, degreesToRadians(0), degreesToRadians(180), false);
ctx.lineWidth = 10;
ctx.stroke();
Subtract half PI to both start and end point when drawing the arc, like this:
...
ctx.arc(80, 80, 50, degreesToRadians(0)-Math.PI/2, degreesToRadians(180)-Math.PI/2, false);
...
Here is the Fiddle.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Growing Circles</title>
</head>
<body>
<canvas id="c" width="960" height="720"></canvas>
</body>
</html>
JavaScript
var canvas = document.getElementById( "c" ),
ctx = canvas.getContext( "2d" );
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc( 500, 350, 60, 0, 2 * Math.PI, false );
ctx.fillStyle = "#4DA54D";
ctx.fill();
ctx.strokeStyle = "DarkRed";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 120, 0, 2 * Math.PI, false );
ctx.strokeStyle = "OliveDrab";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 180, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#530053";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 240, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#208181";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 300, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#CC7A00";
ctx.stroke();
ctx.beginPath();
ctx.arc( 500, 350, 360, 0, 2 * Math.PI, false );
ctx.strokeStyle = "#205450";
ctx.stroke();
I want to divide This demo into 24 hours timeline by using lines. I tried few but not up-to mark.It is going very big in code!
Is there any other way to solve this in small piece of code like using slice as in Tried demo ?
Tried Demo
My Requirement is to do as This demo.
I want to have This demo to be sliced 24/7 where 24 represent hours and 7 for days.
Further Requirement :: Even i must be able to access the particular arc which i want depending on the day and hour !
Finally i want to have a look like this Image I will pass the arguments(Day, hour,Color) and then that particular segment should change the color, as i shown in the Image.
This is how i tried to print the numbers ..
function drawnumbers(){
for(var i=1; i< hours+1;i++){
context.font="18px sans-serif";
context.fillText(+i,20+i*30,20+i*30);
}
}but i want them on the outer circle as in png Image
Although Harsh has already provided a very useful answer, it relates to the wireframe drawing which you depicted.
I thought it would also be useful to show you how you could achieve the drawing of the individual segments.
I think you have asked for a little too much in your PNG as we would practically be creating your project for you, but with my answer and Harsh's answer I believe you can get what you want:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
// centre or center for US :) the drawing
var x = canvas.width / 2;
var y = canvas.height / 2;
// number of days
var days = 7;
// number of hours
var hours = 24;
// one segment represents an hour so divide degrees by hours
var segmentWidth = 360 / hours;
// begin at 0 and end at one segment width
var startAngle = 0;
var endAngle = segmentWidth;
// how thick you want a segment
var segmentDepth = 30;
function init() {
for (var i = 1; i <= days; i++) {
drawSegments(i * segmentDepth);
}
}
function drawSegments(radius) {
for (var i = 0; i < hours; i++) {
context.beginPath();
context.arc(x, y, radius,
(startAngle * Math.PI / 180), (endAngle * Math.PI / 180), false);
context.lineWidth = segmentDepth;
context.strokeStyle = '#' +
(Math.random() * 0xFFFFFF << 0).toString(16);
context.stroke();
// increase per segment
startAngle += segmentWidth;
endAngle += segmentWidth;
}
}
// start drawing our chart
init();
See my http://jsfiddle.net/U2tPJ/6/ demo.
Man, you are doing PROGRAMMING, so why are you afraid of using the tools? Use loops and variables.
Dial
var canvas = document.getElementById( "c" ),
ctx = canvas.getContext( "2d" ),
strokes = ["DarkRed", "OliveDrab", "#530053", "#208181", "#CC7A00", "#205450"];
ctx.lineWidth = 3;
for(var i=0; i<7; i++) {
ctx.beginPath();
ctx.arc( 500, 350, 420-60*i, 0, 2 * Math.PI, false );
if(i==6) {
ctx.fillStyle = "#4DA54D";
ctx.fill();
}
ctx.strokeStyle = strokes[i];
ctx.stroke();
}
// Now do the same for the 24 spokes (Use mathematical formulae)
// this is just from the top of my head, may need some adjustment
angle = 360/25; // you have 24 spokes, so 25 sectors (first and last one overlap or are the same)
for(var j=0; j<24; j++) {
ctx.beginPath();
ctx.moveTo(500, 350);
ctx.lineTo(500+420*Math.sin(angle*j), 350-420*Math.cos(angle*j));
ctx.strokeStyle = "blue";
ctx.stroke();
}
Labels
For printing labels on the dial:
angle = 360/23;
for(var i=0; i<23; i++) {
x = <centerX> + <radius> * Math.sin(angle*i*Math.PI/180) // convert from radian to angle
y = <centerY> - <radius> * Math.cos(angle*i*Math.PI/180)
context.fillText(i+1, x, y);
}