TextWrapper.js 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. export default class TextWrapper {
  2. constructor() {
  3. this.margin = {left: 8, right: 8}
  4. this.padding = {top:4, left: 8, bottom: 4, right: 8}
  5. this.fontSize = 24
  6. }
  7. draw(ctx, text, options) {
  8. let width = options.width
  9. let line = 0;
  10. text.split("\n").forEach(textLine => {
  11. while (this.textIsTooLong(ctx, textLine, width)) {
  12. let shortTextIndex = this.getShortTextIndex(ctx, textLine, width)
  13. let shortText = textLine.substr(0, shortTextIndex).trim()
  14. textLine = textLine.substr(shortTextIndex)
  15. this.drawTextLine(ctx, shortText, line)
  16. line++;
  17. }
  18. this.drawTextLine(ctx, textLine.trim(), line)
  19. line++
  20. })
  21. }
  22. drawTextLine(ctx, text, line) {
  23. let bounds = ctx.measureText(text);
  24. let textHeight = Math.max(Math.ceil(this.fontSize), bounds.actualBoundingBoxAscent + bounds.actualBoundingBoxDescent);
  25. ctx.fillText(text, this.padding.left, this.padding.top + (line * textHeight));
  26. }
  27. getShortTextIndex(ctx, text, width) {
  28. let timeout = 0
  29. while (this.textIsTooLong(ctx, text, width)) {
  30. timeout++
  31. let words = text.split(" ");
  32. words.pop();
  33. text = words.join(" ");
  34. if(timeout > 50) {
  35. throw new Error(`Text won't fit in ${width}`)
  36. }
  37. }
  38. return text.length;
  39. }
  40. textIsTooLong(ctx, text, width) {
  41. let bounds = ctx.measureText(text);
  42. let areaWidth = width - (this.padding.left + this.padding.right + this.margin.left + this.margin.right);
  43. return bounds.width > areaWidth
  44. }
  45. }