Skip to content

Commit

Permalink
feat: using view transitions API on appearance toggle (#1272)
Browse files Browse the repository at this point in the history
Co-authored-by: sunyiteng <[email protected]>
Co-authored-by: SoonIter <[email protected]>
Co-authored-by: Timeless0911 <[email protected]>
  • Loading branch information
4 people committed Sep 3, 2024
1 parent c233a37 commit f9e49cc
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 33 deletions.
25 changes: 22 additions & 3 deletions packages/document/docs/en/api/config/config-theme.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ export default defineConfig({
- Type: `boolean`
- Default: `false`

Whether there is an animation effect when switching between pages. For example:
Whether there is animation effect when switching between pages. It is implemented with [View Transition API](https://developer.mozilla.org/docs/Web/API/View_Transitions_API). For example:

> The animation is not configurable for now.
Expand All @@ -571,12 +571,31 @@ export default defineConfig({
});
```

## enableAppearanceAnimation

- Type: `boolean`
- Default: `false`

Whether there is animation effect when switching between light and dark theme. It is implemented with [View Transition API](https://developer.mozilla.org/docs/Web/API/View_Transitions_API). For example:

> The animation is not configurable for now.
```ts title="rspress.config.ts"
import { defineConfig } from 'rspress/config';

export default defineConfig({
themeConfig: {
enableAppearanceAnimation: true,
},
});
```

## search

- Type: `boolean`
- Default: `true`

Whether to display the search box. for example:
Whether to display the search box. For example:

```ts title="rspress.config.ts"
import { defineConfig } from 'rspress/config';
Expand All @@ -593,7 +612,7 @@ export default defineConfig({
- Type: `string`
- Default: `Source`

The text of the source code button. for example:
The text of the source code button. For example:

```ts title="rspress.config.ts"
import { defineConfig } from 'rspress/config';
Expand Down
1 change: 1 addition & 0 deletions packages/document/docs/en/guide/start/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ export default defineConfig({
themeConfig: {
// Enable View Transition transition
enableContentAnimation: true,
enableAppearanceAnimation: true,
},
});
```
Expand Down
21 changes: 20 additions & 1 deletion packages/document/docs/zh/api/config/config-theme.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ export default defineConfig({
- Type: `boolean`
- Default: `false`

在页面切换的时候是否显示转场动画,例如:
在页面切换的时候是否显示转场动画,使用 [View Transition API](https://developer.mozilla.org/docs/Web/API/View_Transitions_API) 实现。例如:

> 转场动画暂时不能配置。
Expand All @@ -557,6 +557,25 @@ export default defineConfig({
});
```

## enableAppearanceAnimation

- Type: `boolean`
- Default: `false`

在浅色和深色主题之间切换时是否有动画效果,使用 [View Transition API](https://developer.mozilla.org/docs/Web/API/View_Transitions_API) 实现。例如:

> 切换动画暂时不能配置。
```ts title="rspress.config.ts"
import { defineConfig } from 'rspress/config';

export default defineConfig({
themeConfig: {
enableAppearanceAnimation: true,
},
});
```

## search

- Type: `boolean`
Expand Down
1 change: 1 addition & 0 deletions packages/document/docs/zh/guide/start/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ export default defineConfig({
themeConfig: {
// 开启 View Transition 过渡
enableContentAnimation: true,
enableAppearanceAnimation: true,
},
});
```
Expand Down
1 change: 1 addition & 0 deletions packages/document/rspress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default defineConfig({
},
themeConfig: {
enableContentAnimation: true,
enableAppearanceAnimation: false,
footer: {
message: '© 2024 Bytedance Inc. All Rights Reserved.',
},
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export function useI18n<T = Record<string, Record<string, string>>>() {

declare global {
interface Document {
startViewTransition: (callback: () => void) => void;
// @ts-ignore view-transition new API type is failed in tsc, but it works in vscode
startViewTransition: (callback: () => void) => any;
}
}

Expand Down
6 changes: 5 additions & 1 deletion packages/shared/src/types/defaultTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,13 @@ export interface Config {
*/
hideNavbar?: 'always' | 'auto' | 'never';
/**
* Whether to enable the animation for translation pages
* Whether to enable view transition animation for pages switching
*/
enableContentAnimation?: boolean;
/**
* Whether to enable view transition animation for the theme
*/
enableAppearanceAnimation?: boolean;
/**
* Enable scroll to top button on documentation
* @default false
Expand Down

This file was deleted.

15 changes: 15 additions & 0 deletions packages/theme-default/src/components/SwitchAppearance/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}

::view-transition-old(root),
.dark::view-transition-new(root) {
z-index: 1;
}

::view-transition-new(root),
.dark::view-transition-old(root) {
z-index: 9999;
}
90 changes: 82 additions & 8 deletions packages/theme-default/src/components/SwitchAppearance/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,94 @@
import { useContext } from 'react';
import { type MouseEvent, useContext } from 'react';
import { ThemeContext } from '@rspress/runtime';
import SunSvg from '@theme-assets/sun';
import MoonSvg from '@theme-assets/moon';
import { SvgWrapper } from '../SvgWrapper';
import siteData from 'virtual-site-data';
import { isUndefined } from 'lodash-es';
import { flushSync } from 'react-dom';
import './index.scss';

const supportAppearanceTransition = () => {
return (
!isUndefined(document) &&
document.startViewTransition &&
!window.matchMedia('(prefers-reduced-motion: reduce)').matches
);
};

// two view-transition name is conflicted, 'flip' and 'root', see https://github.com/web-infra-dev/rspress/pull/1272
const removeClipViewTransition = () => {
const styleDom = document.createElement('style');
styleDom.innerHTML = `
.rspress-doc {
view-transition-name: none !important;
}
`;
document.head.appendChild(styleDom);
return () => {
document.head.removeChild(styleDom);
};
};

export function SwitchAppearance({ onClick }: { onClick?: () => void }) {
const { theme, setTheme } = useContext(ThemeContext);

const handleClick = (event: MouseEvent) => {
const supported = supportAppearanceTransition();
const enabled = siteData?.themeConfig?.enableAppearanceAnimation;

const nextTheme = theme === 'dark' ? 'light' : 'dark';
const isDark = nextTheme === 'dark';

if (supported && enabled) {
const x = event.clientX;
const y = event.clientY;

const endRadius = Math.hypot(
Math.max(x, innerWidth - x + 200),
Math.max(y, innerHeight - y + 200),
);

const dispose = removeClipViewTransition();
const transition = document.startViewTransition(async () => {
flushSync(() => {
setTheme(nextTheme);
onClick?.();
});
});

const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
];
transition.ready.then(() => {
document.documentElement
.animate(
{
clipPath: isDark ? [...clipPath].reverse() : clipPath,
},
{
duration: 400,
easing: 'ease-in',
pseudoElement: isDark
? '::view-transition-old(root)'
: '::view-transition-new(root)',

id: '',
},
)
.finished.then(() => {
dispose();
});
});
} else {
setTheme(nextTheme);
onClick?.();
}
};

return (
<div
onClick={() => {
setTheme(theme === 'dark' ? 'light' : 'dark');
onClick?.();
}}
className="md:mr-2 rspress-nav-appearance"
>
<div onClick={handleClick} className="md:mr-2 rspress-nav-appearance">
<div className="p-1 border border-solid border-gray-300 text-gray-400 cursor-pointer rounded-md hover:border-gray-600 hover:text-gray-600 dark:hover:border-gray-200 dark:hover:text-gray-200 transition-all duration-300 w-7 h-7">
<SvgWrapper
className="dark:hidden"
Expand Down
4 changes: 0 additions & 4 deletions packages/theme-default/src/layout/DocLayout/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
view-transition-name: flip;
}

:global(*) {
view-transition-name: none;
}

@keyframes fade-out {
from {
opacity: 1;
Expand Down

0 comments on commit f9e49cc

Please sign in to comment.