totothegreat
totothegreat

Reputation: 1643

Reduce the size of text in angularjs when line breaks?

I have a responsive app for desktop and mobile.

In the app i have a div which randomly shows texts of all kinds of lengths. I want to do the following:

If the line breaks because the length of the text is too wide for the width of that div, i want the font-size to reduce itself (I am using em's in my app).

Is it something i need to build directive for it? is it something that was built and used wildly?

Upvotes: 0

Views: 695

Answers (1)

Dan Prince
Dan Prince

Reputation: 29989

Writing a robust solution for this problem is going to be non-trivial. As far as I know, there's no way to tell whether a line of text breaks. However, we do know the criteria for line breaking is the width of the text being wider than the element, accounting for padding.

The Canvas API has a method called measureText which can be used to measure a string, using a given context with a font and size set. If you spoof the settings of the element with a canvas, then you can measure the text with the canvas and adjust the size until it fits without overflowing.

I've written up a rough implementation of the way I would tackle this.

function TextScaler(element) {
  var canvas = document.createElement('canvas'),
      context = canvas.getContext('2d');

  var scaler = {};

  scaler.copyProps = function() {
    var style   = element.style.fontStyle,
        family  = element.style.fontFamily,
        size    = element.style.fontSize,
        weight  = element.style.fontWeight,
        variant = element.style.fontVariant;

    context.font = [style, variant, weight, size, family].join(' ');
  };

  scaler.measure = function(text) {
    text = text || element.innerText;
    return context.measureText(text);
  };

  scaler.overflows = function() {
    var style = window.getComputedStyle(element),
        paddingLeft = style['padding-left'],
        paddingRight = style['padding-right'],
        width = style.width - paddingLeft - paddingRight;

    return scaler.measure() > width;
  };

  scaler.decrease = function() {
    // decrease font size by however much
  };

  scaler.auto = function(retries) {
    retries = retries || 10;

    if(retries <= 0) {
      scaler.apply();
      console.log('used all retries');
    }

    if(scaler.overflows()) {
      scaler.decrease();
      scaler.auto(retries - 1);
    } else {
      console.log('text fits');
      scaler.apply();
    }
  };

  scaler.apply = function() {
    // copy the properties from the context
    // back to the element
  };

  return scaler;
}

After you've sorted out some of the blank details there, you'd be able to use the function something like this:

var element = document.getElementById('');
var scaler = TextScaler(element);
scaler.auto();

If it doesn't manage to decrease it within 10 retries, it will stop there. You could also do this manually.

while(scaler.overflows()) {
  scaler.decrease();
}
scaler.apply();

You'd probably want some fairly fine tuned logic for handling the decrease function. It might be easiest to convert the ems to pixels, then work purely with integers.

This API could quite trivially be wrapped up as a directive, if you want to use this with Angular. I'd probably tackle this with two attribute directives.

<div text-scale retries="10">Hello world</div>

Of course, if it's not important that all the text is there onscreen, then you can just use the text-overflow: ellipsis CSS property.

Upvotes: 1

Related Questions