How do I make text write counter-clockwise?
function drawTextAlongArc(context, str, centerX, centerY, radius, angle){
context.save();
context.translate(centerX, centerY);
context.rotate(-1 * angle / 2);
context.rotate(-1 * (angle / str.length) / 2);
for (var n = 0; n < str.length; n++) {
context.rotate(angle / str.length);
context.save();
context.translate(0, -1 * radius);
var char = str[n];
context.fillText(char, 0, 0);
context.restore();
}
context.restore();
}
window.onload = function(){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.font = "30pt Calibri";
context.textAlign = "center";
context.fillStyle = "blue";
context.strokeStyle = "blue";
context.lineWidth = 4;
var centerX = canvas.width / 2;
var centerY = canvas.height - 30;
var angle = Math.PI * 0.8; // radians
var radius = 150;
drawTextAlongArc(context, "Text along arc path", centerX, centerY, radius, angle);
// draw circle underneath text
context.arc(centerX, centerY, radius - 10, 0, 2 * Math.PI, false);
context.stroke();
};
I want to text to appear like this in counter clockwise
Not sure what you're asking, but if you want to write your text backwards in a counter-clockwise direction you'd just change this line:
drawTextAlongArc(context, "Text along arc path", centerX, centerY, radius, -angle);
last argument changed to -angle
the text is going to be backwards though, as you would expect.
Related
I've been trying to create a 2-part circle using JS where 2 different sentences will be used to compile a whole circle.
I can't find out how to make the direction of the bottom sentence to be from left to right.
No matter what i do the bottom sentence starts from the right side of the circle and circles back to the left.
function drawTextAlongArc(context, str, centerX, centerY, radius, angle,side){
context.save();
context.translate(centerX, centerY);
context.rotate( -1 * angle / 2);
context.rotate(-1 * (angle / str.length) / 2);
for (var n = 0; n < str.length; n++) {
context.rotate(angle / str.length);
context.save();
context.translate(0, side * radius);
var char = str[n];
context.fillText(char, 0, 0);
context.restore();
}
context.restore();
}
window.onload = function(){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.dir = 'ltr';
context.font = "14px Arial";
context.textAlign = "center";
context.direction = "ltr";
var centerX = canvas.width / 2;
var centerY = canvas.height - 190;
var angle = 3; // radians
var radius = 180;
var radius2 = 189;
drawTextAlongArc(context, "Upper Text goes here", centerX, centerY, radius, angle, '-1');
canvas.setAttribute('dir','ltr');
drawTextAlongArc(context, "Bottom Text goes here", centerX, centerY, radius2, angle, '1');
};
<canvas id="myCanvas" width="578" height="550" dir="ltr"></canvas>
I believe this is what you're trying to achieve. I simply took what you already had and called split followed by reverse on the bottom string:
function drawTextAlongArc(context, str, centerX, centerY, radius, angle, side) {
context.save();
context.translate(centerX, centerY);
context.rotate(-1 * angle / 2);
context.rotate(-1 * (angle / str.length) / 2);
for (var n = 0; n < str.length; n++) {
context.rotate(angle / str.length);
context.save();
context.translate(0, side * radius);
var char = str[n];
context.fillText(char, 0, 0);
context.restore();
}
context.restore();
}
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.dir = 'ltr';
context.font = "14px Arial";
context.textAlign = "center";
context.direction = "ltr";
var centerX = canvas.width / 2;
var centerY = canvas.height - 190;
var angle = 3; // radians
var radius = 180;
var radius2 = 189;
drawTextAlongArc(context, "Upper Text goes here", centerX, centerY, radius, angle, '-1');
canvas.setAttribute('dir', 'ltr');
drawTextAlongArc(context, "Bottom Text goes here".split('').reverse(), centerX, centerY, radius2, angle, '1');
};
<canvas id="myCanvas" width="578" height="550" dir="ltr"></canvas>
Ciao, the first solution I found is to reverse the string like this:
<canvas id="myCanvas" width="578" height="550" dir="ltr"></canvas>
<script>
function drawTextAlongArc(context, str, centerX, centerY, radius, angle,side){
context.save();
context.translate(centerX, centerY);
context.rotate( -1 * angle / 2);
context.rotate(-1 * (angle / str.length) / 2);
for (var n = 0; n < str.length; n++) {
context.rotate(angle / str.length);
context.save();
context.translate(0, side * radius);
var char = str[n];
context.fillText(char, 0, 0);
context.restore();
}
context.restore();
}
window.onload = function(){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.dir = 'ltr';
context.font = "14px Arial";
context.textAlign = "center";
context.direction = "ltr";
var centerX = canvas.width / 2;
var centerY = canvas.height - 190;
var angle = 3; // radians
var radius = 180;
var radius2 = 189;
drawTextAlongArc(context, "Upper Text goes here", centerX, centerY, radius, angle, '-1');
canvas.setAttribute('dir','ltr');
let string = "Bottom Text goes here"
var splitString = string.split("");
var reverseArray = splitString.reverse();
var joinArray = reverseArray.join("");
drawTextAlongArc(context, joinArray, centerX, centerY, radius2, angle, '1');
};
</script>
Maybe is not the best solution but it works.
I'd like to draw an ellipse given a cx and cy position-property and a width and height property of the ellipse itself.
Below you can find some working code for this setup:
But now I want to generate a kind of "progress display" by painting a percentage (from 0 to 100) of the ellipse instead of the complete ellipse.
I have attached a graphic here to illustrate the whole thing:
I don't really have a clear idea how to do that. I would prefer a solution where I can do without resizing the canvas - just for performance reasons and I hope someone has a good idea how to solve my problem.
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 280;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height)
let ellipse = function(cx, cy, w, h) {
let lx = cx - w / 2,
rx = cx + w / 2,
ty = cy - h / 2,
by = cy + h / 2;
let magic = 0.551784;
let xmagic = magic * w / 2,
ymagic = h * magic / 2;
let region = new Path2D();
region.moveTo(cx, ty);
region.bezierCurveTo(cx + xmagic, ty, rx, cy - ymagic, rx, cy);
region.bezierCurveTo(rx, cy + ymagic, cx + xmagic, by, cx, by);
region.bezierCurveTo(cx - xmagic, by, lx, cy + ymagic, lx, cy);
region.bezierCurveTo(lx, cy - ymagic, cx - xmagic, ty, cx, ty);
ctx.strokeStyle = "red";
ctx.lineWidth = "10";
region.closePath();
ctx.stroke(region);
}
ellipse(canvas.width / 2, canvas.height / 2, 300, 120)
<canvas id="canvas"></canvas>
You can use the built-in function ctx.ellipse - first we draw the green line as a full ellipse. Next, draw the red partial ellipse on top:
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 280;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height)
function ellipse(ctx, color, x,y, w, h, thickness, angle) {
ctx.strokeStyle = color;
ctx.beginPath();
ctx.ellipse(canvas.width / 2, canvas.height / 2, h/2,w/2, Math.PI*3/2, 0, angle);
ctx.lineWidth = thickness;
ctx.stroke();
}
function ell(percent) {
let x= canvas.width / 2;
let y= canvas.height / 2;
let w=300;
let h=120;
let th = 10; // example thickness 10px
ellipse(ctx, '#608a32', x,y, w, h, th, Math.PI*2);
ellipse(ctx, '#ed3833', x,y , w, h, th+.3, 2*Math.PI*percent/100);
}
ell(90); // here we start draw for 90%
<canvas id="canvas"></canvas>
You can draw the ellipse with a bit of trigonometry
let canvas = document.getElementById("canvas")
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 170;
let ellipse = function(cx, cy, ds, de, w, h, color) {
for (var i = ds; i < de; i ++) {
var angle = i * ((Math.PI * 2) / 360);
var x = Math.cos(angle) * w;
var y = Math.sin(angle) * h;
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(cx+ x, cy+y, 6, 0, 2 * Math.PI);
ctx.fill();
}
}
let draw = function(cx, cy, ds, de, w, h, color) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
delta += 10
if (delta > 350) delta = 40
hw = canvas.width / 2
hh = canvas.height / 2
ellipse(hw, hh, 0, 360, 150, 60, "red")
ellipse(hw, hh, 0, delta, 150, 60, "blue")
ctx.font = "80px Arial";
ctx.fillStyle = "green";
ctx.fillText(Math.round(delta/3.6) + "%", hw-70, hh+30);
}
delta = 90
setInterval(draw, 100)
<canvas id="canvas"></canvas>
Once you have a nice function you can animate it
I have two arcs with strokes and i want to have a line animate between them. The line should animate perpendicular to the points of the inner circle.
Here's something that I hacked together that is almost what I want.
things that are wrong with it are:
the length of the line is not the length between the 2 circle as it revolves around the inner circle.
sometimes the line is not perpendicular to the points of the inner circle. for example when it goes to the corner of the circle it tilts at an angle a little.
I had problem understanding how to use the trig function for lineTo. maybe because that changes the length of the line and I didn't know how to get the x and y coordinates of the outer circle to get the end point of the line. I'm used to doing math.cos * length and this gives me a line at an angle. I don't what a line I just wanted the coords of the outer circle.
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
function lineAtAngle(startX, startY, angleDeg, length, startX2, startY2, angleDeg2, length2){
var angle = angleDeg * (Math.PI / 180);
var angle2 = angleDeg2 * (Math.PI / 180);
// context.moveTo(startX, startY);
context.beginPath();
context.moveTo(
Math.cos(angle) * length + startX,
Math.sin(angle) * length + startY
)
context.lineTo(
Math.cos(angle2) *(length2 )+ startX2,
Math.sin(angle2) *(length2) + startY2
)
// context.lineTo(canvas.width / 2 + 60, canvas.height / 2, angle2, length2)
context.lineWidth = 10;
context.stroke();
context.closePath();
console.log("startX2: " + startX2 + " startY2: " + startY2 )
console.log(Math.sin(angle2) + startY2)
console.log(length)
}
function myLineTo(startX, startY, angleDeg, length){
}
var length1 = canvas.width / 2 + 60 - canvas.width / 2 -30
var length2 = canvas.width / 2 ;
// var length2 = 1;
console.log(length2)
var angle1 = 0;
var angle2 = 0;
(function animate(){
context.clearRect(0,0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
context.beginPath()
context.arc(canvas.width / 2, canvas.height / 2, 30, 0, 2 * Math.PI, true)
context.lineWidth = 1;
context.stroke()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 60, 0, 2 * Math.PI, true);
context.stroke();
context.closePath()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 3, 0, 2 * Math.PI, true)
context.fill()
context.closePath();
angle1++
angle2++
// lineAtAngle(canvas.width / 2 , canvas.height / 2 , angle1, length1, canvas.width / 2 + 60, canvas.height / 2, angle2, length2 )
lineAtAngle(canvas.width / 2 , canvas.height / 2 , angle1, length1, canvas.width / 2 + 60, canvas.height / 2, angle2, length2 )
}())
}
canvas{
background: #aaa;
}
<canvas id="canvas" width="400" height="400"></canvas>
I believe the below is what you're trying to achieve.
I've simplified the code somewhat by using the Canvas's built-in methods to translate the coordinate space to the center and by removing the extra function which had way too many parameters passed when all it needed was one angle and two radii.
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var radius1 = 30;
var radius2 = 60;
var angle = 0;
(function animate(){
context.clearRect(0, 0, canvas.width, canvas.height);
// save state and adjust coordinate space
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.lineWidth = 1;
context.beginPath()
context.arc(0, 0, radius1, 0, 2 * Math.PI, true)
context.stroke()
context.beginPath();
context.arc(0, 0, radius2, 0, 2 * Math.PI, true);
context.stroke();
context.beginPath();
context.arc(0, 0, 3, 0, 2 * Math.PI, true);
context.fill()
++angle;
var rads = angle * Math.PI / 180;
var x = Math.cos(rads);
var y = Math.sin(rads);
context.lineWidth = 10;
context.beginPath();
context.moveTo(radius1 * x, radius1 * y);
context.lineTo(radius2 * x, radius2 * y);
context.stroke();
// restore transformations for next pass
context.restore();
window.requestAnimationFrame(animate)
}())
}
canvas { background: #aaa; }
<canvas id="canvas" width="400" height="200"></canvas>
The idea for drawing a line is that you need to supply a start point (moveTo), and an end point (lineTo). Your current code is complicating the whole thing with multiple angles and lengths. What you want to imagine you are doing is starting at the center of your circle, then adding an offset which places the start point at the edge of the inner circle. Using trig is perfectly fine here. The length of your line will simply be outer radius minus inner radius, in the same direction as your offset (only one angle was needed).
Without fundamentally changing your approach, the code below shows the changes you could use to try this. Have your line function take a starting point (x, y) and an offset, as well as the angle and length. The starting point is your circle center, and the offset is the radius of the inner circle. The length (again) is simply the outer radius minus the inner radius.
window.onload = function(){
var innerCircleRadius = 30;
var outerCircleRadius = 60;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var angle1 = 0;
function lineAtAngle(startX, startY, angleDeg, offset, length) {
var angle = angleDeg * (Math.PI / 180); // Convert to radians.
var cosAngle = Math.cos(angle); // Only need cos(angle) once.
var sinAngle = Math.sin(angle); // Only need sin(angle) once.
var startXPos = cosAngle * offset + startX;
var startYPos = sinAngle * offset + startY;
var endXPos = cosAngle * length + startXPos;
var endYPos = sinAngle * length + startYPos;
context.beginPath();
context.moveTo(startXPos, startYPos);
context.lineTo(endXPos, endYPos);
context.lineWidth = 10;
context.stroke();
context.closePath();
}
(function animate() {
context.clearRect(0,0, canvas.width, canvas.height);
window.requestAnimationFrame(animate);
context.beginPath()
context.arc(canvas.width / 2, canvas.height / 2, innerCircleRadius, 0, 2 * Math.PI, true)
context.lineWidth = 1;
context.stroke()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, outerCircleRadius, 0, 2 * Math.PI, true);
context.stroke();
context.closePath()
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 3, 0, 2 * Math.PI, true)
context.fill()
context.closePath();
angle1++
lineAtAngle(canvas.width / 2 , canvas.height / 2 , angle1, innerCircleRadius, outerCircleRadius - innerCircleRadius);
}())
}
canvas {
background: #aaa;
}
<canvas id="canvas" width="400" height="400"></canvas>
I have been trying to get an array of circle to attract to the center of a canvas element but avoid a specific radius in middle. This gets them to move to the center thanks to this bit of code from codepen.io but I haven't been able to get them to avoid like a 300px radius in the middle. Any help is greatly appreciated.
// for each circle attraction to center
for (var i = 0; i < len; i++) {
var c0 = circles[i];
var dx = c0.x - canvas.center.x;
var dy = c0.y - canvas.center.y;
var f = 1 / c0.radius;
c0.x -= dx * 0.5 * f;
c0.y -= dy * 0.03;
}
You can do via following thing.
First, a function use to fill a circle.
var fillCircle = function(x, y, radius)
{
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.fill();
};
clip()
var clearCircle = function(x, y, radius)
{
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.clip();
context.clearRect(x - radius - 1, y - radius - 1,
radius * 2 + 2, radius * 2 + 2);
};
See this jsFiddle.
globalCompositeOperation
var clearCircle = function(x, y, radius)
{
context.save();
context.globalCompositeOperation = 'destination-out';
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
context.fill();
context.restore();
};
See this jsFiddle.
Both gave the desired result on screen.
So I have code that works when I dont add the rotate() code, but when I add it it spins by the J of my name in more of a pinwheel effect. I want the text to rotate around a circle.
Here is what i have
How can I fix this to do what I want?
CODE
function showCircularNameRotating(text,x,y,radius,space,top){
var w = canvas.width,
h = canvas.height,
cx = w * 0.5,
cy = h * 0.5,
angleStep = 0.1,
txt = 'Justin Esders';
space = space || 0;
var numRadsPerLetter = (Math.PI - space * 2) / text.length;
context.save();
context.translate(x,y);
var k = (top) ? 1 : -1;
context.rotate(-k * ((Math.PI - numRadsPerLetter) / 2 - space));
for(var i=0;i<text.length;i++){
context.save();
context.rotate(k*i*(numRadsPerLetter));
context.textAlign = "center";
context.textBaseline = (!top) ? "top" : "bottom";
context.fillText(text[i],0,-k*(radius));
context.restore();
}
context.restore();
(function rotate() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.translate(cx, cy);
context.rotate(angleStep);
context.translate(-cx, -cy);
context.fillText(txt, cx, cy);
requestAnimationFrame(rotate);
})();
}
Sample Usage
showCircularNameRotating("Justin Esders",150,150,75,Math.PI/12);