I'd like to revolve text around an oval. Some slight distortion occurs on each character however the more extreme (i.e. the less of a perfect circle) the oval becomes. Ideally this method would render text nicely on an oval of any height and width ratio, and even other shapes such as rounded rectangles.
My approach so far has been to:
- Find the edge of the oval
- Rotate a point around the distorted oval using trigonometry
- Draw
text()at this position - Rotate this
text()according to its position - Do this over a
forloop, looping over every character in a provided string.
The characters gain strange spacing (even with textWidth) and strange rotation (even when calculated dynamically). You can see that in the below snippet, particularly on the 'nn' of 'spinning' and 'ie' of 'piece'.
let canvasWidth = 400;
let canvasHeight = 400;
let spinSpeed = 0.25;
let ellipseWidth = 280;
let ellipseHeight = 200;
let angle = 0;
let sourceText = "A beautiful piece of spinning text. ";
let sourceCharacters = sourceText.toUpperCase().split("");
function setup() {
createCanvas(canvasWidth, canvasHeight);
angleMode(DEGREES);
textSize(18);
textAlign(LEFT, BASELINE);
}
function draw() {
background(0);
// Draw an ellipse
stroke("rgba(255, 255, 255, 0.15)");
noFill();
ellipse(canvasWidth / 2, canvasHeight / 2, ellipseWidth, ellipseHeight);
// Prepare for operations around circle
translate(canvasWidth / 2, canvasHeight / 2);
// Create a revolving angle
if (angle < 360) {
angle += spinSpeed;
} else {
angle = 0;
}
// Set variables for trigonometry
let widthR = ellipseWidth / 2;
let heightR = ellipseHeight / 2;
let dx = widthR * cos(angle);
let dy = heightR * sin(angle);
// Set variable for offsetting each character
let currentOffset = 0;
// Loop through each chracter and place on oval edge
for (let i = 0; i < sourceCharacters.length; i++) {
push();
dx = widthR * cos(angle + currentOffset);
dy = heightR * sin(angle + currentOffset);
translate(dx, dy);
rotate(angle + currentOffset + 90);
stroke("rgba(255, 255, 255, 0.25)");
rect(0, 0, textWidth(sourceCharacters[i]), 10);
fill("white");
text(sourceCharacters[i], 0, 0);
currentOffset += textWidth(sourceCharacters[i]);
pop();
}
}
html,
body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.js"></script>