[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,
|
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
|
// Use the `key` to ensure that new selector hooks are made when the id changes
|
||||||
return (
|
return (
|
||||||
<TldrawContext.Provider value={app}>
|
<TldrawContext.Provider value={app}>
|
||||||
|
|
|
@ -3,18 +3,21 @@ import * as React from 'react'
|
||||||
const styles = new Map<string, HTMLStyleElement>()
|
const styles = new Map<string, HTMLStyleElement>()
|
||||||
|
|
||||||
const UID = `Tldraw-fonts`
|
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 = `
|
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() {
|
export function useStylesheet() {
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
if (styles.get(UID)) return
|
if (styles.get(UID)) return
|
||||||
const style = document.createElement('style')
|
const style = document.createElement('style')
|
||||||
style.innerHTML = CSS
|
style.innerHTML = `@import url('${WEBFONT_URL}');`
|
||||||
style.setAttribute('id', UID)
|
style.setAttribute('id', UID)
|
||||||
document.head.appendChild(style)
|
document.head.appendChild(style)
|
||||||
styles.set(UID, style)
|
styles.set(UID, style)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (style && document.head.contains(style)) {
|
if (style && document.head.contains(style)) {
|
||||||
document.head.removeChild(style)
|
document.head.removeChild(style)
|
||||||
|
|
|
@ -80,6 +80,7 @@ import { LineTool } from './tools/LineTool'
|
||||||
import { ArrowTool } from './tools/ArrowTool'
|
import { ArrowTool } from './tools/ArrowTool'
|
||||||
import { StickyTool } from './tools/StickyTool'
|
import { StickyTool } from './tools/StickyTool'
|
||||||
import { StateManager } from './StateManager'
|
import { StateManager } from './StateManager'
|
||||||
|
import { clearPrevSize } from './shapes/shared/getTextSize'
|
||||||
|
|
||||||
const uuid = Utils.uniqueId()
|
const uuid = Utils.uniqueId()
|
||||||
|
|
||||||
|
@ -3075,6 +3076,51 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
||||||
this.currentTool.onKeyUp?.(key, info, e)
|
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 ------------ */
|
/* ------------- Renderer Event Handlers ------------ */
|
||||||
|
|
||||||
onDragOver: TLDropEventHandler = (e) => {
|
onDragOver: TLDropEventHandler = (e) => {
|
||||||
|
|
|
@ -215,8 +215,9 @@ export class TextUtil extends TDShapeUtil<T, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!melm.parentNode) document.body.appendChild(melm)
|
if (!melm.parentNode) document.body.appendChild(melm)
|
||||||
melm.textContent = this.texts.get(shape.id) ?? shape.text
|
|
||||||
melm.style.font = getFontStyle(shape.style)
|
melm.style.font = getFontStyle(shape.style)
|
||||||
|
melm.textContent = this.texts.get(shape.id) ?? shape.text
|
||||||
|
|
||||||
// In tests, offsetWidth and offsetHeight will be 0
|
// In tests, offsetWidth and offsetHeight will be 0
|
||||||
const width = melm.offsetWidth || 1
|
const width = melm.offsetWidth || 1
|
||||||
|
|
|
@ -31,7 +31,6 @@ export const TextLabel = React.memo(function TextLabel({
|
||||||
}: TextLabelProps) {
|
}: TextLabelProps) {
|
||||||
const rInput = React.useRef<HTMLTextAreaElement>(null)
|
const rInput = React.useRef<HTMLTextAreaElement>(null)
|
||||||
const rIsMounted = React.useRef(false)
|
const rIsMounted = React.useRef(false)
|
||||||
const size = getTextLabelSize(text, font)
|
|
||||||
|
|
||||||
const rTextContent = React.useRef(text)
|
const rTextContent = React.useRef(text)
|
||||||
|
|
||||||
|
@ -95,10 +94,11 @@ export const TextLabel = React.memo(function TextLabel({
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => {
|
||||||
const elm = rInnerWrapper.current
|
const elm = rInnerWrapper.current
|
||||||
if (!elm) return
|
if (!elm) return
|
||||||
|
const size = getTextLabelSize(text, font)
|
||||||
elm.style.transform = `scale(${scale}, ${scale}) translate(${offsetX}px, ${offsetY}px)`
|
elm.style.transform = `scale(${scale}, ${scale}) translate(${offsetX}px, ${offsetY}px)`
|
||||||
elm.style.width = size[0] + 'px'
|
elm.style.width = size[0] + 1 + 'px'
|
||||||
elm.style.height = size[1] + 'px'
|
elm.style.height = size[1] + 1 + 'px'
|
||||||
}, [size, offsetY, offsetX, scale])
|
}, [text, font, offsetY, offsetX, scale])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextWrapper>
|
<TextWrapper>
|
||||||
|
|
|
@ -42,6 +42,10 @@ let prevText = ''
|
||||||
let prevFont = ''
|
let prevFont = ''
|
||||||
let prevSize = [0, 0]
|
let prevSize = [0, 0]
|
||||||
|
|
||||||
|
export function clearPrevSize() {
|
||||||
|
prevText = ''
|
||||||
|
}
|
||||||
|
|
||||||
export function getTextLabelSize(text: string, font: string) {
|
export function getTextLabelSize(text: string, font: string) {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return [16, 32]
|
return [16, 32]
|
||||||
|
@ -61,7 +65,7 @@ export function getTextLabelSize(text: string, font: string) {
|
||||||
prevText = text
|
prevText = text
|
||||||
prevFont = font
|
prevFont = font
|
||||||
|
|
||||||
melm.textContent = `${text}`
|
melm.textContent = text
|
||||||
melm.style.font = font
|
melm.style.font = font
|
||||||
|
|
||||||
// In tests, offsetWidth and offsetHeight will be 0
|
// In tests, offsetWidth and offsetHeight will be 0
|
||||||
|
|
|
@ -85,9 +85,9 @@ const fontSizes = {
|
||||||
|
|
||||||
const fontFaces = {
|
const fontFaces = {
|
||||||
[FontStyle.Script]: '"Caveat Brush"',
|
[FontStyle.Script]: '"Caveat Brush"',
|
||||||
[FontStyle.Sans]: '"Source Sans Pro", sans-serif',
|
[FontStyle.Sans]: '"Source Sans Pro"',
|
||||||
[FontStyle.Serif]: '"Crimson Pro", serif',
|
[FontStyle.Serif]: '"Crimson Pro"',
|
||||||
[FontStyle.Mono]: '"Source Code Pro", monospace',
|
[FontStyle.Mono]: '"Source Code Pro"',
|
||||||
}
|
}
|
||||||
|
|
||||||
const fontSizeModifiers = {
|
const fontSizeModifiers = {
|
||||||
|
|
Loading…
Reference in a new issue