من خيالي · فصل ٤

من خيالي — dropdown و select و tabs و modal و switch و tooltip و accordion

dropdown · select · tabs · modal · switch · tooltip · accordion · TransitioningContentControl · Expander
🎯 الهدف: تطبّق نفس روح Filee على عناصر مش موجودة فيه، بنفس القواعد.

القواعد اللي هنمشي عليها

حاجة صغيرة بتظهر أو تتداسBackEaseOut: نطة صغيرة
حاجة كبيرة (صفحة، لوحة)CubicEaseOut: من غير نطة
القفلأسرع من الفتح، EaseInCubic، من غير نطة
قايمة عناصر بتظهرstagger: كل عنصر بعد اللي قبله بحوالي 28ms
لون0.15s من غير نطة
الحركةtransform و opacity بس

القائمة بتطلع من الزرار نفسه: transform-origin: top، من scale(0.95) وشفافة لحجمها الطبيعي. والعناصر بتنزل ورا بعض بـ stagger، زي شرايح الدونت.

القفل عكس كده: ١٢٠ms بس، من غير نطة، ومن غير stagger.

قائمة الخيارات (شبه كليك يمين على ملف في السجل)افتح القائمة واقفلها (Esc أو برا القائمة)
dropdown.css
.menu {  transform-origin: top;  /* مقفولة: سريعة ومن غير نطة */  opacity: 0;  transform: translateY(-6px) scale(0.95);  visibility: hidden;  transition:    opacity 0.12s var(--ease-in),    transform 0.12s var(--ease-in),    visibility 0s 0.12s; /* تستخبى بعد ما الحركة تخلص */}.dd.open .menu {  opacity: 1;  transform: none;  visibility: visible;  transition:    opacity 0.18s var(--ease-out),    transform 0.26s var(--ease-back),    visibility 0s;}/* stagger: كل عنصر متأخر 28ms عن اللي قبله (OpenStagger في Filee) */.menu li {  opacity: 0;  transform: translateY(-4px);}.dd.open .menu li {  opacity: 1;  transform: none;  transition: opacity 0.2s, transform 0.3s var(--ease-back);  transition-delay: calc(var(--i) * 28ms);}

في Avalonia الفكرة إنك تحط animation على الـ Border اللي جوه الـ popup، يشتغل لما الـ ComboBox يبقى :dropdownopen:

<Style Selector="ComboBox:dropdownopen /template/ Border#PopupBorder">  <Style.Animations>    <Animation Duration="0:0:0.22" Easing="CubicEaseOut" FillMode="Forward">      <KeyFrame Cue="0%"><Setter Property="Opacity" Value="0" /></KeyFrame>      <KeyFrame Cue="100%"><Setter Property="Opacity" Value="1" /></KeyFrame>    </Animation>  </Style.Animations></Style>

Select: السهم يلف، والقيمة تتبدّل بحركة

نفس الـ dropdown، وفوقه تلات حاجات صغيرة:

  1. السهم بيلف 180deg بنطة لما تفتح (زي سهم الرئيسية في Filee اللي بيلف 90).
  2. علامة ✓ بتظهر من scale(0.4) بنطة على الاختيار الجديد.
  3. القيمة في الزرار بتطلع من تحت لما تتغير، فتعرف إن حاجة اتغيرت.
اختار الصيغةاختار صيغة
  • PNG✓
  • JPG✓
  • WEBP✓
  • PDF✓
  • AVIF✓
  • GIF✓
.chev { transition: transform 0.3s var(--ease-back); }.open .chev { transform: rotate(180deg); }.check { opacity: 0; transform: scale(0.4); transition: opacity 0.15s, transform 0.3s var(--ease-back); }[aria-selected="true"] > .check { opacity: 1; transform: none; }.value.swap { animation: swap 0.3s var(--ease-back); }@keyframes swap { from { opacity: 0; transform: translateY(8px); } }

Tabs: مؤشر بيتزحلق، ومحتوى جاي من الناحية الصح

بدل ما الخط اللي تحت التاب يختفي ويظهر في مكان تاني، فيه مؤشر واحد بيتزحلق لمكان التاب الجديد بـ BackEaseOut. والمحتوى الجديد بيدخل من ناحية التاب اللي رحتله: لو رحت للي بعده، ييجي من ناحيته.

أنواع الملفاتاتنقّل بين التابات، يمين وشمال

PNG · JPG · WEBP · AVIF

tabs.ts
// المؤشر بيتحرك لمكان وعرض التاب الجديد. الحركة نفسها في CSSfunction place(tab: HTMLElement) {  ink.style.setProperty("--x", `${tab.offsetLeft}px`);  ink.style.setProperty("--w", `${tab.offsetWidth}px`);}// الصفحة RTL: التاب اللي بعده على الشمال، فالمحتوى ييجي من الشمالconst from = next > current ? "from-left" : "from-right";
.ink {  position: absolute;  left: var(--x);  width: var(--w);  transition: left 0.35s var(--ease-back), width 0.35s var(--ease-back);}

في Avalonia ممكن تعمل نفس المؤشر بـ Border واحد فوق التابات، و DoubleTransition على Canvas.Left و Width، وتحسب مكان التاب في الـ code-behind.

حاجتين بيتحركوا مع بعض، بمدد مختلفة:

  • الخلفية بتغمق بـ opacity في ٢٠٠ms، من غير نطة.
  • الـ dialog بيكبر من scale(0.9) في ٣٢٠ms بنطة.
  • القفل للاتنين في ١٦٠ms بـ EaseInCubic، زي قفل الدونت بالظبط.
dialog الإعداداتافتح الـ dialog واقفله
modal.ts
function open() {  backdrop.hidden = false;  // فريمين: الأول يرسم الحالة المقفولة، التاني يبدأ الحركة منها  requestAnimationFrame(() => requestAnimationFrame(() => backdrop.classList.add("open")));}function close() {  backdrop.classList.remove("open");  // استنى الحركة تخلص قبل ما تخفي (زي FinishClose في Filee)  setTimeout(() => (backdrop.hidden = true), 170);}

Switch: الزرار يتمط وانت دايس

الزرار الأبيض بيتزحلق بنطة، والخلفية بتتلوّن في ١٥٠ms. والتفصيلة الحلوة: وانت دايس، الزرار بيعرض شوية (width: 24px بدل 20px)، كأنه بيستعد يتنقل.

إعداداتشغّل واقفل
.switch { transition: background-color 0.15s; }.switch i { transition: transform 0.3s var(--ease-back), width 0.15s; }.switch:active i { width: 24px; }.switch[aria-checked="true"] i { transform: translateX(20px); }.switch[aria-checked="true"]:active i { transform: translateX(16px); } /* يتمط ناحية الشمال */

Tooltip: يستنى قبل ما يظهر، ويمشي علطول

الـ tooltip اللي بيظهر مع كل حركة ماوس مزعج. فـ:

  • الظهور بعد 300ms استنى، وبعدين يطلع بنطة صغيرة.
  • الاختفاء فوري تقريبًا (١٠٠ms)، من غير استنى.
أزرار بأيقوناتاستنى على أيكونة شوية
حوّل تانيافتح الفولدرامسح
.tip {  opacity: 0;  transform: translate(-50%, 4px);  transition: opacity 0.1s, transform 0.1s; /* الاختفاء */}.wrap:hover .tip {  opacity: 1;  transform: translate(-50%, 0);  transition: opacity 0.18s 0.3s, transform 0.25s var(--ease-back) 0.3s; /* الظهور بعد 300ms */}

الحيلة: الـ transition اللي في الحالة الجديدة هو اللي بيتطبق. فتحط delay في الـ :hover بس، والاختفاء ياخد الـ transition العادي من غير delay. في Avalonia في ToolTip.ShowDelay جاهزة للتأخير.

Accordion: grid من صفر لواحد + سهم بيلف ٩٠

سهم الرئيسية في Filee بيلف من 0 لـ 90deg لما تفتح. ضيف عليه فتح القسم بـ grid-template-rows من 0fr لـ 1fr (اتشرحت بالتفصيل في مذكرة الـ Smooth Collapse).

أنواع المحرّكاتافتح واقفل الأقسام

PNG ← JPG ← WEBP. كلها جوه Magick.NET.

DOCX و PDF و HWPX. الأصعب بيروح LibreOffice.

كله على FFmpeg، وبيتنزّل أول ما تحتاجه.

.body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.3s var(--ease-out); }.open .body { grid-template-rows: 1fr; }.body > div { overflow: hidden; } /* القص على الابن */.chev { transition: transform 0.3s var(--ease-back); }.open .chev { transform: rotate(90deg); }

في Avalonia الـ Expander عنده خاصية ContentTransition تحط فيها CrossFade مثلًا. ولصفحات كاملة فيه TransitioningContentControl مع PageSlide أو CrossFade.

خلّصت الفصل لما

🧠 اختبر نفسك
١) ليه القائمة بتطلع بـ transform-origin: top؟
عشان تكبر من ناحية الزرار اللي فتحها. لو من النص، هتحس إنها جاية من الهوا.
٢) إزاي تخلي الظهور متأخر والاختفاء لأ؟
تحط الـ delay في الـ transition بتاع الحالة الظاهرة (:hover) بس. الـ transition بتاع الحالة اللي رايح لها هو اللي بيتطبق.
٣) الـ modal: ليه requestAnimationFrame مرتين؟
عشان المتصفح يرسم الحالة المقفولة الأول بعد ما hidden يتشال، وبعدين الكلاس open يبدأ الحركة منها.
٤) القائمة بتقفل في كام؟ وليه من غير stagger؟
في حوالي 120ms. اللي بيقفل عايزها تمشي. لو العناصر مشيت واحد ورا واحد، القفل هيطوّل من غير لازمة.