mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-13 22:54:42 -04:00
Add new loading animation
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
85eff24be1
commit
bd58bca39c
7 changed files with 242 additions and 72 deletions
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import LogoColor from '../../../common/hedge-doc-logo/logo_color.svg'
|
|
||||||
import styles from './animations.module.scss'
|
|
||||||
|
|
||||||
export interface HedgeDocLogoProps {
|
|
||||||
animation: AnimationType
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum AnimationType {
|
|
||||||
JUMP = 'animation-jump',
|
|
||||||
SHAKE = 'animation-shake'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows an animated hedgedoc logo.
|
|
||||||
*
|
|
||||||
* @param animation The name of the animation
|
|
||||||
*/
|
|
||||||
export const AnimatedHedgeDocLogo: React.FC<HedgeDocLogoProps> = ({ animation }) => {
|
|
||||||
return (
|
|
||||||
<LogoColor
|
|
||||||
className={`w-auto ${styles[animation]}`}
|
|
||||||
title={'HedgeDoc logo'}
|
|
||||||
alt={'HedgeDoc logo'}
|
|
||||||
height={256}
|
|
||||||
width={256}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*!
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
@import "keyframes";
|
||||||
|
|
||||||
|
.rows {
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.particle {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 1.3em !important;
|
||||||
|
top: calc(50% - 12px);
|
||||||
|
left: calc(50% - 12px);
|
||||||
|
animation: particle 3s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@for $i from 1 through 12 {
|
||||||
|
.row:nth-child(#{$i}) {
|
||||||
|
transform: rotateZ(30deg * ($i - 1));
|
||||||
|
|
||||||
|
@for $j from 1 through 10 {
|
||||||
|
& .particle:nth-child(#{$j}) {
|
||||||
|
opacity: 0;
|
||||||
|
animation-timing-function: ease-out;
|
||||||
|
animation-delay: -$i * 830ms - $j * 600ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
z-index: 1000;
|
||||||
|
position: relative;
|
||||||
|
font-size: 3em;
|
||||||
|
height: 240px;
|
||||||
|
width: 203px;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 4px 4px 0 #3b4045;
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
color: rgb(181, 31, 8);
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: fill 6s infinite;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&, :global(.fa) {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse {
|
||||||
|
animation: 3s pulse infinite;
|
||||||
|
box-shadow: #404040 0 0 200px 100px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
border-radius: 100%;
|
||||||
|
margin: auto;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
.channels {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pulse {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
.overlay {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: rgb(181, 31, 8);
|
||||||
|
animation: 1s shake;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import { createNumberRangeArray } from '../../common/number-range/number-range'
|
||||||
|
import { RandomIcon } from './random-icon'
|
||||||
|
import styles from './animations.module.scss'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a number of {@link RandomIcon random icons in a row}.
|
||||||
|
*/
|
||||||
|
export const IconRow: React.FC = () => {
|
||||||
|
const children = useMemo(() => createNumberRangeArray(5).map((index) => <RandomIcon key={index}></RandomIcon>), [])
|
||||||
|
|
||||||
|
return <div className={styles.row}>{children}</div>
|
||||||
|
}
|
|
@ -1,34 +1,10 @@
|
||||||
/*
|
/*!
|
||||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@keyframes animation-jump {
|
@keyframes shake {
|
||||||
0% {
|
|
||||||
transform: scale(1, 1) translateY(0);
|
|
||||||
}
|
|
||||||
10% {
|
|
||||||
transform: scale(1.1, .9) translateY(0);
|
|
||||||
}
|
|
||||||
30% {
|
|
||||||
transform: scale(.9, 1.1) translateY(-100px);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: scale(1.05, .95) translateY(0);
|
|
||||||
}
|
|
||||||
57% {
|
|
||||||
transform: scale(1, 1) translateY(-7px);
|
|
||||||
}
|
|
||||||
64% {
|
|
||||||
transform: scale(1, 1) translateY(0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: scale(1, 1) translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animation-shake {
|
|
||||||
0% {
|
0% {
|
||||||
transform: translate(1px, 1px) rotate(0deg);
|
transform: translate(1px, 1px) rotate(0deg);
|
||||||
}
|
}
|
||||||
|
@ -64,14 +40,54 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.animation-jump {
|
@keyframes particle {
|
||||||
transform-origin: bottom;
|
0% {
|
||||||
animation-duration: 2s;
|
opacity: 0.3;
|
||||||
animation-iteration-count: infinite;
|
transform: translate(300px, 300px) rotateZ(360deg);
|
||||||
animation-name: animation-jump;
|
border-radius: 0;
|
||||||
animation-timing-function: cubic-bezier(0.280, 0.840, 0.420, 1);
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(120px, 120px) rotateZ(180deg);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0px, 0px) rotateZ(0deg);
|
||||||
|
color: rgb(181, 31, 8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.animation-shake {
|
@keyframes fill {
|
||||||
animation: animation-shake 0.3s ease-in-out;
|
0% {
|
||||||
|
height: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
height: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
100%{
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: #ffffff00 0 0 200px 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
30% {
|
||||||
|
box-shadow: #ffffff33 0 0 200px 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: #ffffff00 0 0 200px 100px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import styles from './animations.module.scss'
|
||||||
|
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
||||||
|
import { IconRow } from './icon-row'
|
||||||
|
import { createNumberRangeArray } from '../../common/number-range/number-range'
|
||||||
|
|
||||||
|
export interface HedgeDocLogoProps {
|
||||||
|
error: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a loading animation.
|
||||||
|
*
|
||||||
|
* @param error Defines if the error animation should be shown instead
|
||||||
|
*/
|
||||||
|
export const LoadingAnimation: React.FC<HedgeDocLogoProps> = ({ error }) => {
|
||||||
|
const iconRows = useMemo(() => createNumberRangeArray(12).map((index) => <IconRow key={index} />), [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`position-relative ${error ? styles.error : ''}`}>
|
||||||
|
<div className={styles.logo}>
|
||||||
|
<div>
|
||||||
|
<ForkAwesomeIcon icon={'pencil'} className={styles.background} size={'5x'}></ForkAwesomeIcon>
|
||||||
|
</div>
|
||||||
|
<div className={`${styles.overlay}`}>
|
||||||
|
<ForkAwesomeIcon icon={'pencil'} size={'5x'}></ForkAwesomeIcon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.pulse}></div>
|
||||||
|
<div className={styles.rows}>{iconRows}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Alert } from 'react-bootstrap'
|
import { Alert } from 'react-bootstrap'
|
||||||
import { AnimatedHedgeDocLogo, AnimationType } from './animated-hedge-doc-logo/animated-hedge-doc-logo'
|
import { LoadingAnimation } from './loading-animation'
|
||||||
import { ShowIf } from '../../common/show-if/show-if'
|
import { ShowIf } from '../../common/show-if/show-if'
|
||||||
import styles from '../application-loader.module.scss'
|
import styles from '../application-loader.module.scss'
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export const LoadingScreen: React.FC<LoadingScreenProps> = ({ failedTaskName })
|
||||||
<div className={`${styles.loader} ${styles.middle} text-light overflow-hidden`}>
|
<div className={`${styles.loader} ${styles.middle} text-light overflow-hidden`}>
|
||||||
<div className='mb-3 text-light'>
|
<div className='mb-3 text-light'>
|
||||||
<span className={`d-block`}>
|
<span className={`d-block`}>
|
||||||
<AnimatedHedgeDocLogo animation={failedTaskName ? AnimationType.SHAKE : AnimationType.JUMP} />
|
<LoadingAnimation error={!!failedTaskName} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<ShowIf condition={!!failedTaskName}>
|
<ShowIf condition={!!failedTaskName}>
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import type { IconName } from '../../common/fork-awesome/types'
|
||||||
|
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
||||||
|
import styles from './animations.module.scss'
|
||||||
|
|
||||||
|
const elements: IconName[] = [
|
||||||
|
'file-text',
|
||||||
|
'markdown',
|
||||||
|
'pencil',
|
||||||
|
'bold',
|
||||||
|
'italic',
|
||||||
|
'align-justify',
|
||||||
|
'tag',
|
||||||
|
'user',
|
||||||
|
'file',
|
||||||
|
'keyboard-o',
|
||||||
|
'cog',
|
||||||
|
'font'
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chooses a random fork awesome icon from a predefined set and renders it.
|
||||||
|
*/
|
||||||
|
export const RandomIcon: React.FC = () => {
|
||||||
|
const icon = useMemo(() => elements[Math.floor(Math.random() * elements.length)], [])
|
||||||
|
|
||||||
|
return <ForkAwesomeIcon icon={icon} className={styles.particle}></ForkAwesomeIcon>
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue