Transition: Tiny updates

This commit is contained in:
Alexander Zinchuk 2023-04-15 13:50:41 +02:00
parent 01cf273b92
commit c467da8680
3 changed files with 154 additions and 114 deletions

View File

@ -1,9 +1,12 @@
.Transition { .Transition {
position: relative; position: relative;
width: 100%;
height: 100%;
> .Transition__slide { > .Transition__slide {
width: 100%; width: 100%;
height: 100%; height: 100%;
animation-fill-mode: forwards !important; animation-fill-mode: forwards !important;
&.from, &.from,
@ -25,9 +28,6 @@
transition: none !important; transition: none !important;
} }
/*
* slide
*/
&.slide-optimized, &.slide-optimized,
&.slide-optimized-rtl { &.slide-optimized-rtl {
contain: strict; contain: strict;
@ -40,8 +40,9 @@
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
transition: transform var(--slide-transition);
transform: scale(0); transform: scale(0);
transition: transform var(--slide-transition);
} }
} }
@ -109,10 +110,6 @@
} }
} }
/*
* slide-vertical
*/
&.slide-vertical { &.slide-vertical {
> .to { > .to {
transform: translateY(100%); transform: translateY(100%);
@ -145,10 +142,6 @@
} }
} }
/*
* slide-vertical-fade
*/
&.slide-vertical-fade { &.slide-vertical-fade {
> .to { > .to {
transform: translateY(100%); transform: translateY(100%);
@ -181,10 +174,6 @@
} }
} }
/*
* mv-slide
*/
&.mv-slide { &.mv-slide {
> .Transition__slide > div { > .Transition__slide > div {
animation-fill-mode: forwards !important; animation-fill-mode: forwards !important;
@ -221,19 +210,18 @@
} }
} }
/*
* slide-fade
*/
&.slide-fade { &.slide-fade {
> .from { > .from {
transform-origin: left;
transform: translateX(0); transform: translateX(0);
transform-origin: left;
opacity: 1; opacity: 1;
} }
> .to { > .to {
transform-origin: left;
transform: translateX(1.5rem); transform: translateX(1.5rem);
transform-origin: left;
opacity: 0; opacity: 0;
} }
@ -251,39 +239,40 @@
&.slide-fade.backwards { &.slide-fade.backwards {
> .from { > .from {
transform: translateX(0); transform: translateX(0);
opacity: 1; opacity: 1;
} }
> .to { > .to {
transform: translateX(-1.5rem); transform: translateX(-1.5rem);
opacity: 0; opacity: 0;
} }
&.animating { &.animating {
> .from { > .from {
animation: fade-in-backwards-opacity var(--slide-transition), animation: fade-in-backwards-opacity var(--slide-transition),
slide-fade-in-backwards-move var(--slide-transition); slide-fade-in-backwards-move var(--slide-transition);
} }
> .to { > .to {
animation: fade-out-backwards-opacity var(--slide-transition), animation: fade-out-backwards-opacity var(--slide-transition),
slide-fade-out-backwards-move var(--slide-transition); slide-fade-out-backwards-move var(--slide-transition);
} }
} }
} }
/*
* zoom-fade
*/
&.zoom-fade { &.zoom-fade {
> .from { > .from {
transform-origin: center;
transform: scale(1); transform: scale(1);
transform-origin: center;
opacity: 1; opacity: 1;
} }
> .to { > .to {
transform-origin: center; transform-origin: center;
opacity: 0; opacity: 0;
// We can omit `transform: scale(1.1);` here because `opacity` is 0. // We can omit `transform: scale(1.1);` here because `opacity` is 0.
// We need to for proper position calculation in `InfiniteScroll`. // We need to for proper position calculation in `InfiniteScroll`.
@ -320,9 +309,6 @@
} }
} }
/*
* fade
*/
&.fade { &.fade {
> .from { > .from {
opacity: 1; opacity: 1;
@ -343,12 +329,49 @@
} }
} }
/* &.semi-fade {
* slide-layers > .Transition__slide {
*/ isolation: isolate;
}
> .from {
opacity: 1;
}
> .to {
opacity: 0;
}
&.animating {
> .to {
animation: fade-in-opacity 250ms ease;
}
}
}
&.semi-fade.backwards {
> .from {
opacity: 1;
}
> .to {
opacity: 1;
}
&.animating {
> .from {
animation: fade-in-backwards-opacity 250ms ease;
}
> .to {
animation: none !important;
}
}
}
&.slide-layers { &.slide-layers {
--background-color: var(--color-background); --background-color: var(--color-background);
background: black !important; background: black !important;
> .Transition__slide { > .Transition__slide {
@ -373,6 +396,7 @@
&.slide-layers.backwards { &.slide-layers.backwards {
> .to { > .to {
transform: translateX(-20%); transform: translateX(-20%);
opacity: 0.75; opacity: 0.75;
} }
@ -387,18 +411,15 @@
} }
} }
/*
* push-slide
*/
&.push-slide { &.push-slide {
> .Transition__slide { > .Transition__slide {
background: var(--color-background); background: var(--color-background);
} }
> .from { > .from {
transform-origin: center;
transform: scale(1); transform: scale(1);
transform-origin: center;
opacity: 1; opacity: 1;
.custom-scroll { .custom-scroll {
@ -428,6 +449,7 @@
&.push-slide.backwards { &.push-slide.backwards {
> .to { > .to {
transform: scale(0.7); transform: scale(0.7);
opacity: 0; opacity: 0;
} }
@ -442,9 +464,6 @@
} }
} }
/*
* slide-fade
*/
&.reveal { &.reveal {
> .to { > .to {
clip-path: inset(0 100% 0 0); clip-path: inset(0 100% 0 0);
@ -478,9 +497,6 @@
} }
} }
/*
* common
*/
@keyframes fade-in-opacity { @keyframes fade-in-opacity {
0% { 0% {
opacity: 0; opacity: 0;
@ -517,9 +533,6 @@
} }
} }
/*
* slide
*/
@keyframes slide-in { @keyframes slide-in {
0% { 0% {
transform: translateX(100%); transform: translateX(100%);
@ -556,9 +569,6 @@
} }
} }
/*
* slide-vertical
*/
@keyframes slide-vertical-in { @keyframes slide-vertical-in {
0% { 0% {
transform: translateY(100%); transform: translateY(100%);
@ -595,17 +605,15 @@
} }
} }
/*
* slide-vertical-fade
*/
@keyframes slide-vertical-fade-in { @keyframes slide-vertical-fade-in {
0% { 0% {
transform: translateY(100%); transform: translateY(100%);
opacity: 0; opacity: 0;
} }
100% { 100% {
transform: translateY(0); transform: translateY(0);
opacity: 1; opacity: 1;
} }
} }
@ -613,10 +621,12 @@
@keyframes slide-vertical-fade-out { @keyframes slide-vertical-fade-out {
0% { 0% {
transform: translateY(0); transform: translateY(0);
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: translateY(-100%); transform: translateY(-100%);
opacity: 0; opacity: 0;
} }
} }
@ -624,10 +634,12 @@
@keyframes slide-vertical-fade-in-backwards { @keyframes slide-vertical-fade-in-backwards {
0% { 0% {
transform: translateY(0); transform: translateY(0);
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: translateY(100%); transform: translateY(100%);
opacity: 0; opacity: 0;
} }
} }
@ -635,18 +647,16 @@
@keyframes slide-vertical-fade-out-backwards { @keyframes slide-vertical-fade-out-backwards {
0% { 0% {
transform: translateY(-100%); transform: translateY(-100%);
opacity: 0; opacity: 0;
} }
100% { 100% {
transform: translateY(0); transform: translateY(0);
opacity: 1; opacity: 1;
} }
} }
/*
* mv-slide
*/
@keyframes mv-slide-in { @keyframes mv-slide-in {
0% { 0% {
transform: translateX(100vw); transform: translateX(100vw);
@ -683,9 +693,6 @@
} }
} }
/*
* slide-fade
*/
@keyframes slide-fade-in-move { @keyframes slide-fade-in-move {
0% { 0% {
transform: translateX(1.5rem); transform: translateX(1.5rem);
@ -722,9 +729,6 @@
} }
} }
/*
* zoom-fade
*/
@keyframes zoom-fade-in-move { @keyframes zoom-fade-in-move {
0% { 0% {
transform: scale(1.1); transform: scale(1.1);
@ -752,16 +756,15 @@
} }
} }
/*
* slide-layers
*/
@keyframes slide-layers-out { @keyframes slide-layers-out {
0% { 0% {
transform: translateX(0); transform: translateX(0);
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: translateX(-20%); transform: translateX(-20%);
opacity: calc(1 - var(--layer-blackout-opacity)); opacity: calc(1 - var(--layer-blackout-opacity));
} }
} }
@ -769,25 +772,25 @@
@keyframes slide-layers-out-backwards { @keyframes slide-layers-out-backwards {
0% { 0% {
transform: translateX(-20%); transform: translateX(-20%);
opacity: calc(1 - var(--layer-blackout-opacity)); opacity: calc(1 - var(--layer-blackout-opacity));
} }
100% { 100% {
transform: translateX(0); transform: translateX(0);
opacity: 1; opacity: 1;
} }
} }
/*
* push-slide
*/
@keyframes push-out { @keyframes push-out {
0% { 0% {
transform: scale(1); transform: scale(1);
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: scale(0.7); transform: scale(0.7);
opacity: 0; opacity: 0;
} }
} }
@ -795,17 +798,16 @@
@keyframes push-out-backwards { @keyframes push-out-backwards {
0% { 0% {
transform: scale(0.7); transform: scale(0.7);
opacity: 0; opacity: 0;
} }
100% { 100% {
transform: scale(1); transform: scale(1);
opacity: 1; opacity: 1;
} }
} }
/*
* slide
*/
@keyframes slide-in-200 { @keyframes slide-in-200 {
0% { 0% {
transform: translateX(200%); transform: translateX(200%);
@ -824,9 +826,6 @@
} }
} }
/*
* slide
*/
@keyframes reveal-in { @keyframes reveal-in {
0% { 0% {
clip-path: inset(0 100% 0 0); clip-path: inset(0 100% 0 0);
@ -836,9 +835,6 @@
} }
} }
/*
* slide
*/
@keyframes reveal-in-backwards { @keyframes reveal-in-backwards {
0% { 0% {
clip-path: inset(0 0 0 0); clip-path: inset(0 0 0 0);

View File

@ -1,5 +1,4 @@
import type { RefObject } from 'react'; import type { RefObject } from 'react';
import type { FC } from '../../lib/teact/teact';
import React, { useLayoutEffect, useRef } from '../../lib/teact/teact'; import React, { useLayoutEffect, useRef } from '../../lib/teact/teact';
import { addExtraClass, removeExtraClass, toggleExtraClass } from '../../lib/teact/teact-dom'; import { addExtraClass, removeExtraClass, toggleExtraClass } from '../../lib/teact/teact-dom';
import { getGlobal } from '../../global'; import { getGlobal } from '../../global';
@ -19,10 +18,11 @@ export type ChildrenFn = (isActive: boolean, isFrom: boolean, currentKey: number
export type TransitionProps = { export type TransitionProps = {
ref?: RefObject<HTMLDivElement>; ref?: RefObject<HTMLDivElement>;
activeKey: number; activeKey: number;
nextKey?: number;
name: ( name: (
'none' | 'slide' | 'slide-rtl' | 'mv-slide' | 'slide-fade' | 'zoom-fade' | 'slide-layers' 'none' | 'slide' | 'slide-rtl' | 'mv-slide' | 'slide-fade' | 'zoom-fade' | 'slide-layers'
| 'fade' | 'push-slide' | 'reveal' | 'slide-optimized' | 'slide-optimized-rtl' | 'slide-vertical' | 'fade' | 'push-slide' | 'reveal' | 'slide-optimized' | 'slide-optimized-rtl' | 'semi-fade'
| 'slide-vertical-fade' | 'slide-vertical' | 'slide-vertical-fade'
); );
direction?: 'auto' | 'inverse' | 1 | -1; direction?: 'auto' | 'inverse' | 1 | -1;
renderCount?: number; renderCount?: number;
@ -32,9 +32,9 @@ export type TransitionProps = {
// Used by async components which are usually remounted during first animation // Used by async components which are usually remounted during first animation
shouldWrap?: boolean; shouldWrap?: boolean;
wrapExceptionKey?: number; wrapExceptionKey?: number;
isDisabled?: boolean;
id?: string; id?: string;
className?: string; className?: string;
slideClassName?: string;
onStart?: NoneToVoidFunction; onStart?: NoneToVoidFunction;
onStop?: NoneToVoidFunction; onStop?: NoneToVoidFunction;
children: React.ReactNode | ChildrenFn; children: React.ReactNode | ChildrenFn;
@ -48,9 +48,10 @@ const CLASSES = {
afterSlides: 'Transition__after-slides', afterSlides: 'Transition__after-slides',
}; };
const Transition: FC<TransitionProps> = ({ function Transition({
ref, ref,
activeKey, activeKey,
nextKey,
name, name,
direction = 'auto', direction = 'auto',
renderCount, renderCount,
@ -61,11 +62,12 @@ const Transition: FC<TransitionProps> = ({
wrapExceptionKey, wrapExceptionKey,
id, id,
className, className,
slideClassName,
onStart, onStart,
onStop, onStop,
children, children,
afterChildren, afterChildren,
}) => { }: TransitionProps) {
// No need for a container to update on change // No need for a container to update on change
const { animationLevel } = getGlobal().settings.byKey; const { animationLevel } = getGlobal().settings.byKey;
const currentKeyRef = useRef<number>(); const currentKeyRef = useRef<number>();
@ -87,6 +89,9 @@ const Transition: FC<TransitionProps> = ({
} }
rendersRef.current[activeKey] = children; rendersRef.current[activeKey] = children;
if (nextKey) {
rendersRef.current[nextKey] = children;
}
useLayoutEffect(() => { useLayoutEffect(() => {
function cleanup() { function cleanup() {
@ -102,30 +107,38 @@ const Transition: FC<TransitionProps> = ({
} }
const container = containerRef.current!; const container = containerRef.current!;
const childElements = (Array.from(container.children) as HTMLElement[]) const keys = Object.keys(rendersRef.current).map(Number);
.filter((el) => !el.classList.contains(CLASSES.afterSlides)); const prevActiveIndex = renderCount ? prevActiveKey : keys.indexOf(prevActiveKey);
const activeIndex = renderCount ? activeKey : keys.indexOf(activeKey);
childElements.forEach((el) => {
addExtraClass(el, CLASSES.slide);
});
if (childElements.length === 1 && !activeKeyChanged) {
const firstChild = childElements[0];
if (name.startsWith('slide-optimized')) {
firstChild.style.transition = 'none';
firstChild.style.transform = 'translate3d(0, 0, 0)';
}
addExtraClass(firstChild, CLASSES.active);
return;
}
const childNodes = Array.from(container.childNodes) const childNodes = Array.from(container.childNodes)
.filter((el) => !(el instanceof HTMLElement && el.classList.contains(CLASSES.afterSlides))); .filter((el) => !(el instanceof HTMLElement && el.classList.contains(CLASSES.afterSlides)));
if (!childNodes.length) {
return;
}
const childElements = (Array.from(container.children) as HTMLElement[])
.filter((el) => !el.classList.contains(CLASSES.afterSlides));
childElements.forEach((el) => {
addExtraClass(el, CLASSES.slide);
if (slideClassName) {
addExtraClass(el, slideClassName);
}
});
if (!activeKeyChanged) {
if (childElements.length === 1 || (nextKey !== undefined && childElements.length === 2)) {
const firstChild = childNodes[activeIndex] as HTMLElement;
if (name.startsWith('slide-optimized')) {
firstChild.style.transition = 'none';
firstChild.style.transform = 'translate3d(0, 0, 0)';
}
addExtraClass(firstChild, CLASSES.active);
}
if (!activeKeyChanged || !childNodes.length) {
return; return;
} }
@ -137,10 +150,6 @@ const Transition: FC<TransitionProps> = ({
|| (direction === 'inverse' && prevActiveKey < activeKey) || (direction === 'inverse' && prevActiveKey < activeKey)
); );
const keys = Object.keys(rendersRef.current).map(Number);
const prevActiveIndex = renderCount ? prevActiveKey : keys.indexOf(prevActiveKey);
const activeIndex = renderCount ? activeKey : keys.indexOf(activeKey);
if (name === 'slide-optimized' || name === 'slide-optimized-rtl') { if (name === 'slide-optimized' || name === 'slide-optimized-rtl') {
performSlideOptimized( performSlideOptimized(
animationLevel, animationLevel,
@ -243,6 +252,7 @@ const Transition: FC<TransitionProps> = ({
}); });
}, [ }, [
activeKey, activeKey,
nextKey,
prevActiveKey, prevActiveKey,
activeKeyChanged, activeKeyChanged,
direction, direction,
@ -252,6 +262,7 @@ const Transition: FC<TransitionProps> = ({
renderCount, renderCount,
shouldRestoreHeight, shouldRestoreHeight,
shouldCleanup, shouldCleanup,
slideClassName,
cleanupExceptionKey, cleanupExceptionKey,
animationLevel, animationLevel,
forceUpdate, forceUpdate,
@ -307,7 +318,7 @@ const Transition: FC<TransitionProps> = ({
{contents} {contents}
</div> </div>
); );
}; }
export default Transition; export default Transition;

View File

@ -700,7 +700,18 @@ function updateClassName(element: HTMLElement, value: string) {
element.className = extraArray.join(' '); element.className = extraArray.join(' ');
} }
export function addExtraClass(element: HTMLElement, className: string) { export function addExtraClass(element: HTMLElement, className: string, forceSingle = false) {
if (!forceSingle) {
const classNames = className.split(' ');
if (className.length > 1) {
classNames.forEach((cn) => {
addExtraClass(element, cn, true);
});
return;
}
}
element.classList.add(className); element.classList.add(className);
const classList = extraClasses.get(element); const classList = extraClasses.get(element);
@ -711,7 +722,18 @@ export function addExtraClass(element: HTMLElement, className: string) {
} }
} }
export function removeExtraClass(element: HTMLElement, className: string) { export function removeExtraClass(element: HTMLElement, className: string, forceSingle = false) {
if (!forceSingle) {
const classNames = className.split(' ');
if (className.length > 1) {
classNames.forEach((cn) => {
removeExtraClass(element, cn, true);
});
return;
}
}
element.classList.remove(className); element.classList.remove(className);
const classList = extraClasses.get(element); const classList = extraClasses.get(element);
@ -724,7 +746,18 @@ export function removeExtraClass(element: HTMLElement, className: string) {
} }
} }
export function toggleExtraClass(element: HTMLElement, className: string, force?: boolean) { export function toggleExtraClass(element: HTMLElement, className: string, force?: boolean, forceSingle = false) {
if (!forceSingle) {
const classNames = className.split(' ');
if (className.length > 1) {
classNames.forEach((cn) => {
toggleExtraClass(element, cn, force, true);
});
return;
}
}
element.classList.toggle(className, force); element.classList.toggle(className, force);
if (element.classList.contains(className)) { if (element.classList.contains(className)) {