"Soft preload" icons (#3507)

This PR includes a "soft preload" feature for icons, where icons will be
loaded when the canvas first mounts. The component will not wait for
icons to finish loading before showing the editor, but this should help
with "pop in" on menu icons.

### Change Type

- [x] `sdk` — Changes the tldraw SDK
- [x] `improvement` — Improving existing features

### Test Plan

1. Load the component
2. After load, open a menu for the first time
3. The icons should immediately be visible

### Release Notes

- Improve icon preloading
pull/3512/head
Steve Ruiz 2024-04-17 11:57:08 +01:00 zatwierdzone przez GitHub
rodzic a253af95d9
commit 1450454873
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
5 zmienionych plików z 19 dodań i 43 usunięć

Wyświetl plik

@ -19,7 +19,7 @@ body:
id: reproduction
attributes:
label: How can we reproduce the bug?
description: If you can make the bug happen again, please share the steps involved.
description: If you can make the bug happen again, please share the steps involved. You can [fork this CodeSandbox](https://codesandbox.io/p/sandbox/tldraw-example-n539u) to make a reproduction.
validations:
required: false
- type: dropdown

Wyświetl plik

@ -11,6 +11,10 @@ import { TextDecoder, TextEncoder } from 'util'
global.TextEncoder = TextEncoder
global.TextDecoder = TextDecoder
Image.prototype.decode = async function () {
return true
}
function convertNumbersInObject(obj: any, roundToNearest: number) {
if (!obj) return obj
if (Array.isArray(obj)) {

Wyświetl plik

@ -109,13 +109,10 @@ export function Tldraw(props: TldrawProps) {
)
const assets = useDefaultEditorAssetsWithOverrides(rest.assetUrls)
const { done: preloadingComplete, error: preloadingError } = usePreloadAssets(assets)
if (preloadingError) {
return <ErrorScreen>Could not load assets. Please refresh the page.</ErrorScreen>
}
if (!preloadingComplete) {
return <LoadingScreen>Loading assets...</LoadingScreen>
}

Wyświetl plik

@ -1,4 +1,4 @@
import { createContext, useContext } from 'react'
import { createContext, useContext, useEffect } from 'react'
import { TLUiAssetUrls } from '../assetUrls'
/** @internal */
@ -14,6 +14,19 @@ export function AssetUrlsProvider({
assetUrls: TLUiAssetUrls
children: React.ReactNode
}) {
useEffect(() => {
for (const src of Object.values(assetUrls.icons)) {
const image = new Image()
image.src = src
image.decode()
}
for (const src of Object.values(assetUrls.embedIcons)) {
const image = new Image()
image.src = src
image.decode()
}
}, [assetUrls])
return <AssetUrlsContext.Provider value={assetUrls}>{children}</AssetUrlsContext.Provider>
}

Wyświetl plik

@ -1,38 +0,0 @@
import { useEffect, useState } from 'react'
import { useAssetUrls } from '../context/asset-urls'
import { iconTypes } from '../icon-types'
/** @internal */
export function usePreloadIcons(): boolean {
const [isLoaded, setIsLoaded] = useState<boolean>(false)
const assetUrls = useAssetUrls()
useEffect(() => {
let cancelled = false
async function loadImages() {
// Run through all of the icons and load them. It doesn't matter
// if any of the images don't load; though we expect that they would.
// Instead, we just want to make sure that the browser has cached
// all of the icons it can so that they're available when we need them.
await Promise.allSettled(
iconTypes.map((icon) => {
const image = new Image()
image.src = assetUrls.icons[icon]
return image.decode()
})
)
if (cancelled) return
setIsLoaded(true)
}
loadImages()
return () => {
cancelled = true
}
}, [isLoaded, assetUrls])
return isLoaded
}