Created
November 20, 2024 10:11
-
-
Save kerns/2e0feab1f8538b9e496cd9c52ef0bf55 to your computer and use it in GitHub Desktop.
A React / Next.js 15 Friendly unicorn.studio Embed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use client'; | |
import { useEffect, useRef, useState } from 'react'; | |
export type UnicornSceneProps = { | |
projectId: string; | |
width?: number | string; | |
height?: number | string; | |
scale?: number; | |
dpi?: number; | |
altText?: string; | |
ariaLabel?: string; | |
className?: string; | |
isEditingOrPreviewing?: boolean; | |
}; | |
export default function UnicornScene({ | |
projectId, | |
width = "100%", | |
height = "100%", | |
scale = 0.85, | |
dpi = 2, | |
altText = "Unicorn Studio Animation", | |
ariaLabel = altText, | |
className = "", | |
isEditingOrPreviewing = false, | |
}: UnicornSceneProps) { | |
const elementRef = useRef<HTMLDivElement>(null); | |
const [error, setError] = useState<string | null>(null); | |
const [scriptLoaded, setScriptLoaded] = useState(false); | |
// Load the UnicornStudio script | |
useEffect(() => { | |
if (typeof window === 'undefined') return; | |
const script = document.createElement('script'); | |
script.src = 'https://cdn.unicorn.studio/v1.3.2/unicornStudio.umd.js'; | |
script.async = true; | |
script.onload = () => setScriptLoaded(true); | |
script.onerror = () => setError('Failed to load UnicornStudio script'); | |
document.body.appendChild(script); | |
return () => { | |
script.parentNode?.removeChild(script); | |
}; | |
}, []); | |
// Initialize the scene | |
useEffect(() => { | |
if (!scriptLoaded || !elementRef.current) return; | |
// Add cache-busting for preview mode | |
const cleanProjectId = projectId.split("?")[0]; | |
const cacheBuster = isEditingOrPreviewing ? `?update=${Math.random()}` : ''; | |
// Set the project ID attribute | |
elementRef.current.setAttribute('data-us-project', cleanProjectId + cacheBuster); | |
// Initialize UnicornStudio | |
const initUnicorn = async () => { | |
try { | |
const UnicornStudio = (window as any).UnicornStudio; | |
if (!UnicornStudio) { | |
throw new Error('UnicornStudio not found'); | |
} | |
if (!UnicornStudio.isInitialized) { | |
// Initialize with configuration options directly | |
await UnicornStudio.init({ | |
scale, | |
dpi, | |
}); | |
UnicornStudio.isInitialized = true; | |
} | |
} catch (err) { | |
setError(err instanceof Error ? err.message : 'Failed to initialize UnicornStudio'); | |
} | |
}; | |
initUnicorn(); | |
return () => { | |
const UnicornStudio = (window as any).UnicornStudio; | |
if (UnicornStudio?.destroy) { | |
UnicornStudio.destroy(); | |
} | |
}; | |
}, [scriptLoaded, projectId, scale, dpi, isEditingOrPreviewing]); | |
return ( | |
<div | |
ref={elementRef} | |
style={{ | |
width: typeof width === 'number' ? `${width}px` : width, | |
height: typeof height === 'number' ? `${height}px` : height | |
}} | |
className={`relative ${className}`} | |
role="img" | |
aria-label={ariaLabel} | |
> | |
{error && <div className="text-red-500">{error}</div>} | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment