cmiscm / leonsans

Leon Sans is a geometric sans-serif typeface made with code in 2019 by Jongmin Kim.
https://leon-kim.com/
MIT License
10.17k stars 534 forks source link

How to animate text weight #24

Closed chrisheuberger closed 3 years ago

chrisheuberger commented 4 years ago

Amazing work here! Sorry to ask this as it's not an issue with the project but my own question for working with Leon Sans.

How can I create an animated weight change as shown in the README so that each letter becomes thicker after the previous one?

I just can't figure it out and I don't know where else to ask. Thanks!

cmiscm commented 4 years ago

There's no function for the each latter weight change animation, I think the best way is that create new LeonSans() for all the letters. Then change the weight for the each LeonSans object. this.leons_ = []; this.text_ = String('LEON SANS').split(''); this.leonTotal_ = this.text_.length; for (let i = 0; i < this.leonTotal_; i++) { const ls = new LeonSans({ text: this.text_[i], color: ['#000000'], size: this.size_, weight: 600 }); this.leons_.push(ls); }

chrisheuberger commented 4 years ago

Thanks so much for the reply.

I tried to incorporate the above into the basic setup but couldn't get it working. Can you tell what I'm doing wrong?

let leon, canvas, ctx, leons_ , text_, leonTotal_;

const sw = 800;
const sh = 600;
const size_ = 100;
const weight_ = 200;
const pixelRatio = 2;

function init() {
  canvas = document.getElementById("leon-canvas");
  document.body.appendChild(canvas);
  ctx = canvas.getContext("2d");

  canvas.width = sw * pixelRatio;
  canvas.height = sh * pixelRatio;
  canvas.style.width = sw + 'px';
  canvas.style.height = sh + 'px';
  ctx.scale(pixelRatio, pixelRatio);

  leons_ = [];
  text_ = String('LEON SANS').split('');
  leonTotal_ = text_.length;
  for (let i = 0; i < leonTotal_; i++) {
   const ls = new LeonSans({
     text: text_[i],
     color: ['blue'],
     size: size_,
     weight: weight_
   });
   leons_.push(ls);
  }

  requestAnimationFrame(animate);
}

function animate(t) {
    requestAnimationFrame(animate);

    ctx.clearRect(0, 0, sw, sh);

    const x = (sw - leon.rect.w) / 2;
    const y = (sh - leon.rect.h) / 2;
    leon.position(x, y);

    leon.draw(ctx);
}

window.onload = () => {
    init();
};
wgolledge commented 3 years ago

Hi @ChrisBup, not sure if you managed to solve it but hopefully this might help someone else trying to achieve the same!

I was able to animate the weight by using a Tween on the weight property.


TweenMax.fromTo(
  leon,
  1,
  { weight: 400 },
  { weight: 200 }
);  
chrisheuberger commented 3 years ago

Thanks @wgolledge! I actually hadn't yet figured this out. Do you have a working sharable example by any chance? If not, all good!

chrisheuberger commented 3 years ago

For reference, this Glitch project is where I'm testing and I've made this StackOverflow issue as well.

In script.js, I have a function called aimateWeight using the TweenMax.fromTo method mentioned above which is animating text weight all at once but I'm trying to stagger the animation so that the timing can be slightly offset for each letter. To accomplish that, I'm using a for loop in the function called staggerAnimateWeight but when using that function (in the window.onload call at the bottom), it doesn't animate the text at all.

This is what I'm trying to do:

Weight change

@cmiscm or anyone have any ideas?

wgolledge commented 3 years ago

Good to see you managed to animate the weight in your Glitch app! Here's an example with a yoyo effect similar to the gif you shared https://codesandbox.io/s/bold-voice-hmpfu.

The weight is a property on the LeonSans instance itself, not per letter. So for your use-case you will either need to create separate instances of LeonSans (e.g. one per letter) with a tween, or ideally animate it with WebGL like in this example.

chrisheuberger commented 3 years ago

For anyone interested, I figured it out.

Starting with the idea of creating a new, separate instance of LeonSans for each letter using:

for (let i = 0; i < leonTotal_; i++) {
  const ls = new LeonSans({
    text: text_[i],
    size: sw * leonHeightRatio,
    weight: minWeight,
    tracking: 100,
    color:["Indigo"]
  });
  leons_.push(ls);
}

I initially thought I'd need to target each letter in a for loop with something like let letter = leons_[index]; to use TweenMax.fromTo but I eventually figured out that what I wanted was TweenMax.staggerFromTo which takes an array so I could use the one I created above (leons_):

TweenMax.staggerFromTo(
  leons_,
  1,
  {
    weight: minWeight,
    size: sw * leonHeightRatio
  },                    
  {
    weight: maxWeight,
    size: sw * leonHeightRatioLarge,
    repeat: -1,
    yoyo: true,
    ease: Power2.easeInOut
  },
0.1);

View working code example here: https://leon-sans-staggered-weight-size-animations.glitch.me/