author Yannick Deubel (https://github.com/yandeu)
 *  copyright license https import { LinearFilter , Texture } from 'three' ; import { SimpleSprite } from './simpleSprite' ; import { calcHeight , calcWidth , canvas , clearObjects , createNewTexture , roundRect } from './_misc' ; export interface TextStyles { align ? : 'center' | 'left' | 'right' ; background ? : string | CanvasGradient | CanvasPattern ; baseline ? : CanvasTextBaseline ; borderColor ? : string ; borderRadius ? : number ; borderWidth ? : number ; fillStyle ? : string | CanvasGradient | CanvasPattern ; fontFamily ? : string ; fontSize ? : number ; fontWeight ? : string ; lineHeight ? : number ; lineWidth ? : number ; offset ? : { x ? : number ; y ? : number ; } ; padding ? : number | { x ? : number ; y ? : number ; } ; strokeStyle ? : string | CanvasGradient | CanvasPattern ; } export class TextTexture extends Texture { private _text : string ; private _styles : TextStyles ; private _image : any ; width : number ; height : number ; getText ( ) { return this . _text ; } getStyles ( ) { return this . _styles ; } clone ( ) { return new TextTexture ( this . _text , this . _styles ) . copy ( this ) as this ; } copy ( source ) { super . copy ( source ) ; return this ; } constructor ( text , styles = { } ) { const { imageData , width , height } = createTextImage ( text , styles ) ; super ( imageData ) ; this . width = width ; this . height = height ; this . _text = text ; this . _styles = styles ; this . _image = imageData ; this . minFilter = LinearFilter ; this . generateMipmaps = false ; this . needsUpdate = true ; } } export class TextSprite extends SimpleSprite { private _text : string ; private _styles : TextStyles ; constructor ( texture ) { super ( texture , false ) ; this . _text = texture . getText ( ) ; this . _styles = texture . getStyles ( ) ; } getText ( ) { return this . _text ; } getStyles ( ) { return this . _styles ; } setStyles ( styles ) { this . _styles = styles ; this . texture . dispose ( ) ; this . setTexture ( createNewTexture ( createTextImage ( this . _text , styles ) . imageData ) ) ; } setText ( text ) { this . _text = text ; this . texture . dispose ( ) ; this . setTexture ( createNewTexture ( createTextImage ( text , this . _styles ) . imageData ) ) ; this . _update ( ) ; } _update ( ) { this . textureHeight = this . texture . image . height ; this . textureWidth = this . texture . image . width ; this . texture . needsUpdate = true ; this . material . needsUpdate = true ; const { x , y } = this . _internalScale ; this . setScale ( x , y ) ; } } const createTextImage = ( text , styles ) => { const { align = 'center' , background = '' , baseline = 'middle' , borderColor = '' , borderRadius = 0 , borderWidth = 0 , fillStyle = 'SlateBlue' , fontFamily = 'Arial' , fontSize = 48 , fontWeight = '' , lineHeight = 1 , lineWidth = 4 , padding = 0 , strokeStyle = '' } = styles ; const { offset : { x : offsetX = 0 , y : offsetY = 0 } = { } } = styles ; let paddingX ; let paddingY ; if ( typeof padding !== 'number' ) { paddingX = padding . x || 0 ; paddingY = padding . y || 0 ; } else { paddingX = padding ; paddingY = padding ; } const font = `${ fontWeight } ${ fontSize }px ${ fontFamily }` ; const ctx = canvas . getContext ( '2d' ) as CanvasRenderingContext2D ; ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ; const lines = text . split ( '\n' ) ; ctx . font = font ; const line_height = calcHeight ( text , fontSize , fontFamily , lineHeight ) ; const line_width = fillStyle ? lineWidth * 2 : lineWidth ; const height = line_height * lines . length + paddingY * 2 + borderWidth * 2 ; const width = calcWidth ( ctx , lines ) + line_width + paddingX * 2 + borderWidth * 2 ; canvas . height = height ; canvas . width = width ; if ( borderColor ) { ctx . strokeStyle = borderColor ; roundRect ( ctx , borderWidth / 2 , borderWidth / 2 , canvas . width - borderWidth , canvas . height - borderWidth , borderRadius ) ; ctx . lineWidth = borderWidth ; ctx . stroke ( ) ; } if ( background ) { ctx . fillStyle = background ; roundRect ( ctx , borderWidth , borderWidth , canvas . width - borderWidth * 2 , canvas . height - borderWidth * 2 , borderColor ? borderRadius / 2 : borderRadius ) ; ctx . fill ( ) ; } ctx . font = font ; ctx . textAlign = align ; ctx . textBaseline = baseline ; if ( strokeStyle && lineWidth ) { ctx . strokeStyle = strokeStyle ; ctx . lineWidth = line_width ; } if ( fillStyle ) ctx . fillStyle = fillStyle ; for ( var i = 0 ; i < lines . length ; i ++ ) { let top = i * line_height + line_height / 2 ; let left = 0 ; if ( align === 'left' ) { left = line_width / 2 ; left += paddingX ; left += borderWidth ; } if ( align === 'center' ) { left = width / 2 ; } if ( align === 'right' ) { left = width - line_width / 2 ; left -= paddingX ; left -= borderWidth ; } top += paddingY ; top += borderWidth ; top += offsetY ; left += offsetX ; if ( strokeStyle && lineWidth ) ctx . strokeText ( lines [ i ] , left , top ) ; if ( fillStyle ) ctx . fillText ( lines [ i ] , left , top ) ; } const imageData = ctx . getImageData ( 0 , 0 , canvas . width , canvas . height ) as any ; return { imageData , width , height } ; } ;