PlAnimateFade
불투명도만으로 도착하거나 떠나는 내용입니다. 아무것도 움직이지 않으니 reflow도 없고 다시 샘플링되는 것도 없습니다. 어떤 크기의 본문 위에서도 안전한 유일한 등장입니다.
import { PlAnimateFade } from 'plass-ui';
<PlAnimateFade>
<p>Two services restarted, no errors.</p>
</PlAnimateFade>;
<PlAnimateFade trigger="visible" duration={600}>
<PlCard title="Usage">…</PlCard>
</PlAnimateFade>;import 'package:plass_ui/plass_ui.dart';
const PlAnimateFade(child: Text('Two services restarted, no errors.'));
const PlAnimateFade(
trigger: PlassAnimateTrigger.visible,
duration: Duration(milliseconds: 600),
child: PlCard(title: Text('Usage'), child: Text('…')),
);Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| mode | 'in' | 'out' | 'in' | 내용이 도착하는지 떠나는지. out은 같은 키프레임을 거꾸로 돌린 것이고, 끝난 자리에 그대로 붙들려 있습니다 |
| from | number | 0 | 시작하는 불투명도, 0과 1 사이. 완전히 사라지면 안 되는 내용이라면 올려 잡으세요 |
| duration공통 | number | 300 | 한 번 도는 데 걸리는 시간(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 | double | 0 | 시작하는 불투명도, 0과 1 사이. 완전히 사라지면 안 되는 내용이라면 올려 잡으세요 |
| duration공통 | Duration | Duration(milliseconds: 300) | 한 번 도는 데 걸리는 시간(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로 요소 자체를 바꿀 수 있습니다. <section>이든 <li>든 주변 마크업이 필요로 하는 요소로 바꿉니다.
duration과 delay는 Duration이고 curve는 Curve입니다. repeat은 int?이며 null이 멈추지 않음을 뜻합니다. 적을 'infinite'가 없습니다.
공유되는 열 가지 설정(duration, delay, easing, repeat, alternate, paused, trigger, play, once, threshold)은 모든 PlAnimate* 컴포넌트에서 같고, 각각에서 같은 것을 뜻합니다. 라이브러리 전체의 공유 스타일 축이 무엇을 뜻하는지는 prop 규칙에 있습니다.
세 가지가 더 있고, 이들은 효과를 상자에서 떼어 안의 것들로 옮깁니다. stagger, durationStep, reverse. 아래 자식들을 하나씩 떼어 놓기를 보세요.
Examples
trigger
들어오는 방법 네 가지이고, 공유 설정이 있는 이유이기도 합니다. mount는 caller에게 아무것도 요구하지 않습니다. visible은 요소가 화면 안으로 스크롤될 때까지 기다립니다. 기다리는 동안 자기 첫 프레임에 멈춰 있으므로, 다 그려져 있다가 도착하는 순간 깜빡 사라지는 일이 없습니다. hover는 포인터와 focus 양쪽에서 시작합니다. 그러지 않으면 마우스가 없는 사람에게는 닿지 않는 효과가 됩니다. manual은 혼자 돌지 않고, play가 false에서 true로 바뀔 때마다 처음부터 다시 돕니다.
mode
out은 두 번째 애니메이션이 아니라 같은 키프레임을 거꾸로 돌린 것입니다. 그래서 비용이 들지 않고, 그래서 끝난 자리에 붙들려 있습니다. 사라진 요소는 사라진 채로 남고, 실행이 끝났다고 화면으로 튀어 돌아오지 않습니다.
delay
요소마다 다른 delay가 여러 개를 하나의 순서로 만듭니다. 목록의 자식들이 같은 효과를 차례로 받는 경우라면 PlAnimateAppear가 단계를 대신 세어 줍니다.
timeline
timeline="view"는 효과를 시계가 아니라 독자의 스크롤 위치에 맡깁니다. 어느 순간에 일어나는 일이기를 그만두고, 요소가 스크롤포트 안 어디에 있는지를 따라가는 일이 됩니다. 그래서 위로 되감아 스크롤하면 거꾸로 재생되고, 중간에서 멈춘 독자에게는 중간에서 멈춰 있습니다.
설정 네 가지는 뜻을 잃어서, 어중간하게 동작하는 대신 무시됩니다. duration, delay, repeat은 모두 시계의 것이고 trigger도 마찬가지입니다. 스크롤 위치가 곧 트리거이기 때문입니다. duration을 대신하는 것은 range입니다. CSS가 animation-range를 쓰는 방식 그대로이고, 기본값 entry 0% cover 45%는 요소가 화면 한가운데 닿을 때가 아니라 아직 들어오는 중에 끝납니다.
animation-timeline이 없는 브라우저는 시계 기반 1회 재생으로 폴백하므로 내용은 그대로 도착합니다. degraded는 되어도 blank는 안 됩니다. 이것이 이 기능이 JavaScript로 재는 무언가가 아니라 @supports 뒤의 선언 두 줄인 이유입니다.
stagger가 있는 여섯 효과에 똑같이 있고, 없는 넷에서 똑같이 없습니다. animation-timeline은 키프레임이 도는 요소의 속성인데, marquee의 움직임은 복제된 트랙에 있고 typewriter의 것은 애초에 키프레임이 아니며 lighting의 것은 pseudo-element에 있습니다. 끝이 없는 장식에는 스크롤 위치가 진행시켜 데려갈 도착점도 없습니다.
제공하지 않습니다. animation-timeline은 여기에 대응물이 없는 CSS 속성입니다. Flutter에서 스크롤 연동 효과는 ScrollPosition으로 구동하는 AnimationController이고, 그것은 위젯이 prop으로 받을 수 있는 것이 아니라 애플리케이션 자신의 배선입니다.
자식들을 하나씩 떼어 놓기
stagger는 효과를 상자에서 떼어 안의 것들로 옮겨, 하나씩 차례로 재생하게 합니다. 각 자식의 delay에 더해지는 밀리초이고, 기본값 0은 상자 자체를 재생합니다. 하나를 감쌀 때는 그것이 계속 옳습니다.
켜지는 순간 상자는 애니메이션을 완전히 멈춥니다. 자식 여덟이 나타나는 위에서 상자까지 나타나면 같은 내용을 두 번 나타내는 것이고, 두 번째는 공짜가 아닙니다.
durationStep은 자식마다 앞의 것보다 긴(음수면 짧은) 재생 시간을 주며, 0 아래로는 내려가지 않습니다. reverse는 집합의 끝에서부터 시작합니다. 순서만 뒤집히고 그 외에는 아무것도 바뀌지 않습니다. 거꾸로 도는 효과는 mode="out"이기 때문입니다.
간격은 자식 하나마다이므로 무엇을 넘기는지가 중요합니다. 자식 다섯은 다섯 단계이고, 다섯 개를 담은 자식 하나는 한 단계입니다. 집합의 일부를 빼는 방법도 이것입니다. 묶으세요.
애니메이션은 자식을 감싼 래퍼가 아니라 자식 자신에게 쓰이므로, <li> 줄은 <li> 줄로 남고 그리드의 칸은 그리드의 직계 자식으로 남습니다. 대가는 자식이 className과 style을 받아야 한다는 것입니다. 받지 않는 자식은 애니메이션되지 않습니다. 문자열 하나는 쓸 요소가 없으므로, 유일하게 <span>으로 감싸집니다.
키프레임 하나짜리 효과 여섯 개가 모두 이 셋을 받습니다. PlAnimateMarquee, PlAnimateHeadline, PlAnimateTyping, PlAnimateLighting은 받지 않습니다. 앞의 셋은 이미 자식을 읽고 있고, 마지막은 움직임이 pseudo-element에 있기 때문입니다. PlAnimateAppear는 같은 이름의 세 prop에 기본값이 70인 stagger가 더해집니다.
PlAnimateStagger는 의도적으로 없습니다. 간격은 효과가 아니라 차등이고, 래퍼는 효과들이 이미 말할 수 있는 것을 두 번째 방식으로 철자하는 일이 됩니다. Pulse(blink + alternate)와 Bounce(grow + alternate)를 라이브러리에 넣지 않는 것과 같은 규칙입니다.
차등을 준 집합은 PlAnimateAppear입니다. React 빌드는 호출자의 CSS가 여전히 자식들을 배치하고 있으므로 임의의 자식에 효과를 써 넣을 수 있습니다. 여기에는 그 일을 할 스타일시트가 없어서, 차등을 준 효과는 행이나 열까지 자기가 가져야 합니다. 그것이 바로 PlAnimateAppear이고, 그것을 여섯 개 더 만드는 일이 됩니다.
Accessibility
prefers-reduced-motion에서는 애니메이션이 통째로 없어지고 내용은 그냥 거기 있습니다. 로딩 인디케이터와 정반대이고, 그 차이는 각자가 무슨 말을 하고 있는지에서 옵니다. 멈춘 spinner는 무언가 진행 중인지에 대해 거짓말을 하지만, 재생되지 않은 등장은 담고 있던 것을 이미 다 전달했습니다.- wrapper는 role도 label도 붙이지 않습니다. 이미 자기가 무엇인지 알리는 내용을 감싼
<div>일 뿐입니다. - 여기 있는 어떤 것도 내용을 숨기는 방법이 아닙니다.
mode="out"인 요소도 문서에 그대로 있고 그대로 읽힙니다. 없어져야 한다면 unmount하세요. trigger="hover"는 focus에서도 시작하므로, 키보드로 닿을 수 있는 것 위의 효과는 마우스를 쥐고 있지 않은 사람에게도 돕니다.timeline="view"도prefers-reduced-motion에서 같은 이유로 같은 결과로 걷히고,animation-timeline이 없는 브라우저에서는 시계 기반 1회 재생으로 폴백합니다. 어느 쪽도 빈 화면을 남기지 않습니다.
- 플랫폼에서 애니메이션이 꺼져 있으면(
MediaQuery.disableAnimations) 효과가 통째로 없어지고 내용은 그냥 거기 있습니다. 로딩 인디케이터와 정반대이고, 그 차이는 각자가 무슨 말을 하고 있는지에서 옵니다. 멈춘 spinner는 무언가 진행 중인지에 대해 거짓말을 하지만, 재생되지 않은 등장은 담고 있던 것을 이미 다 전달했습니다. - widget은 자기 semantics를 붙이지 않습니다. 이미 자기가 무엇인지 알리는 내용을 감싼
Opacity일 뿐입니다. - 여기 있는 어떤 것도 내용을 숨기는 방법이 아닙니다.
PlassAnimateMode.exit인 widget도 트리에 그대로 있고 semantics에도 그대로 있습니다. 없어져야 한다면 빼세요. PlassAnimateTrigger.hover는 focus에서도 시작하므로, 키보드로 닿을 수 있는 것 위의 효과는 마우스를 쥐고 있지 않은 사람에게도 돕니다.
React 빌드와 다른 점
| React | Flutter | 이유 |
|---|---|---|
duration, delay가 밀리초 | Duration | 프레임워크에 이미 타입이 있습니다. int 밀리초를 받는 패키지는 그것을 쓰는 모든 파일에서 혼자 다른 말을 하게 됩니다. |
easing이 CSS 문자열 | curve, Curve | 같은 것에 대한 Dart 자신의 이름입니다. |
repeat: number | 'infinite' | int?, null이 멈추지 않음 | 적을 'infinite'가 없고, -1은 caller가 찾아봐야 하는 sentinel입니다. PlProgressLinear가 null value로 하는 것과 같은 거래입니다. |
mode="in" | "out" | PlassAnimateMode.enter / .exit | in은 Dart의 예약어라 enum 값이 될 수 없습니다. |
trigger="visible"이 IntersectionObserver | 가장 가까운 Scrollable을 봅니다 | 여기에는 observer가 없습니다. 위에 scrollable이 없으면 볼 것이 없으므로 그냥 돕니다. 브라우저에 observer가 없을 때 React 빌드가 하는 것과 같습니다. |
prefers-reduced-motion | MediaQuery.disableAnimations | 플랫폼 자신의 신호입니다. |
render | — | Flutter에는 다형적 요소가 없습니다. |
stagger, durationStep, reverse | — | React 빌드는 효과를 자식들 자신에게 써 넣으므로 호출자의 레이아웃은 그대로입니다. Flutter에는 집합을 배치할 스타일시트가 없어서, 차등을 준 효과는 행이나 열까지 자기가 가져야 합니다. 그것이 바로 PlAnimateAppear이고, 그것을 여섯 개 더 만드는 일이 됩니다. |
timeline="view" | — | animation-timeline은 여기에 대응물이 없는 CSS 속성입니다. Flutter에서 스크롤 연동 효과는 ScrollPosition으로 구동하는 AnimationController이고, 위젯이 prop으로 받는 것이 아니라 애플리케이션 자신의 배선입니다. |
className, style | — | 통과시킬 class 목록도 style 속성도 없습니다. |