اللي في Filee · فصل ٣

اللي في Filee — زراير، chips، ألوان، sidebar، toast، صفحات

TransformOperationsTransition · BrushTransition · BoxShadowsTransition · Style.Animations · KeyFrame
🎯 الهدف: تعيد كل حركة عادية في Filee على الويب، وتعرف المقابل بتاعها في الـ XAML.

كل ديمو تحت معمول بـ CSS بس، نفس الأرقام اللي في كود Filee. الـ easing في الديموهات هو cubic-bezier(0.34, 1.56, 0.64, 1)، أقرب حاجة في CSS للـ BackEaseOut (شوف الفصل اللي فات).

الزراير: تتكبس ٥٪ وترجع

لما تدوس، الزرار بيصغر لـ scale(0.95). لما تسيب، بيرجع لحجمه بـ BackEaseOut، فيعدّي حجمه شعرة ويرجع. ده إحساس “المهلبية”.

زرار عادي قصاد زرار Fileeدوس على كل زرار واستمر دايس، وبعدين سيبه
Filee (XAML)
<Style Selector="Button, ToggleButton, RepeatButton">  <Setter Property="Transitions">    <Transitions>      <TransformOperationsTransition        Property="RenderTransform"        Duration="0:0:0.22"        Easing="{StaticResource SoftEase}" />    </Transitions>  </Setter></Style><Style Selector="Button:pressed">  <Setter Property="RenderTransform"          Value="scale(0.95)" /></Style>
الويب (CSS)
.btn {  transition: transform 0.22s var(--ease-back);}.btn:active {  transform: scale(0.95);}

الـ chips: تكبر ٥٪ وتتلوّن

في Filee، الـ chips هي الصيغ اللي بتسحبها وترتبها في محرر الدونت (عشان كده الـ cursor بتاعها SizeAll). لما تعدّي عليها، بتكبر لـ scale(1.05) والخلفية بتاخد لون فاتح من الـ accent.

chips الصيغعدّي بالماوس على الـ chips، ودوس عشان تختار
Styles/Soft.axaml (Filee)
<Style Selector="Border.chip">  <Setter Property="Transitions">    <Transitions>      <TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.25" Easing="{StaticResource SoftEase}" />      <BrushTransition Property="Background" Duration="0:0:0.15" />    </Transitions>  </Setter></Style><Style Selector="Border.chip:pointerover">  <Setter Property="Background" Value="{DynamicResource AccentFaintBrush}" />  <Setter Property="RenderTransform" Value="scale(1.05)" /></Style>

لاحظ حاجة: الحجم بياخد 0.25 ثانية بنطة، واللون بياخد 0.15 من غير نطة. اللون مينفعش “ينط” (هيعدّي اللون التاني ويرجع، وده شكله وحش)، فبياخد مدة أقصر ومنحنى عادي.

الألوان: BrushTransition

أي لون بيتغير في Filee (خلفية، ثيم، accent) بيتغير في ١٥٠ms. مش بيقفز. ده اللي بيخلي تغيير الثيم أو اختيار عنصر يحس ناعم.

غيّر الـ accentغيّر اللون وشوف الكارت بيتلوّن إزاي
FileeBrushTransition · 0.15s
.card {  transition: background-color 0.15s, border-color 0.15s, color 0.15s;}

الـ Drop zone: بتكبر وتتلوّن لما الملف ييجي فوقها

في الصفحة الرئيسية في Filee مربع تسيب فيه الملفات. لما ملف يبقى فوقه، بياخد كلاس over: الخلفية بتتلوّن، والإطار بياخد الـ accent، وبيكبر لـ scale(1.015) بس. ١.٥٪. صغيرة جدًا بس بتحسها.

مربع الـ drop (جرّب تسحب ملف حقيقي)عدّي بالماوس، أو اسحب أي ملف من جهازك فوق المربع
⬇سيب الملف هناscale(1.015) + لون
Views/Pages/HomePage.axaml (Filee)
<Style Selector="Border#DropZone">  <Setter Property="Transitions">    <Transitions>      <BrushTransition Property="Background" Duration="0:0:0.2" />      <TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.3" Easing="BackEaseOut" />    </Transitions>  </Setter></Style><Style Selector="Border#DropZone.over">  <Setter Property="Background" Value="{DynamicResource AccentSoftBrush}" />  <Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}" />  <Setter Property="RenderTransform" Value="scale(1.015)" /></Style>

على الويب، الـ dragenter بيتنده على كل عنصر جوه المربع، فلو شلت الكلاس في أول dragleave هيرمش. الحل إنك تعد: زوّد مع كل dragenter وقلّل مع كل dragleave، وشيل الكلاس لما العدد يوصل صفر.

كروت المحرّكات: ظل بيترفع

في صفحة المحرّكات بتسحب الكارت عشان تغيّر الأولوية. الكارت اللي بتسحبه بياخد ظل أكبر وإطار ملوّن، والظل بيتحرك بـ BoxShadowsTransition في ١٥٠ms. فيحس إنك رفعته من على الترابيزة.

ارفع كارتدوس على كارت عشان ترفعه، ودوس تاني عشان تنزّله
.engine {  transition: box-shadow 0.15s, border-color 0.15s;}.engine.dragging {  box-shadow: 0 8px 24px 0 rgb(0 0 0 / 0.2);}

عناصر الـ sidebar: تتزق ٣ بكسل

لما تعدّي على عنصر في القائمة الجانبية، بيتحرك translateX(3px) ناحية المحتوى. ٣ بكسل بس، بس بتحس إن القائمة بتسمعك.

القائمة الجانبيةعدّي على العناصر واختار واحد
Styles/Soft.axaml (Filee)
<Style Selector="ListBox.nav ListBoxItem">  <Setter Property="RenderTransform" Value="none" />  <Setter Property="Transitions">    <Transitions>      <TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.25" Easing="{StaticResource SoftEase}" />    </Transitions>  </Setter></Style><Style Selector="ListBox.nav ListBoxItem:pointerover">  <Setter Property="RenderTransform" Value="translateX(3px)" /></Style>

الـ toast: keyframes بيدخل من الجنب

ده أول مكان نشوف فيه keyframes بدل transition. الـ toast مش “بيتغير” من حالة لحالة. هو بيظهر. فمحتاج حركة تشتغل لوحدها أول ما يتحط.

toast التحويلدوس كذا مرة ورا بعض
Filee (XAML)
<Style Selector="ItemsControl#Jobs ContentPresenter">  <Style.Animations>    <Animation Duration="0:0:0.35"               Easing="BackEaseOut"               FillMode="Forward">      <KeyFrame Cue="0%">        <Setter Property="Opacity" Value="0" />        <Setter Property="Margin" Value="40,0,-40,0" />      </KeyFrame>      <KeyFrame Cue="100%">        <Setter Property="Opacity" Value="1" />        <Setter Property="Margin" Value="0" />      </KeyFrame>    </Animation>  </Style.Animations></Style>
الويب (CSS)
.toast {  animation: toast-in 0.35s var(--ease-back) both;}@keyframes toast-in {  from {    opacity: 0;    transform: translateX(40px);  }}

لاحظ إن Filee بيحرّك Margin مش RenderTransform. ده مش ذوق: كاتب في الكود إن الـ keyframes في نسخته من Avalonia مش بتعرف تحرّك RenderTransform، فاستخدم الـ margin بدلها. على الويب استخدم transform لأنه أخف.

فتح صفحة: تطلع ١٠ بكسل وتظهر

لما تتنقل بين صفحات الإعدادات، المحتوى الجديد بيظهر من الشفافية وهو طالع ١٠ بكسل لفوق، في ٢٨٠ms بـ CubicEaseOut. من غير نطة، لأن صفحة كاملة بتنط شكلها وحش.

التنقل بين الصفحاتاتنقّل بين الصفحات

الرئيسية

Styles/Soft.axaml (Filee)
<!-- keyframes can't animate RenderTransform, so Margin slides --><Style Selector="ContentControl.page > :is(Control)">  <Style.Animations>    <Animation Duration="0:0:0.28" Easing="CubicEaseOut" FillMode="Forward">      <KeyFrame Cue="0%">        <Setter Property="Opacity" Value="0" />        <Setter Property="Margin" Value="0,10,0,-10" />      </KeyFrame>      <KeyFrame Cue="100%">        <Setter Property="Opacity" Value="1" />        <Setter Property="Margin" Value="0" />      </KeyFrame>    </Animation>  </Style.Animations></Style>

على الويب، عشان الـ animation يشتغل تاني مع كل صفحة، شيل الكلاس، اقرا offsetWidth (ده بيجبر المتصفح يحسب)، ورجّع الكلاس:

box.classList.remove("enter");void box.offsetWidth; // reflow: من غيرها المتصفح مش هيحس إن الكلاس اتشالbox.classList.add("enter");

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

🧠 اختبر نفسك
١) ليه اللون مبياخدش BackEaseOut زي الحجم؟
لأن اللون لو عدّى هدفه هيروح للون تاني ويرجع، وده بيبان غلط. فبياخد 0.15s من غير نطة.
٢) الـ toast ليه keyframes مش transition؟
لأنه بيظهر، مش بيتغير من حالة لحالة. الـ keyframes بيشتغل لوحده أول ما العنصر يتحط.
٣) فتح الصفحة ليه CubicEaseOut مش BackEaseOut؟
صفحة كاملة لو نطت شكلها بيتهز. الحاجات الكبيرة تتحرك من غير overshoot.
٤) ليه Filee بيحرّك Margin في الـ keyframes؟
لأن الـ keyframes في Avalonia عنده مش بتحرّك RenderTransform، وده مكتوب كتعليق في الكود.