canvastext.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import * as THREE from 'three'
  2. function wordWrap(text, context, options) {
  3. const words = text.split(" ")
  4. const lines = []
  5. let line = ""
  6. const maxWidth = options.width - (8 * 2)
  7. words.forEach(word => {
  8. const newLine = line+word+" "
  9. const metrics = context.measureText(newLine.trim())
  10. let fontWidth = metrics.actualBoundingBoxLeft + metrics.actualBoundingBoxRight
  11. if(fontWidth > maxWidth) {
  12. lines.push(line.trim())
  13. line = word + " "
  14. } else {
  15. line = newLine
  16. }
  17. })
  18. if (line != "") {
  19. lines.push(line.trim())
  20. }
  21. return lines
  22. }
  23. export function buildCanvasText(text, inOptions = {}) {
  24. const options = {width: 512, height: 512, font: "86px Arial", textAlign: "center", lineWidth: 8}
  25. if(inOptions.width) options.width = inOptions.width
  26. if(inOptions.height) options.height = inOptions.height
  27. if(inOptions.font) options.font = inOptions.font
  28. if(inOptions.textAlign) options.textAlign = inOptions.textAlign
  29. if(inOptions.lineWidth) options.lineWidth = inOptions.lineWidth
  30. const texture = generateMap(text, options)
  31. const alphaMap = generateAlphaMap(text, options)
  32. const material = new THREE.MeshBasicMaterial({transparent: true, map: texture, side: THREE.DoubleSide, alphaMap: alphaMap, alphaTest: 0.1})
  33. const geometry = new THREE.PlaneGeometry(4,4)
  34. const textMesh = new THREE.Mesh(geometry, material)
  35. textMesh.castShadow = true
  36. return textMesh
  37. }
  38. function generateMap(text, options) {
  39. const textCanvas = document.createElement('canvas')
  40. textCanvas.width = options.width
  41. textCanvas.height = options.height
  42. const context = textCanvas.getContext('2d')
  43. context.clearRect(0, 0, options.width, options.height)
  44. context.font = options.font
  45. context.textAlign = options.textAlign
  46. context.lineWidth = options.lineWidth
  47. context.lineCap = "round"
  48. context.lineJoin = "round"
  49. const lines = wordWrap(text, context, options)
  50. for(let i = 0; i < lines.length; i++) {
  51. const lineToDraw = lines[i]
  52. const metrics = context.measureText(lineToDraw)
  53. let fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent
  54. // context.beginPath()
  55. // context.fillStyle = "rbga(0,0,0,0.5)"
  56. // context.rect(0,0, options.width, options.height)
  57. // context.fill()
  58. context.beginPath()
  59. context.strokeStyle = "black"
  60. context.strokeText(lineToDraw, options.width / 2, 8 + (fontHeight * (i + 1)))
  61. context.beginPath()
  62. context.fillStyle = "white"
  63. context.fillText(lineToDraw, options.width / 2, 8 + (fontHeight * (i + 1)))
  64. }
  65. const texture = new THREE.CanvasTexture(textCanvas)
  66. texture.minFilter = THREE.LinearFilter;
  67. texture.wrapS = THREE.ClampToEdgeWrapping;
  68. texture.wrapT = THREE.ClampToEdgeWrapping;
  69. texture.needsUpdate = true
  70. return texture
  71. }
  72. function generateAlphaMap(text, options) {
  73. const textCanvas = document.createElement('canvas')
  74. textCanvas.width = options.width
  75. textCanvas.height = options.height
  76. const context = textCanvas.getContext('2d')
  77. context.clearRect(0, 0, options.width, options.height)
  78. context.beginPath()
  79. context.fillStyle = "black"
  80. context.rect(0, 0, options.width, options.height)
  81. context.fill()
  82. // context.beginPath()
  83. // context.strokeStyle = "magenta"
  84. // context.rect(0,0, options.width, options.height)
  85. // context.stroke()
  86. context.font = options.font
  87. context.textAlign = options.textAlign
  88. context.lineWidth = options.lineWidth
  89. context.lineCap = "round"
  90. context.lineJoin = "round"
  91. const lines = wordWrap(text, context, options)
  92. for(let i = 0; i < lines.length; i++) {
  93. const lineToDraw = lines[i]
  94. const metrics = context.measureText(lineToDraw)
  95. let fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent
  96. // context.beginPath()
  97. // context.fillStyle = "rbga(0,0,0,0.5)"
  98. // context.rect(0,0, options.width, options.height)
  99. // context.fill()
  100. context.beginPath()
  101. context.strokeStyle = "white"
  102. context.strokeText(lineToDraw, options.width / 2, 8 + (fontHeight * (i + 1)))
  103. context.beginPath()
  104. context.fillStyle = "white"
  105. context.fillText(lineToDraw, options.width / 2, 8 + (fontHeight * (i + 1)))
  106. }
  107. const texture = new THREE.CanvasTexture(textCanvas)
  108. texture.minFilter = THREE.LinearFilter;
  109. texture.wrapS = THREE.ClampToEdgeWrapping;
  110. texture.wrapT = THREE.ClampToEdgeWrapping;
  111. texture.needsUpdate = true
  112. return texture
  113. }