كل ديمو تحت معمول بـ CSS بس، نفس الأرقام اللي في كود Filee. الـ easing في الديموهات هو cubic-bezier(0.34, 1.56, 0.64, 1)، أقرب حاجة في CSS للـ BackEaseOut (شوف الفصل اللي فات).
الزراير: تتكبس ٥٪ وترجع
لما تدوس، الزرار بيصغر لـ scale(0.95). لما تسيب، بيرجع لحجمه بـ BackEaseOut، فيعدّي حجمه شعرة ويرجع. ده إحساس “المهلبية”.
<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>.btn { transition: transform 0.22s var(--ease-back);}.btn:active { transform: scale(0.95);}الـ chips: تكبر ٥٪ وتتلوّن
في Filee، الـ chips هي الصيغ اللي بتسحبها وترتبها في محرر الدونت (عشان كده الـ cursor بتاعها SizeAll). لما تعدّي عليها، بتكبر لـ scale(1.05) والخلفية بتاخد لون فاتح من الـ accent.
<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. مش بيقفز. ده اللي بيخلي تغيير الثيم أو اختيار عنصر يحس ناعم.
.card { transition: background-color 0.15s, border-color 0.15s, color 0.15s;}
الـ Drop zone: بتكبر وتتلوّن لما الملف ييجي فوقها
في الصفحة الرئيسية في Filee مربع تسيب فيه الملفات. لما ملف يبقى فوقه، بياخد كلاس over: الخلفية بتتلوّن، والإطار بياخد الـ accent، وبيكبر لـ scale(1.015) بس. ١.٥٪. صغيرة جدًا بس بتحسها.
<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) ناحية المحتوى. ٣ بكسل بس، بس بتحس إن القائمة بتسمعك.
<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 مش “بيتغير” من حالة لحالة. هو بيظهر. فمحتاج حركة تشتغل لوحدها أول ما يتحط.
<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>.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. من غير نطة، لأن صفحة كاملة بتنط شكلها وحش.
الرئيسية
<!-- 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");