Skip to content

Instantly share code, notes, and snippets.

@HelveticaScenario
Last active June 15, 2017 01:33
Show Gist options
  • Select an option

  • Save HelveticaScenario/748623a84ac340db16dec511915f4abb to your computer and use it in GitHub Desktop.

Select an option

Save HelveticaScenario/748623a84ac340db16dec511915f4abb to your computer and use it in GitHub Desktop.
two implementations of a react component for resizing rendering text line by line to fit a container
import * as React from 'react'
import testContent from './content'
import { make2dContext, throttle } from './utils'
interface EditorState {
readonly font: string
readonly str: string
readonly words: ReadonlyArray<string>
readonly lines: ReadonlyArray<string>
}
interface EditorProps {}
class Editor extends React.PureComponent<EditorProps, EditorState> {
readonly ctx: CanvasRenderingContext2D
constructor(props: EditorProps) {
super(props)
this.state = {
font: 'normal normal normal normal 16px / 18px sans-serif ',
str: testContent,
words: testContent.split(' '),
lines: [],
}
this.ctx = make2dContext()
this.ctx.font = 'normal normal normal normal 16px / 18px sans-serif '
}
componentDidMount() {
throttle('resize', this.onResize)
this.onResize()
}
makeLines = (
words: ReadonlyArray<string>,
containerWidth: number,
oldLines: ReadonlyArray<string>
) => {
let changed = false
let lines: string[] = []
let totalWords = words.length
let str = ''
let lastStr = ''
let idx = 0
const makeLine = () => {
lines.push(lastStr)
const lastIdx = lines.length - 1
if (lines[lastIdx] !== oldLines[lastIdx]) {
changed = true
}
lastStr = ''
str = words[idx]
}
while (idx < totalWords) {
if (str !== '') {
str += ' '
}
str += words[idx]
const { width } = this.ctx.measureText(str)
if (width > containerWidth) {
makeLine()
} else {
lastStr = str
if (idx + 1 >= totalWords) {
makeLine()
}
}
idx++
}
if (lines.length !== oldLines.length) {
changed = true
}
return changed ? lines : oldLines
}
onResize = () => {
if (!this.container) {
return
}
const { width } = this.container.getBoundingClientRect()
const newLines = this.makeLines(this.state.words, width, this.state.lines)
if (newLines !== this.state.lines) {
this.state = {
...this.state,
lines: newLines,
}
this.forceUpdate()
}
}
container?: HTMLElement
getRef = (node: HTMLElement) => (this.container = node)
render() {
const divs = this.state.lines.map((line, index) =>
<div key={index}>{line}</div>
)
return (
<div ref={this.getRef}>
{divs}
</div>
)
}
}
export default Editor
import * as React from 'react'
import Measure, { ContentRect } from 'react-measure'
import testContent from './content'
import { make2dContext } from './utils'
interface EditorState {
readonly font: string
readonly str: string
readonly words: ReadonlyArray<string>
readonly lines: ReadonlyArray<string>
readonly containerRect?: ContentRect
}
interface EditorProps {}
class Editor extends React.Component<EditorProps, EditorState> {
readonly ctx: CanvasRenderingContext2D
constructor(props: EditorProps) {
super(props)
this.state = {
font: 'normal normal normal normal 16px / 18px sans-serif ',
str: testContent,
words: testContent.split(' '),
lines: [],
}
this.ctx = make2dContext()
this.ctx.font = 'normal normal normal normal 16px / 18px sans-serif '
}
makeLines = (words: ReadonlyArray<string>, containerWidth: number) => {
let lines: string[] = []
let totalWords = words.length
let str = ''
let lastStr = ''
let idx = 0
const makeLine = () => {
lines.push(lastStr)
lastStr = ''
str = words[idx]
}
while (idx < totalWords) {
if (str !== '') {
str += ' '
}
str += words[idx]
const { width } = this.ctx.measureText(str)
if (width > containerWidth) {
makeLine()
} else {
lastStr = str
if (idx + 1 >= totalWords) {
makeLine()
}
}
idx++
}
return lines
}
onResize = (contentRect: ContentRect) => {
if (contentRect.bounds.width != null) {
this.setState({
lines: this.makeLines(this.state.words, contentRect.bounds.width),
containerRect: contentRect,
})
}
}
render() {
return (
<Measure bounds={true} onResize={this.onResize}>
{({ contentRect, measureRef }) => {
const divs = this.state.lines.map((line, index) =>
<div key={index}>{line}</div>
)
return (
<div ref={measureRef}>
{divs}
</div>
)
}}
</Measure>
)
}
}
export default Editor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment