You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Text.js 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import {
  2. adopt,
  3. extend,
  4. nodeOrNew,
  5. register,
  6. wrapWithAttrCheck
  7. } from '../utils/adopter.js'
  8. import { registerMethods } from '../utils/methods.js'
  9. import SVGNumber from '../types/SVGNumber.js'
  10. import Shape from './Shape.js'
  11. import { globals } from '../utils/window.js'
  12. import * as textable from '../modules/core/textable.js'
  13. import { isDescriptive, writeDataToDom } from '../utils/utils.js'
  14. export default class Text extends Shape {
  15. // Initialize node
  16. constructor(node, attrs = node) {
  17. super(nodeOrNew('text', node), attrs)
  18. this.dom.leading = this.dom.leading ?? new SVGNumber(1.3) // store leading value for rebuilding
  19. this._rebuild = true // enable automatic updating of dy values
  20. this._build = false // disable build mode for adding multiple lines
  21. }
  22. // Set / get leading
  23. leading(value) {
  24. // act as getter
  25. if (value == null) {
  26. return this.dom.leading
  27. }
  28. // act as setter
  29. this.dom.leading = new SVGNumber(value)
  30. return this.rebuild()
  31. }
  32. // Rebuild appearance type
  33. rebuild(rebuild) {
  34. // store new rebuild flag if given
  35. if (typeof rebuild === 'boolean') {
  36. this._rebuild = rebuild
  37. }
  38. // define position of all lines
  39. if (this._rebuild) {
  40. const self = this
  41. let blankLineOffset = 0
  42. const leading = this.dom.leading
  43. this.each(function (i) {
  44. if (isDescriptive(this.node)) return
  45. const fontSize = globals.window
  46. .getComputedStyle(this.node)
  47. .getPropertyValue('font-size')
  48. const dy = leading * new SVGNumber(fontSize)
  49. if (this.dom.newLined) {
  50. this.attr('x', self.attr('x'))
  51. if (this.text() === '\n') {
  52. blankLineOffset += dy
  53. } else {
  54. this.attr('dy', i ? dy + blankLineOffset : 0)
  55. blankLineOffset = 0
  56. }
  57. }
  58. })
  59. this.fire('rebuild')
  60. }
  61. return this
  62. }
  63. // overwrite method from parent to set data properly
  64. setData(o) {
  65. this.dom = o
  66. this.dom.leading = new SVGNumber(o.leading || 1.3)
  67. return this
  68. }
  69. writeDataToDom() {
  70. writeDataToDom(this, this.dom, { leading: 1.3 })
  71. return this
  72. }
  73. // Set the text content
  74. text(text) {
  75. // act as getter
  76. if (text === undefined) {
  77. const children = this.node.childNodes
  78. let firstLine = 0
  79. text = ''
  80. for (let i = 0, len = children.length; i < len; ++i) {
  81. // skip textPaths - they are no lines
  82. if (children[i].nodeName === 'textPath' || isDescriptive(children[i])) {
  83. if (i === 0) firstLine = i + 1
  84. continue
  85. }
  86. // add newline if its not the first child and newLined is set to true
  87. if (
  88. i !== firstLine &&
  89. children[i].nodeType !== 3 &&
  90. adopt(children[i]).dom.newLined === true
  91. ) {
  92. text += '\n'
  93. }
  94. // add content of this node
  95. text += children[i].textContent
  96. }
  97. return text
  98. }
  99. // remove existing content
  100. this.clear().build(true)
  101. if (typeof text === 'function') {
  102. // call block
  103. text.call(this, this)
  104. } else {
  105. // store text and make sure text is not blank
  106. text = (text + '').split('\n')
  107. // build new lines
  108. for (let j = 0, jl = text.length; j < jl; j++) {
  109. this.newLine(text[j])
  110. }
  111. }
  112. // disable build mode and rebuild lines
  113. return this.build(false).rebuild()
  114. }
  115. }
  116. extend(Text, textable)
  117. registerMethods({
  118. Container: {
  119. // Create text element
  120. text: wrapWithAttrCheck(function (text = '') {
  121. return this.put(new Text()).text(text)
  122. }),
  123. // Create plain text element
  124. plain: wrapWithAttrCheck(function (text = '') {
  125. return this.put(new Text()).plain(text)
  126. })
  127. }
  128. })
  129. register(Text, 'Text')