mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-11 20:08:37 +00:00
fix(core): impl ai-island animation via css transform (#7458)
This commit is contained in:
@@ -8,30 +8,9 @@ import {
|
|||||||
aiIslandAnimationBg,
|
aiIslandAnimationBg,
|
||||||
aiIslandBtn,
|
aiIslandBtn,
|
||||||
aiIslandWrapper,
|
aiIslandWrapper,
|
||||||
borderAngle1,
|
gradient,
|
||||||
borderAngle2,
|
|
||||||
borderAngle3,
|
|
||||||
} from './styles.css';
|
} from './styles.css';
|
||||||
|
|
||||||
if (
|
|
||||||
typeof window !== 'undefined' &&
|
|
||||||
window.CSS &&
|
|
||||||
window.CSS.registerProperty
|
|
||||||
) {
|
|
||||||
const getName = (nameWithVar: string) => nameWithVar.slice(4, -1);
|
|
||||||
const registerAngle = (varName: string, initialValue: number) => {
|
|
||||||
window.CSS.registerProperty({
|
|
||||||
name: getName(varName),
|
|
||||||
syntax: '<angle>',
|
|
||||||
inherits: false,
|
|
||||||
initialValue: `${initialValue}deg`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
registerAngle(borderAngle1, 0);
|
|
||||||
registerAngle(borderAngle2, 90);
|
|
||||||
registerAngle(borderAngle3, 180);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AIIsland = () => {
|
export const AIIsland = () => {
|
||||||
// to make sure ai island is hidden first and animate in
|
// to make sure ai island is hidden first and animate in
|
||||||
const [hide, setHide] = useState(true);
|
const [hide, setHide] = useState(true);
|
||||||
@@ -52,7 +31,13 @@ export const AIIsland = () => {
|
|||||||
data-hide={hide}
|
data-hide={hide}
|
||||||
data-animation={!aiChatHasEverOpened}
|
data-animation={!aiChatHasEverOpened}
|
||||||
>
|
>
|
||||||
<div className={aiIslandAnimationBg} />
|
{aiChatHasEverOpened ? null : (
|
||||||
|
<div className={aiIslandAnimationBg}>
|
||||||
|
<div className={gradient} />
|
||||||
|
<div className={gradient} />
|
||||||
|
<div className={gradient} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
className={aiIslandBtn}
|
className={aiIslandBtn}
|
||||||
data-testid="ai-island"
|
data-testid="ai-island"
|
||||||
|
|||||||
@@ -51,14 +51,8 @@ const brightGreen = createVar('bright-green');
|
|||||||
const brightRed = createVar('bright-red');
|
const brightRed = createVar('bright-red');
|
||||||
const borderWidth = createVar('border-width');
|
const borderWidth = createVar('border-width');
|
||||||
|
|
||||||
const rotateBg1 = keyframes({
|
const rotateBg = keyframes({
|
||||||
to: { [borderAngle1.slice(4, -1)]: '360deg' },
|
to: { transform: 'rotate(360deg)' },
|
||||||
});
|
|
||||||
const rotateBg2 = keyframes({
|
|
||||||
to: { [borderAngle2.slice(4, -1)]: '450deg' },
|
|
||||||
});
|
|
||||||
const rotateBg3 = keyframes({
|
|
||||||
to: { [borderAngle3.slice(4, -1)]: '540deg' },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const aiIslandAnimationBg = style({
|
export const aiIslandAnimationBg = style({
|
||||||
@@ -68,6 +62,7 @@ export const aiIslandAnimationBg = style({
|
|||||||
left: 0,
|
left: 0,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
|
overflow: 'hidden',
|
||||||
|
|
||||||
vars: {
|
vars: {
|
||||||
[borderAngle1]: '0deg',
|
[borderAngle1]: '0deg',
|
||||||
@@ -79,21 +74,6 @@ export const aiIslandAnimationBg = style({
|
|||||||
[borderWidth]: '1.5px',
|
[borderWidth]: '1.5px',
|
||||||
},
|
},
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
backgroundImage: `conic-gradient(from ${borderAngle1} at 50% 50%,
|
|
||||||
transparent,
|
|
||||||
${brightBlue} 10%,
|
|
||||||
transparent 30%,
|
|
||||||
transparent),
|
|
||||||
conic-gradient(from ${borderAngle2} at 50% 50%,
|
|
||||||
transparent,
|
|
||||||
${brightGreen} 10%,
|
|
||||||
transparent 60%,
|
|
||||||
transparent),
|
|
||||||
conic-gradient(from ${borderAngle3} at 50% 50%,
|
|
||||||
transparent,
|
|
||||||
${brightRed} 10%,
|
|
||||||
transparent 50%,
|
|
||||||
transparent)`,
|
|
||||||
|
|
||||||
selectors: {
|
selectors: {
|
||||||
[`${aiIslandWrapper}[data-animation="true"] &`]: {
|
[`${aiIslandWrapper}[data-animation="true"] &`]: {
|
||||||
@@ -101,7 +81,44 @@ export const aiIslandAnimationBg = style({
|
|||||||
height: `calc(100% + 2 * ${borderWidth})`,
|
height: `calc(100% + 2 * ${borderWidth})`,
|
||||||
top: `calc(-1 * ${borderWidth})`,
|
top: `calc(-1 * ${borderWidth})`,
|
||||||
left: `calc(-1 * ${borderWidth})`,
|
left: `calc(-1 * ${borderWidth})`,
|
||||||
animation: `${rotateBg1} 3s linear infinite, ${rotateBg2} 8s linear infinite, ${rotateBg3} 13s linear infinite`,
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const gradient = style({
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
borderRadius: 'inherit',
|
||||||
|
animationName: rotateBg,
|
||||||
|
animationIterationCount: 'infinite',
|
||||||
|
animationTimingFunction: 'linear',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
willChange: 'transform',
|
||||||
|
selectors: {
|
||||||
|
[`&:nth-of-type(1)`]: {
|
||||||
|
animationDuration: '3s',
|
||||||
|
backgroundImage: `conic-gradient(from ${borderAngle1} at 50% 50%,
|
||||||
|
transparent, ${brightBlue} 10%,
|
||||||
|
transparent 30%,
|
||||||
|
transparent
|
||||||
|
)`,
|
||||||
|
},
|
||||||
|
[`&:nth-of-type(2)`]: {
|
||||||
|
animationDuration: '8s',
|
||||||
|
backgroundImage: `conic-gradient(from ${borderAngle2} at 50% 50%,
|
||||||
|
transparent, ${brightGreen} 10%,
|
||||||
|
transparent 60%,
|
||||||
|
transparent
|
||||||
|
)`,
|
||||||
|
},
|
||||||
|
[`&:nth-of-type(3)`]: {
|
||||||
|
animationDuration: '13s',
|
||||||
|
backgroundImage: `conic-gradient(from ${borderAngle3} at 50% 50%,
|
||||||
|
transparent, ${brightRed} 10%,
|
||||||
|
transparent 50%,
|
||||||
|
transparent
|
||||||
|
)`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { GlobalState } from '@toeverything/infra';
|
import type { GlobalState } from '@toeverything/infra';
|
||||||
import { Entity, LiveData } from '@toeverything/infra';
|
import { Entity, LiveData } from '@toeverything/infra';
|
||||||
|
import { combineLatest } from 'rxjs';
|
||||||
|
|
||||||
import type { SidebarTabName } from '../../multi-tab-sidebar';
|
import type { SidebarTabName } from '../../multi-tab-sidebar';
|
||||||
import { RightSidebarView } from './right-sidebar-view';
|
import { RightSidebarView } from './right-sidebar-view';
|
||||||
@@ -14,11 +15,13 @@ export class RightSidebar extends Entity {
|
|||||||
constructor(private readonly globalState: GlobalState) {
|
constructor(private readonly globalState: GlobalState) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
const sub = this.activeTabName$.subscribe(name => {
|
const sub = combineLatest([this.activeTabName$, this.isOpen$]).subscribe(
|
||||||
if (name === 'chat') {
|
([name, open]) => {
|
||||||
this.globalState.set(RIGHT_SIDEBAR_AI_HAS_EVER_OPENED_KEY, true);
|
if (name === 'chat' && open) {
|
||||||
|
this.globalState.set(RIGHT_SIDEBAR_AI_HAS_EVER_OPENED_KEY, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
this._disposables.push(() => sub.unsubscribe());
|
this._disposables.push(() => sub.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user