[fix] Refresh bounding boxes when fonts load (#612)
* remove font face fallbacks * When fonts load, force the document to recalculate bounding boxes.
This commit is contained in:
parent
1778a49272
commit
15e3e9805f
7 changed files with 78 additions and 11 deletions
|
@ -255,6 +255,19 @@ export function Tldraw({
|
|||
onExport,
|
||||
])
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
if (!window.document?.fonts) return
|
||||
|
||||
function refreshBoundingBoxes() {
|
||||
app.refreshBoundingBoxes()
|
||||
}
|
||||
window.document.fonts.addEventListener('loadingdone', refreshBoundingBoxes)
|
||||
return () => {
|
||||
window.document.fonts.removeEventListener('loadingdone', refreshBoundingBoxes)
|
||||
}
|
||||
}, [app])
|
||||
|
||||
// Use the `key` to ensure that new selector hooks are made when the id changes
|
||||
return (
|
||||
<TldrawContext.Provider value={app}>
|
||||
|
|
|
@ -3,18 +3,21 @@ import * as React from 'react'
|
|||
const styles = new Map<string, HTMLStyleElement>()
|
||||
|
||||
const UID = `Tldraw-fonts`
|
||||
const WEBFONT_URL =
|
||||
'https://fonts.googleapis.com/css2?family=Caveat+Brush&family=Source+Code+Pro&family=Source+Sans+Pro&family=Crimson+Pro&display=block'
|
||||
const CSS = `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Caveat+Brush&family=Source+Code+Pro&family=Source+Sans+Pro&family=Crimson+Pro&display=block');
|
||||
@import url('');
|
||||
`
|
||||
|
||||
export function useStylesheet() {
|
||||
React.useLayoutEffect(() => {
|
||||
if (styles.get(UID)) return
|
||||
const style = document.createElement('style')
|
||||
style.innerHTML = CSS
|
||||
style.innerHTML = `@import url('${WEBFONT_URL}');`
|
||||
style.setAttribute('id', UID)
|
||||
document.head.appendChild(style)
|
||||
styles.set(UID, style)
|
||||
|
||||
return () => {
|
||||
if (style && document.head.contains(style)) {
|
||||
document.head.removeChild(style)
|
||||
|
|
|
@ -80,6 +80,7 @@ import { LineTool } from './tools/LineTool'
|
|||
import { ArrowTool } from './tools/ArrowTool'
|
||||
import { StickyTool } from './tools/StickyTool'
|
||||
import { StateManager } from './StateManager'
|
||||
import { clearPrevSize } from './shapes/shared/getTextSize'
|
||||
|
||||
const uuid = Utils.uniqueId()
|
||||
|
||||
|
@ -3075,6 +3076,51 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
|||
this.currentTool.onKeyUp?.(key, info, e)
|
||||
}
|
||||
|
||||
/** Force bounding boxes to reset when the document loads. */
|
||||
refreshBoundingBoxes = () => {
|
||||
// force a change to every text shape
|
||||
const force = this.shapes.map((shape) => {
|
||||
return [
|
||||
shape.id,
|
||||
{
|
||||
point: [...shape.point],
|
||||
...('label' in shape && { label: '' }),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const restore = this.shapes.map((shape) => {
|
||||
return [
|
||||
shape.id,
|
||||
{
|
||||
point: [...shape.point],
|
||||
...('label' in shape && { label: shape.label }),
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
clearPrevSize()
|
||||
|
||||
this.patchState({
|
||||
document: {
|
||||
pages: {
|
||||
[this.currentPageId]: {
|
||||
shapes: Object.fromEntries(force),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
this.patchState({
|
||||
document: {
|
||||
pages: {
|
||||
[this.currentPageId]: {
|
||||
shapes: Object.fromEntries(restore),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/* ------------- Renderer Event Handlers ------------ */
|
||||
|
||||
onDragOver: TLDropEventHandler = (e) => {
|
||||
|
|
|
@ -215,8 +215,9 @@ export class TextUtil extends TDShapeUtil<T, E> {
|
|||
}
|
||||
|
||||
if (!melm.parentNode) document.body.appendChild(melm)
|
||||
melm.textContent = this.texts.get(shape.id) ?? shape.text
|
||||
|
||||
melm.style.font = getFontStyle(shape.style)
|
||||
melm.textContent = this.texts.get(shape.id) ?? shape.text
|
||||
|
||||
// In tests, offsetWidth and offsetHeight will be 0
|
||||
const width = melm.offsetWidth || 1
|
||||
|
|
|
@ -31,7 +31,6 @@ export const TextLabel = React.memo(function TextLabel({
|
|||
}: TextLabelProps) {
|
||||
const rInput = React.useRef<HTMLTextAreaElement>(null)
|
||||
const rIsMounted = React.useRef(false)
|
||||
const size = getTextLabelSize(text, font)
|
||||
|
||||
const rTextContent = React.useRef(text)
|
||||
|
||||
|
@ -95,10 +94,11 @@ export const TextLabel = React.memo(function TextLabel({
|
|||
React.useLayoutEffect(() => {
|
||||
const elm = rInnerWrapper.current
|
||||
if (!elm) return
|
||||
const size = getTextLabelSize(text, font)
|
||||
elm.style.transform = `scale(${scale}, ${scale}) translate(${offsetX}px, ${offsetY}px)`
|
||||
elm.style.width = size[0] + 'px'
|
||||
elm.style.height = size[1] + 'px'
|
||||
}, [size, offsetY, offsetX, scale])
|
||||
elm.style.width = size[0] + 1 + 'px'
|
||||
elm.style.height = size[1] + 1 + 'px'
|
||||
}, [text, font, offsetY, offsetX, scale])
|
||||
|
||||
return (
|
||||
<TextWrapper>
|
||||
|
|
|
@ -42,6 +42,10 @@ let prevText = ''
|
|||
let prevFont = ''
|
||||
let prevSize = [0, 0]
|
||||
|
||||
export function clearPrevSize() {
|
||||
prevText = ''
|
||||
}
|
||||
|
||||
export function getTextLabelSize(text: string, font: string) {
|
||||
if (!text) {
|
||||
return [16, 32]
|
||||
|
@ -61,7 +65,7 @@ export function getTextLabelSize(text: string, font: string) {
|
|||
prevText = text
|
||||
prevFont = font
|
||||
|
||||
melm.textContent = `${text}`
|
||||
melm.textContent = text
|
||||
melm.style.font = font
|
||||
|
||||
// In tests, offsetWidth and offsetHeight will be 0
|
||||
|
|
|
@ -85,9 +85,9 @@ const fontSizes = {
|
|||
|
||||
const fontFaces = {
|
||||
[FontStyle.Script]: '"Caveat Brush"',
|
||||
[FontStyle.Sans]: '"Source Sans Pro", sans-serif',
|
||||
[FontStyle.Serif]: '"Crimson Pro", serif',
|
||||
[FontStyle.Mono]: '"Source Code Pro", monospace',
|
||||
[FontStyle.Sans]: '"Source Sans Pro"',
|
||||
[FontStyle.Serif]: '"Crimson Pro"',
|
||||
[FontStyle.Mono]: '"Source Code Pro"',
|
||||
}
|
||||
|
||||
const fontSizeModifiers = {
|
||||
|
|
Loading…
Reference in a new issue