Text reveal along a path


#1

Hi there!

Help needed…Please

I’m new to coding…

What I am trying to do is create an interacted animation for the background of a website where; when you move the mouse down and to the right, and/ or scroll the page down it will reveal a sentence on a curved path (Like shown in the pic below)…

and when you move the mouse up and to the left, and/ or scroll the page up, the text will be hidden again.


need it to be responsive too mobile etc too.

Can anyone please help :slight_smile:

Dan


#2

What have you got working so far? Post your code!

Without seeing your code, we have no idea how to help!

Are you stuck on doing the letters?

Positioning them on a path?

Rotating them?

Dealing with mouse inputs?

Making it work on mobile?

Make it easy for us to help you! If I have to start by writing a sketch from scratch just to even BEGIN helping, I might not even bother putting the effort in!

If you are asking for help, EVERY POST YOU MAKE SHOULD HAVE YOUR CURRENT CODE.

Make sure to hit the </> button to format your code too.


#3

Thanks for the reply @TfGuy44,

Here is what I have so far…

Cheers,
Dan


#4

String message = "Lorem ipsum dolor sit amet,";
PFont f;
float r = 200;
float lineLength = width/5; 

void setup() {
  size(1000, 1000);
  f = createFont("futura",40,true);
  textFont(f);
  // The text must be centered!
  textAlign(CENTER);
  smooth();
}

void draw() {
  background(#1f23d6);

  translate(mouseX,mouseY); 
  noFill();
  stroke(0);
  ellipse(0, 0, r*2, r*2);


  float arclength = 0;

  for (int i = 0; i < message.length(); i++)
  {
    char currentChar = message.charAt(i);
    float w = textWidth(currentChar);

    arclength += w/2;
    float theta = PI + arclength / r;    
    pushMatrix();
    translate(r*cos(theta), r*sin(theta));
    rotate(theta+PI/2);
    fill(0);
    text(currentChar,0,0);
    popMatrix();
    arclength += w/2;
  }
}

#5

I have also been playing around with html and css here so maybe it could be a better way at doing it?

see below…

HTML

<svg viewBox="0 0 425 300">
  <path id="curve" d="M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145" />
  <text x="25">
    <textPath xlink:href="#curve">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed dignissim turpis odio, vel elementum erat mollis eget. Nunc scelerisque vulputate metus vitae cursus. Suspendisse ac pretium ex. Pellentesque pellentesque lacus eget massa tempus, sed bibendum est semper. Vestibulum accumsan molestie est, nec rutrum i
    </textPath>
  </text>
</svg>

CSS

body {
  background-color: #2E3B97;
  font-family: 'futura', cursive;
  font-size: 5px;
}

path {
  fill: transparent;
}

text {
  fill: #000000;
}

#6

Okay. So you have some code that shows you have to lop over the letters in a string, position each letter at a different position, and rotate it accordingly.

If you’re going to make the letters go in a path (not just a circle), you’re going to need to associate some information with each letter:

  • What that letter is.
  • Which number letter it is.
  • Where it should appear.
  • How much it should be rotated.

You might also want code that does useful things for a letter:

  • Creates a new letter given some information about it.
  • Draws the letter.
  • Determines if a letter should appear.

The point I’m trying to make is that you should write a Letter class.


Oh, I see now that you have some letters going in a curve already. Okay. Different approach…

  • How many letters are you going to draw?
  • How are you going to keep track of that information?
  • Can you make the number of letters drawn depend on that piece of information? Maybe use substring()?
  • Initially, how many letters are displayed?
  • What actions cause more letters to appear?
  • What actions cause less letters to appear?
  • How much does the number of shown letters change?

#7

cheers @TfGuy44,

  • I will be drawing 100 plus letters. (that includes spaces too). The amount of letters will be a matter of how many can fit on the path, so factors that will affect the amount of letters will be the size of the text, the kerning and the length of the path…
  • how would I use substring() for to calculate this info?
  • the mouse moving down or to the right and/or scrolling down the page will make the letters appear.
  • the mouse movie up, to the left and/or scrolling up the page will make the letters disappear.
  • the amount of number of letters appearing or disappearing is valued at the speed of the mouse…the faster the speed the more letters appear or disappear…

Cheers,
Dan


#8

Hi dnganeko,

I suspect that creating your background with HTML and CSS, or with an SVG editor, would be an easier way to do it… Even p5.js may be preferable to Java-based Processing.

There’s more than one way to create a curve and then calculate a point on that curve from a percentage (a step value in the range 0.0 to 1.0). For Bezier curves, two functions in the Processing API that you may find helpful are bezierPoint (in p5.js) and bezierTangent (in p5.js); for Catmull-Rom curves, curvePoint (in p5.js) and curveTangent (in p5.js).

String message = "Lorem ipsum dolor sit amet,"
  + "consectetur adipiscing elit.";
char[] messageAsChars = message.toCharArray();
int charCount = messageAsChars.length;
float charCountToStep = 1.0 / float(charCount);

float ap0x, ap0y, 
  cp0x, cp0y, 
  cp1x, cp1y, 
  ap1x, ap1y;

void setup() {
  size(1024, 512);
  smooth(8);
  randomizePoints();
  textSize(32);
  textAlign(CENTER, CENTER);
  textMode(MODEL);
}

void draw() {
  background(255);
  noFill();
  stroke(0xff007fff);
  bezier(ap0x, ap0y, 
    cp0x, cp0y, 
    cp1x, cp1y, 
    ap1x, ap1y);

  noStroke();
  fill(0xff000000);
  
  float step0 = frameCount * 0.01;
  
  for (int i = 0; i < charCount; ++i) {
    float step1 = i * charCountToStep;
    float step2 = (step0 + step1) % 1.0;
    
    float x = bezierPoint(ap0x, cp0x, cp1x, ap1x, step2);
    float y = bezierPoint(ap0y, cp0y, cp1y, ap1y, step2);
    
    float tanx = bezierTangent(ap0x, cp0x, cp1x, ap1x, step2);
    float tany = bezierTangent(ap0y, cp0y, cp1y, ap1y, step2);
    
    float angle = atan2(tany, tanx);

    pushMatrix();
    translate(x, y);
    rotate(angle);
    text(messageAsChars[i], 0.0, 0.0);
    popMatrix();
  }
}

void mouseReleased() {
  randomizePoints();
}

void randomizePoints() {
  float halfw = width * 0.5;
  float halfh = height * 0.5;
  ap0x = random(0.0, halfw);
  cp0x = random(0.0, width);
  cp1x = random(0.0, width);
  ap1x = random(halfw, width);

  ap0y = random(0.0, halfh);
  cp0y = random(0.0, height);
  cp1y = random(0.0, height);
  ap1y = random(halfh, height);
}

From bezierTangent or curveTangent, you can use atan2 (in p5.js) to find the angle of rotation for a point along the curve. This is simply one curve, not multiple curves connected together, where the last anchor point of the first curve is the first anchor point of the next. Even though the steps provided to bezierPoint are evenly distributed, the resulting points can crowd along the curve.

There are resources here and there which may help with the maths once you know the key words to search for. I’ve found the following helpful:


#9

The point I was trying to make is that you can have a variable (let’s call it number_letters) that tracks how many letters you should display at any time.

This variable needs to be at least 0, and at most the number of letters in your text.

You can use this variable as the upper limit when you loop over your letters. That way you only draw as many letters as you want, and not every letter.

Here is an example. Click the left/right mouse buttons to increase/decrease the value of this new variable. See how changing it’s value causes more or less letters to appear, as it is the value used as the limit when looping over letters:

String message = "Lorem ipsum dolor sit amet,";
PFont f;
float r = 200;
float lineLength = width/5; 

int number_letters = 3;

void setup() {
  size(1000, 1000);
  f = createFont("futura", 40, true);
  textFont(f);
  // The text must be centered!
  textAlign(CENTER);
  smooth();
}

void draw() {
  background(#1f23d6);

  translate(mouseX, mouseY); 
  noFill();
  stroke(0);
  ellipse(0, 0, r*2, r*2);


  float arclength = 0;

  number_letters = constrain(number_letters, 0, message.length());

  for (int i = 0; i < number_letters; i++)
  {
    char currentChar = message.charAt(i);
    float w = textWidth(currentChar);

    arclength += w/2;
    float theta = PI + arclength / r;    
    pushMatrix();
    translate(r*cos(theta), r*sin(theta));
    rotate(theta+PI/2);
    fill(0);
    text(currentChar, 0, 0);
    popMatrix();
    arclength += w/2;
  }
}

void mousePressed() {
  if ( mouseButton == LEFT ) {
    number_letters++;
  } else {
    number_letters--;
  }
}

Now it’s up to you to have this value change in an appropriate way when the mouse is moved or the page is scrolled. Try looking at the mouseMoved()function.


#10

Hi @behreajj,

Thanks heaps for the help! This has given me a lot to play with. I’ll let you know how I go :slight_smile:

Dan


#11

Hi @TfGuy44,

Cheers for that. I understand now. kind of haha. I’m going to have a play and will let you know how I go.

Cheers again mate.

Dan


#12

Hey behreajj,

Sorry, this may be a simple question but how do you change the font ? I’m trying to use PFont?

String message = "Lorem ipsum dolor sit amet,"
  + "consectetur adipiscing elit.";
char[] messageAsChars = message.toCharArray();
int charCount = messageAsChars.length;
float charCountToStep = 1.0 / float(charCount);

PFont font;
font = createFont("ACaslon-Italic-48.vlw");

float ap0x, ap0y, 
  cp0x, cp0y, 
  cp1x, cp1y, 
  ap1x, ap1y;

void setup() {
  size(1024, 512);
  smooth(8);
  randomizePoints();
  textSize(32);
  textFont(font);
  textAlign(CENTER, CENTER);
  textMode(MODEL);
}

void draw() {
  background(255);
  noFill();
  stroke(0xff007fff);
  bezier(ap0x, ap0y, 
    cp0x, cp0y, 
    cp1x, cp1y, 
    ap1x, ap1y);

  noStroke();
  fill(0xff000000);
  
  float step0 = frameCount * 0.01;
  
  for (int i = 0; i < charCount; ++i) {
    float step1 = i * charCountToStep;
    float step2 = (step0 + step1) % 1.0;
    
    float x = bezierPoint(ap0x, cp0x, cp1x, ap1x, step2);
    float y = bezierPoint(ap0y, cp0y, cp1y, ap1y, step2);
    
    float tanx = bezierTangent(ap0x, cp0x, cp1x, ap1x, step2);
    float tany = bezierTangent(ap0y, cp0y, cp1y, ap1y, step2);
    
    float angle = atan2(tany, tanx);

    pushMatrix();
    translate(x, y);
    rotate(angle);
    text(messageAsChars[i], 0.0, 0.0);
    popMatrix();
  }
}

void mouseMoved() {
  randomizePoints();
}

void randomizePoints() {
  float halfw = width * 0.5;
  float halfh = height * 0.5;
  ap0x = random(0.0, halfw);
  cp0x = random(0.0, width);
  cp1x = random(0.0, width);
  ap1x = random(halfw, width);

  ap0y = random(0.0, halfh);
  cp0y = random(0.0, height);
  cp1y = random(0.0, height);
  ap1y = random(halfh, height);
}

#13

Hi dnganeko,

Depending on your goals, you can accomplish what you want to do with the HTML, CSS and SVG you’ve already posted. Neither Processing nor p5.js are required. Two key pieces of information are 1. how to get the amount that the user has scrolled down the page as a percentage; then 2. how to convert that to a substring of the complete text. I added to your original code in a CodePen here.

There are two ways of creating a font. With createFont:

String content = "Lorem ipsum";

void setup() {
  size(512, 256);

  String[] available = PFont.list();
  printArray(available);
  int chosenFontIndex = 152;
  int fontSize = 32;
  PFont font = createFont(available[chosenFontIndex], fontSize);

  textAlign(CENTER, CENTER);
  textSize(fontSize);
  textFont(font);
}

void draw() {
  background(255);
  fill(0);
  text(content, width * 0.5, height * 0.5);
}

With loadFont:

String content = "Lorem ipsum";

void setup() {
  size(512, 256);

  int fontSize = 32;
  PFont font = loadFont("FiraCode-Regular-48.vlw");

  textAlign(CENTER, CENTER);
  textSize(fontSize);
  textFont(font);
}

void draw() {
  background(255);
  fill(0);
  text(content, width * 0.5, height * 0.5);
}

The .vlw file is created by going to the Processing IDE’s menu Tools > Create Font .... The .vlw file is placed in the sketch’s data folder.


#14

Hi behreajj,

Thanks heaps for the help! It has helped a lot!

Dan