Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
You can’t perform that action at this time.