Skip to content

Commit 4628630

Browse files
author
Mathieu Legault
committed
Update all animations for Light and Dark support
1 parent a25e38c commit 4628630

File tree

2 files changed

+182
-7
lines changed

2 files changed

+182
-7
lines changed

fern/assets/styles.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -854,14 +854,14 @@ a[href*="changelog"] svg {
854854
display: block;
855855
overflow: visible; /* Changed from hidden - was clipping interactive areas */
856856
transform: translateY(0px);
857-
transition: transform 1s ease-out !important;
857+
transition: transform 250ms ease-out !important;
858858
will-change: transform;
859859
/* Ensure click events can reach the canvas */
860860
pointer-events: auto;
861861
}
862862

863863
.sdk-rive:hover {
864-
transform: translateY(-4px) !important;
864+
transform: translateY(-3px) !important;
865865
}
866866

867867
/* Ensure the canvas can receive clicks and is properly layered */

fern/rive-animation.js

Lines changed: 180 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
canvas: canvas,
6262
autoplay: true,
6363
stateMachines: stateMachine,
64+
autoBind: false, // Manual binding for theme switching
6465
layout: new rive.Layout({
6566
fit: rive.Fit[fit], // Configurable fit mode
6667
alignment: rive.Alignment.Center
@@ -77,6 +78,12 @@
7778
// Set up dynamic cursor changes based on Rive's interactive areas
7879
setupDynamicCursor(canvas, r, stateMachine);
7980

81+
// Initialize with current site theme
82+
if (r.viewModelCount > 0) {
83+
const currentIsDark = detectSiteTheme();
84+
setRiveTheme(r, currentIsDark, 'Palette');
85+
}
86+
8087
// Set up Rive event handling (primary method)
8188
try {
8289
if (r.on && rive.EventType && rive.EventType.RiveEvent) {
@@ -222,7 +229,7 @@
222229
// SDK Animation with native Rive interactions
223230
createRiveAnimation({
224231
canvasSelector: '#sdk-rive-canvas',
225-
riveUrl: 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68802bc752aef23fab76e6fc_sdk-rive.riv',
232+
riveUrl: 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68802bc752aef23fab76e6fc_12235f640f9ad3339b42e8d026c6345a_sdk-rive.riv',
226233
aspectRatio: 369/93,
227234
stateMachine: "State Machine 1",
228235
fallbackImages: [
@@ -243,7 +250,7 @@
243250
// Docs Animation
244251
createRiveAnimation({
245252
canvasSelector: '#docs-rive-canvas',
246-
riveUrl: 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825994c55f0eece04ce4e2_8014c1e8d9aa904e6d659541b198df0f_docs_animation.riv',
253+
riveUrl: 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825994c55f0eece04ce4e2_d261956a0f627eb6b94c39aa9fcc26f0_docs_animation.riv',
247254
aspectRatio: 404/262,
248255
stateMachine: "State Machine 1",
249256
fallbackImages: [
@@ -264,7 +271,7 @@
264271
// AI Animation with custom event handling
265272
createRiveAnimation({
266273
canvasSelector: '#ai-rive-canvas',
267-
riveUrl: 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825e97fd6225e1c8a7488c_afec146ef95157cbef522bcdee1ba8d9_ai_animation.riv',
274+
riveUrl: 'https://cdn.prod.website-files.com/67880ff570cdb1a85eee946f/68825e97fd6225e1c8a7488c_b8d233d3b43c3da6eff8cc65874d7b49_ai_animation.riv',
268275
aspectRatio: 371/99,
269276
stateMachine: "State Machine 1",
270277
fallbackImages: [
@@ -289,6 +296,13 @@
289296
});
290297

291298
// Add additional Rive animations by calling createRiveAnimation() with your config
299+
300+
// Enable automatic site theme synchronization
301+
setupRiveSiteThemeSync();
302+
303+
// Or manually control themes:
304+
// switchRiveThemes(true); // Switch to dark theme
305+
// switchRiveThemes(false); // Switch to light theme
292306
}
293307

294308
// Cleanup function to dispose of Rive instances
@@ -323,6 +337,167 @@
323337
}
324338
}).observe(document, { subtree: true, childList: true });
325339

326-
// Expose function globally for manual use
340+
// Data Binding approach using View Model Instances (Official Rive API)
341+
function setRiveTheme(riveInstance, isDark, viewModelName = 'Palette') {
342+
try {
343+
// Get the view model by name
344+
const viewModel = riveInstance.viewModelByName(viewModelName);
345+
if (!viewModel) {
346+
console.warn(`View model '${viewModelName}' not found.`);
347+
return false;
348+
}
349+
350+
// Check if instances exist
351+
if (viewModel.instanceCount === 0) {
352+
console.warn(`No instances found in view model '${viewModelName}'. Please create instances in the Rive editor.`);
353+
return false;
354+
}
355+
356+
let viewModelInstance = null;
357+
const instanceName = isDark ? 'Dark' : 'Light';
358+
359+
// Try 1: Get instance by name (if names are set)
360+
try {
361+
viewModelInstance = viewModel.instanceByName(instanceName);
362+
} catch (e) {
363+
// Name-based access failed, continue to index-based
364+
}
365+
366+
// Try 2: Get instance by index (fallback for unnamed instances)
367+
if (!viewModelInstance) {
368+
const instanceIndex = isDark ? 1 : 0; // Assume: 0=Light, 1=Dark
369+
if (instanceIndex < viewModel.instanceCount) {
370+
viewModelInstance = viewModel.instanceByIndex(instanceIndex);
371+
console.log(`Using instance by index: ${instanceIndex} (${isDark ? 'Dark' : 'Light'} theme)`);
372+
} else {
373+
console.warn(`Instance index ${instanceIndex} not available. Only ${viewModel.instanceCount} instances found.`);
374+
return false;
375+
}
376+
}
377+
378+
if (!viewModelInstance) {
379+
console.warn(`Could not get ${instanceName} theme instance.`);
380+
return false;
381+
}
382+
383+
// Bind the instance to the Rive file (this switches the theme)
384+
riveInstance.bindViewModelInstance(viewModelInstance);
385+
console.log(`Successfully switched to ${isDark ? 'Dark' : 'Light'} theme`);
386+
return true;
387+
388+
} catch (e) {
389+
console.warn('Could not switch Rive theme instance:', e);
390+
return false;
391+
}
392+
}
393+
394+
// Switch all animations to light/dark theme using Data Binding
395+
function switchAllRiveThemes(isDark) {
396+
document.querySelectorAll('canvas[id$="rive-canvas"]').forEach(canvas => {
397+
if (canvas._riveInstance) {
398+
setRiveTheme(canvas._riveInstance, isDark);
399+
}
400+
});
401+
}
402+
403+
// Detect current site theme using multiple methods
404+
function detectSiteTheme() {
405+
// Method 1: Check HTML/body classes (most common)
406+
if (document.documentElement.classList.contains('dark') ||
407+
document.body.classList.contains('dark') ||
408+
document.documentElement.classList.contains('dark-mode') ||
409+
document.body.classList.contains('dark-mode')) {
410+
return true;
411+
}
412+
413+
// Method 2: Check data attributes
414+
if (document.documentElement.getAttribute('data-theme') === 'dark' ||
415+
document.body.getAttribute('data-theme') === 'dark' ||
416+
document.documentElement.getAttribute('theme') === 'dark') {
417+
return true;
418+
}
419+
420+
// Method 3: Check CSS custom properties (if available)
421+
if (typeof getComputedStyle !== 'undefined') {
422+
const style = getComputedStyle(document.documentElement);
423+
const colorScheme = style.getPropertyValue('color-scheme');
424+
if (colorScheme.includes('dark')) {
425+
return true;
426+
}
427+
}
428+
429+
// Method 4: Fallback to system preference
430+
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
431+
return true;
432+
}
433+
434+
return false; // Default to light theme
435+
}
436+
437+
// Set up comprehensive theme detection and syncing
438+
function setupRiveSiteThemeSync() {
439+
// Set initial theme based on current site state
440+
const initialIsDark = detectSiteTheme();
441+
switchAllRiveThemes(initialIsDark);
442+
console.log('Rive themes initialized:', initialIsDark ? 'Dark' : 'Light');
443+
444+
// Method 1: Watch for class changes on html/body (most reliable)
445+
const observer = new MutationObserver((mutations) => {
446+
mutations.forEach((mutation) => {
447+
if (mutation.type === 'attributes' &&
448+
(mutation.attributeName === 'class' ||
449+
mutation.attributeName === 'data-theme' ||
450+
mutation.attributeName === 'theme')) {
451+
const isDark = detectSiteTheme();
452+
switchAllRiveThemes(isDark);
453+
console.log('Site theme changed, Rive themes updated:', isDark ? 'Dark' : 'Light');
454+
}
455+
});
456+
});
457+
458+
// Observe both html and body for attribute changes
459+
observer.observe(document.documentElement, {
460+
attributes: true,
461+
attributeFilter: ['class', 'data-theme', 'theme']
462+
});
463+
observer.observe(document.body, {
464+
attributes: true,
465+
attributeFilter: ['class', 'data-theme', 'theme']
466+
});
467+
468+
// Method 2: Listen for system color scheme changes (backup)
469+
if (window.matchMedia) {
470+
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
471+
darkModeMediaQuery.addEventListener('change', (e) => {
472+
// Only apply system preference if no explicit site theme is set
473+
if (!document.documentElement.classList.contains('dark') &&
474+
!document.documentElement.classList.contains('light') &&
475+
!document.documentElement.getAttribute('data-theme')) {
476+
switchAllRiveThemes(e.matches);
477+
console.log('System theme changed, Rive themes updated:', e.matches ? 'Dark' : 'Light');
478+
}
479+
});
480+
}
481+
482+
// Method 3: Listen for custom theme events (if your site dispatches them)
483+
document.addEventListener('themeChanged', (e) => {
484+
if (e.detail && typeof e.detail.isDark === 'boolean') {
485+
switchAllRiveThemes(e.detail.isDark);
486+
console.log('Custom theme event received, Rive themes updated:', e.detail.isDark ? 'Dark' : 'Light');
487+
}
488+
});
489+
490+
return observer; // Return observer for cleanup if needed
491+
}
492+
493+
// Legacy function for backward compatibility
494+
function setupRiveSystemThemeDetection() {
495+
return setupRiveSiteThemeSync();
496+
}
497+
498+
// Expose functions globally for manual use
327499
window.createRiveAnimation = createRiveAnimation;
328-
})();
500+
window.switchRiveThemes = switchAllRiveThemes;
501+
window.setupRiveSystemThemeDetection = setupRiveSystemThemeDetection;
502+
window.setupRiveSiteThemeSync = setupRiveSiteThemeSync;
503+
})();

0 commit comments

Comments
 (0)