Skip to content

Instantly share code, notes, and snippets.

@rista404
Last active September 3, 2018 11:58
Show Gist options
  • Save rista404/2a730e52f7bee08d699ba0fe14a3898e to your computer and use it in GitHub Desktop.
Save rista404/2a730e52f7bee08d699ba0fe14a3898e to your computer and use it in GitHub Desktop.
Utility to wrap text in canvas, or to get text sizing info (number of lines, height, positions).
import { wrapText } from './wrap-text'
// Drawing and highlighting words (twitter style)
wrapText({
ctx,
text: text,
x: 0,
y: 0,
maxWidth: 500,
lineHeight: fontSize * 1.2,
drawWord: ({ ctx, word, currentX, currentY }) => {
// Highlight
if (word.startsWith('#') || word.startsWith('@')) {
ctx.fillStyle = highlightColor
}
else {
ctx.fillStyle = 'white'
}
ctx.fillText(word, currentX, currentY)
},
})
// Getting text size without drawing
const { textHeight, numberOfLines } =
wrapText({
ctx,
text: text,
x: 0,
y: 0,
maxWidth: 500,
lineHeight: fontSize * 1.2,
})
export default function wrapText(opts) {
const {
ctx,
text = '',
x = 0,
y = 0,
maxWidth,
lineHeight,
drawWord = null,
} = opts
const words = text.replace(/\n/g, ' \\n ').split(/\s/ig)
let currentLineWidth = 0
let currentLine = 0
words.forEach(word => {
// line break
if (word === '\\n') {
currentLineWidth = 0
currentLine += 1
return
}
const { width } = ctx.measureText(`${word} `)
if ((currentLineWidth + width) > maxWidth) {
currentLineWidth = 0
currentLine += 1
}
if (drawWord) {
const currentX = x + currentLineWidth
const currentY = y + (currentLine * lineHeight)
drawWord({ word, ctx, currentX, currentY })
}
currentLineWidth += width
})
const numberOfLines = currentLine + 1
const endX = x + currentLineWidth
const endY = y + (numberOfLines * lineHeight)
const textHeight = endY - y
return {
numberOfLines,
startX: x,
startY: y,
endX,
endY,
textHeight,
}
}
// @flow
type DrawWordOptions = {
word: string,
ctx: CanvasRenderingContext2D,
currentX: number,
currentY: number,
}
type WrapTextOptions = {
ctx: CanvasRenderingContext2D,
text: string,
x: number,
y: number,
maxWidth: number,
lineHeight: number,
drawWord?: (opts: DrawWordOptions) => any,
}
type WrappedTextInfo = {
numberOfLines: number,
startX: number,
startY: number,
endX: number,
endY: number,
textHeight: number,
}
export default function wrapText(opts: WrapTextOptions): WrappedTextInfo {
const {
ctx,
text = '',
x = 0,
y = 0,
maxWidth,
lineHeight,
drawWord = null,
} = opts
const words = text.replace(/\n/g, ' \\n ').split(/\s/ig)
let currentLineWidth = 0
let currentLine = 0
words.forEach(word => {
// line break
if (word === '\\n') {
currentLineWidth = 0
currentLine += 1
return
}
const { width } = ctx.measureText(`${word} `)
if ((currentLineWidth + width) > maxWidth) {
currentLineWidth = 0
currentLine += 1
}
if (drawWord) {
const currentX = x + currentLineWidth
const currentY = y + (currentLine * lineHeight)
drawWord({ word, ctx, currentX, currentY })
}
currentLineWidth += width
})
const numberOfLines = currentLine + 1
const endX = x + currentLineWidth
const endY = y + (numberOfLines * lineHeight)
const textHeight = endY - y
return {
numberOfLines,
startX: x,
startY: y,
endX,
endY,
textHeight,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment