PlAnimateSlide
한쪽 모서리에서 들어오는 내용입니다. 기본 이동 거리가 요소 자신의 크기라서, 정확히 화면 밖에서 시작하고 있어서는 안 될 자리에 반쯤 그려지는 일이 없습니다.
import { PlAnimateSlide } from 'plass-ui';
<div className="overflow-hidden">
<PlAnimateSlide from="right">
<PlCard title="New message">Ada replied to your review.</PlCard>
</PlAnimateSlide>
</div>;import 'package:plass_ui/plass_ui.dart';
const ClipRect(
child: PlAnimateSlide(
from: PlassSide.right,
child: PlCard(title: Text('New message'), child: Text('Ada replied to your review.')),
),
);Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| mode | 'in' | 'out' | 'in' | 들어오는지 나가는지. out은 들어왔을 그 모서리로 나갑니다 |
| from공통 | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | 어느 모서리에서 오는지. 라이브러리 전체가 그렇듯 물리적입니다 — 위에서 내려오는 패널은 어떤 쓰기 방향에서도 위에서 내려옵니다 |
| distance | number | string | '100%' | 얼마나 이동할지 — CSS 길이 또는 픽셀 숫자. '100%'는 요소 자신의 너비나 높이라, 자기 모서리 뒤에서 나타나게 됩니다 |
| fade | boolean | true | 미끄러지면서 함께 나타납니다 |
| duration공통 | number | 360 | 한 번 도는 데 걸리는 시간(ms). CSS 문자열이 아니라 숫자입니다 |
| delay공통 | number | 0 | 시작하기까지 기다리는 시간(ms) |
| easing공통 | string | the house curve | CSS가 쓰는 그대로의 이징 곡선 |
| repeat공통 | number | 'infinite' | 1 | 몇 번 반복할지. 끝없이 돌리려면 Infinity가 아니라 'infinite' — CSS에 그 단어로 그대로 쓰이기 때문입니다 |
| alternate공통 | boolean | false | 한 번 걸러 거꾸로 돌립니다. 반복이 처음으로 튀어 돌아가는 대신 되돌아옵니다 |
| paused공통 | boolean | false | 있는 자리에 멈춰 세웁니다 |
| trigger공통 | 'mount' | 'visible' | 'hover' | 'manual' | 'mount' | 무엇이 시작시키는지. mount는 화면에 올라오자마자, visible은 스크롤되어 보일 때, hover는 포인터나 focus가 닿을 때, manual은 play가 시킬 때만 |
| play공통 | boolean | — | trigger가 manual일 때 실행합니다. false → true가 될 때마다 처음부터 다시 돕니다 |
| once공통 | boolean | true | trigger="visible"에서 처음 한 번만 돌릴지. 끄면 화면에 다시 들어올 때마다 다시 돕니다 |
| threshold공통 | number | 0.2 | trigger="visible"에서 얼마나 보여야 보이는 것으로 칠지, 0에서 1 사이 |
| timeline공통 | 'auto' | 'view' | 'auto' | 무엇이 애니메이션을 진행시키는지 — 시계인지 독자의 스크롤 위치인지. view는 duration, delay, repeat, trigger를 무시하고 range로 달립니다 |
| range공통 | string | 'entry 0% cover 45%' | CSS가 animation-range를 쓰는 그대로. timeline이 view일 때만 읽힙니다 |
| stagger공통 | number | 0 | 자식마다 delay에 더해지는 시간(ms). 0이면 상자 자체가 재생되고, 그 외에는 효과가 자식들로 옮겨 가면서 상자에서는 빠집니다 |
| durationStep공통 | number | 0 | 자식마다 duration에 더해지는 시간(ms). 음수도 되고, 0 아래로는 내려가지 않습니다 |
| reverse공통 | boolean | false | 마지막 자식부터 첫 자식까지 순서를 뒤집습니다. 순서만 뒤집히고 각 자식은 그대로 앞으로 재생됩니다 |
| render | ReactElement | (props, state) => ReactElement | — | <div> 대신 다른 요소로 렌더링합니다 |
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| mode | PlassAnimateMode | PlassAnimateMode.enter | 들어오는지 나가는지. exit는 들어왔을 그 모서리로 나갑니다. in이 Dart의 예약어라 enter/exit입니다 |
| from공통 | PlassSide | PlassSide.bottom | 어느 모서리에서 오는지. 라이브러리 전체가 그렇듯 물리적입니다 — 위에서 내려오는 패널은 어떤 쓰기 방향에서도 위에서 내려옵니다 |
| distance | double? | its own size | 이동 거리(논리 픽셀). null이면 widget 자신의 너비나 높이라, 자기 모서리 뒤에서 나타나게 됩니다 |
| fade | bool | true | 미끄러지면서 함께 나타납니다 |
| duration공통 | Duration | Duration(milliseconds: 360) | 한 번 도는 데 걸리는 시간(ms). CSS 문자열이 아니라 숫자입니다 |
| delay공통 | Duration | Duration.zero | 시작하기까지 기다리는 시간(ms) |
| curve공통 | Curve? | the house curve | CSS가 쓰는 그대로의 이징 곡선 |
| repeat공통 | int? | 1 | 몇 번 반복할지. null이 멈추지 않음을 뜻합니다 — 적을 'infinite'가 없고, -1은 찾아봐야 하는 sentinel입니다 |
| alternate공통 | bool | false | 한 번 걸러 거꾸로 돌립니다. 반복이 처음으로 튀어 돌아가는 대신 되돌아옵니다 |
| paused공통 | bool | false | 있는 자리에 멈춰 세웁니다 |
| trigger공통 | PlassAnimateTrigger | PlassAnimateTrigger.mount | 무엇이 시작시키는지. mount는 화면에 올라오자마자, visible은 스크롤되어 보일 때, hover는 포인터나 focus가 닿을 때, manual은 play가 시킬 때만 |
| play공통 | bool | false | trigger가 manual일 때 실행합니다. false → true가 될 때마다 처음부터 다시 돕니다 |
| once공통 | bool | true | trigger="visible"에서 처음 한 번만 돌릴지. 끄면 화면에 다시 들어올 때마다 다시 돕니다 |
| threshold공통 | double | 0.2 | trigger="visible"에서 얼마나 보여야 보이는 것으로 칠지, 0에서 1 사이 |
| child * | Widget | — | 무엇이 이동하는지 |
네이티브 <div> 속성은 그대로 통과하고, render로 요소 자체를 바꿀 수 있습니다.
distance는 논리 픽셀 단위의 double?이고, 기본값인 null이 widget 자신의 너비나 높이입니다. 적을 CSS 길이가 없습니다. widget 자기 크기에 대한 비율은 FractionalTranslation이 이미 뜻하는 것이고, 거리가 주어지지 않으면 widget이 그것을 씁니다.
from은 라이브러리 전체의 PlassSide가 그렇듯 물리적입니다 — top, right, bottom, left. 위에서 내려오는 패널은 어떤 쓰기 방향에서도 위에서 내려옵니다.
공유되는 열 가지 설정 — duration, delay, easing, repeat, alternate, paused, trigger, play, once, threshold — 은 모든 PlAnimate* 컴포넌트에서 같습니다. trigger의 네 값은 PlAnimateFade 페이지에 있습니다.
세 가지가 더 있고, 이들은 효과를 상자에서 떼어 안의 것들로 옮깁니다. stagger는 각 자식을 자기 위치만큼 뒤로 미루고, durationStep은 자식마다 앞의 것보다 길거나 짧은 재생 시간을 주며, reverse는 집합의 끝에서부터 시작합니다. 키프레임 하나짜리 효과 여섯 개 모두에 있고, PlAnimateFade 페이지에 설명이 있습니다. timeline="view"와 range도 같은 자리에 있고, 효과를 시계가 아니라 독자의 스크롤 위치에 맡깁니다.
Examples
from
네 개의 모서리이고, mode="out"은 도착했을 그 모서리로 나갑니다.
import { PlAnimateSlide, PlChip } from 'plass-ui';
const sides = ['top', 'right', 'bottom', 'left'] as const;
export default function AnimateSlideSides() {
return (
<div className="flex flex-wrap items-center justify-center gap-4">
{sides.map((side) => (
<PlAnimateSlide
key={side}
from={side}
distance={24}
duration={1200}
repeat="infinite"
alternate
>
<PlChip>{side}</PlChip>
</PlAnimateSlide>
))}
</div>
);
}import 'package:flutter/widgets.dart';
import 'package:plass_ui/plass_ui.dart';
class AnimateSlideSides extends StatelessWidget {
const AnimateSlideSides({super.key});
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 16,
runSpacing: 16,
alignment: WrapAlignment.center,
children: <Widget>[
for (final PlassSide side in PlassSide.values)
PlAnimateSlide(
from: side,
distance: 24,
duration: const Duration(milliseconds: 1200),
repeat: null,
alternate: true,
child: PlChip(child: Text(side.name)),
),
],
);
}
}distance
숫자는 픽셀이고, 문자열은 어떤 CSS 길이든 됩니다. '100%'는 요소 자신의 너비나 높이입니다. overflow: hidden인 상자에 넣으면 그 상자의 모서리 뒤에서 패널이 나타나는 효과가 됩니다. 짧은 거리는 다른 몸짓입니다. 등장이 아니라 무언가 바뀌었다고 말하는 툭 침이죠.
import { PlAnimateSlide, PlBox } from 'plass-ui';
export default function AnimateSlideDistance() {
return (
<div className="flex w-full max-w-sm flex-col gap-3 overflow-hidden">
<PlAnimateSlide from="left" distance="100%" duration={1400} repeat="infinite" alternate>
<PlBox size="sm">100% — its own width, so it starts out of frame</PlBox>
</PlAnimateSlide>
<PlAnimateSlide from="left" distance={16} duration={1400} repeat="infinite" alternate>
<PlBox size="sm">16px — a nudge rather than an entrance</PlBox>
</PlAnimateSlide>
</div>
);
}import 'package:flutter/widgets.dart';
import 'package:plass_ui/plass_ui.dart';
class AnimateSlideDistance extends StatelessWidget {
const AnimateSlideDistance({super.key});
@override
Widget build(BuildContext context) {
return const SizedBox(
width: 320,
child: ClipRect(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
spacing: 12,
children: <Widget>[
PlAnimateSlide(
from: PlassSide.left,
duration: Duration(milliseconds: 1400),
repeat: null,
alternate: true,
child: PlBox(
size: PlassSize.sm,
child: Text('no distance — its own width, so it starts out of frame'),
),
),
PlAnimateSlide(
from: PlassSide.left,
distance: 16,
duration: Duration(milliseconds: 1400),
repeat: null,
alternate: true,
child: PlBox(
size: PlassSize.sm,
child: Text('16px — a nudge rather than an entrance'),
),
),
],
),
),
);
}
}Accessibility
prefers-reduced-motion에서는 애니메이션이 통째로 없어지고 내용은 그냥 거기 있습니다.- 실행되는 동안 페이지의 어떤 것도 reflow하지 않습니다. 레이아웃 변화가 아니라
translate이므로 요소 주변은 움직이지 않습니다. - 화면 밖에서 시작하는 slide는 담고 있는 상자가 잘라 내지 않으면 넘칩니다. 잘라 내세요. 아니면 애니메이션이 도는 동안 페이지에 스크롤바가 생깁니다.
- 목록을 훨씬 짧은 거리로 하나씩 지나가게 하려면 PlAnimateAppear를 쓰세요. 그 효과를 만드는 것은 시차이고, 자식마다 slide를 두면 delay를 직접 써야 합니다.
- 플랫폼에서 애니메이션이 꺼져 있으면(
MediaQuery.disableAnimations) 효과가 통째로 없어지고 내용은 그냥 거기 있습니다. - 실행되는 동안 주변의 어떤 것도 다시 레이아웃되지 않습니다. 레이아웃 변화가 아니라 widget을 움직이는 것입니다.
- 화면 밖에서 시작하는 slide는 담고 있는 상자가 잘라 내지 않으면 넘칩니다.
ClipRect로 감싸세요. - 목록을 훨씬 짧은 거리로 하나씩 지나가게 하려면 PlAnimateAppear를 쓰세요.
React 빌드와 다른 점
| React | Flutter | 이유 |
|---|---|---|
distance가 CSS 길이 또는 숫자 | double?, null이 자기 크기 | widget 자기 크기에 대한 비율은 FractionalTranslation이 이미 뜻하는 것이라, 문자열로 적을 것이 없습니다. |
mode="in" | "out" | PlassAnimateMode.enter / .exit | in은 Dart의 예약어입니다. |
wrapper의 overflow: hidden | ClipRect | 같은 잘라 내기에 대한 프레임워크 자신의 이름입니다. |
render | — | Flutter에는 다형적 요소가 없습니다. |
duration, delay가 밀리초 | Duration | 프레임워크에 이미 타입이 있습니다. |
easing이 CSS 문자열 | curve, Curve | 같은 것에 대한 Dart 자신의 이름입니다. |
repeat: number | 'infinite' | int?, null이 멈추지 않음 | 적을 'infinite'가 없고, -1은 caller가 찾아봐야 하는 sentinel입니다. |
trigger="visible"이 IntersectionObserver | 가장 가까운 Scrollable을 봅니다 | 여기에는 observer가 없습니다. 위에 scrollable이 없으면 볼 것이 없으므로 그냥 돕니다. |
prefers-reduced-motion | MediaQuery.disableAnimations | 플랫폼 자신의 신호입니다. |
stagger, durationStep, reverse | — | React 빌드는 효과를 자식들 자신에게 써 넣으므로 호출자의 레이아웃은 그대로입니다. Flutter에는 집합을 배치할 스타일시트가 없어서, 차등을 준 효과는 행이나 열까지 자기가 가져야 합니다. 그것이 바로 PlAnimateAppear이고, 그것을 여섯 개 더 만드는 일이 됩니다. |
timeline="view" | — | animation-timeline은 여기에 대응물이 없는 CSS 속성입니다. Flutter에서 스크롤 연동 효과는 ScrollPosition으로 구동하는 AnimationController이고, 위젯이 prop으로 받는 것이 아니라 애플리케이션 자신의 배선입니다. |
className, style | — | 통과시킬 class 목록도 style 속성도 없습니다. |