feat: add ConfettiCannon.
This commit is contained in:
1 parent
94aa32cec1
commit
79b32d573c
13 files changed
+885
-9
No files matched your search
+8
-4
@@ -205,13 +205,17 @@ Step 4: 开始编码
|
|||||||
|
|
||||||
## 最新示例
|
## 最新示例
|
||||||
|
|
||||||
|
### ConfettiCannon
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### Watermark
|
### Watermark
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### AutoCompleteTextBox
|
### AutoCompleteTextBox
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### ElementGroup
|
### ElementGroup
|
||||||
|
|
||||||
@@ -221,12 +225,12 @@ Step 4: 开始编码
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## 历史发布
|
||||||
|
|
||||||
### FlexPanel
|
### FlexPanel
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 历史发布
|
|
||||||
|
|
||||||
### CheckComboBox
|
### CheckComboBox
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -193,6 +193,10 @@ Step 4: Enjoy coding
|
|||||||
|
|
||||||
## Latest examples
|
## Latest examples
|
||||||
|
|
||||||
|
### ConfettiCannon
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### Watermark
|
### Watermark
|
||||||
|
|
||||||

|

|
||||||
@@ -209,12 +213,12 @@ Step 4: Enjoy coding
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## History publication
|
||||||
|
|
||||||
### FlexPanel
|
### FlexPanel
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## History publication
|
|
||||||
|
|
||||||
### CheckComboBox
|
### CheckComboBox
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -101,6 +101,9 @@
|
|||||||
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\CompareSliderDemo.xaml">
|
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\CompareSliderDemo.xaml">
|
||||||
<Link>UserControl\Controls\CompareSliderDemo.xaml</Link>
|
<Link>UserControl\Controls\CompareSliderDemo.xaml</Link>
|
||||||
</Resource>
|
</Resource>
|
||||||
|
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\ConfettiCannonDemo.xaml">
|
||||||
|
<Link>UserControl\Controls\ConfettiCannonDemo.xaml</Link>
|
||||||
|
</Resource>
|
||||||
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\CoverFlowDemo.xaml">
|
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\CoverFlowDemo.xaml">
|
||||||
<Link>UserControl\Controls\CoverFlowDemo.xaml</Link>
|
<Link>UserControl\Controls\CoverFlowDemo.xaml</Link>
|
||||||
</Resource>
|
</Resource>
|
||||||
@@ -495,6 +498,10 @@
|
|||||||
<Link>UserControl\Controls\CompareSliderDemo.xaml.cs</Link>
|
<Link>UserControl\Controls\CompareSliderDemo.xaml.cs</Link>
|
||||||
<DependentUpon>CompareSliderDemo.xaml</DependentUpon>
|
<DependentUpon>CompareSliderDemo.xaml</DependentUpon>
|
||||||
</Resource>
|
</Resource>
|
||||||
|
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\ConfettiCannonDemo.xaml.cs">
|
||||||
|
<Link>UserControl\Controls\ConfettiCannonDemo.xaml.cs</Link>
|
||||||
|
<DependentUpon>ConfettiCannonDemo.xaml</DependentUpon>
|
||||||
|
</Resource>
|
||||||
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\CoverFlowDemo.xaml.cs">
|
<Resource Include="..\..\Shared\HandyControlDemo_Shared\UserControl\Controls\CoverFlowDemo.xaml.cs">
|
||||||
<Link>UserControl\Controls\CoverFlowDemo.xaml.cs</Link>
|
<Link>UserControl\Controls\CoverFlowDemo.xaml.cs</Link>
|
||||||
<DependentUpon>CoverFlowDemo.xaml</DependentUpon>
|
<DependentUpon>CoverFlowDemo.xaml</DependentUpon>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"group": true,
|
"group": true,
|
||||||
"demoItemList": [
|
"demoItemList": [
|
||||||
[ "FloatingBlock", "FloatingBlockDemo", "Brush.LikeFilled", "", "Interacting" ],
|
[ "FloatingBlock", "FloatingBlockDemo", "Brush.LikeFilled", "", "Interacting" ],
|
||||||
|
[ "ConfettiCannon", "ConfettiCannonDemo", "Brush.Favorite", "1", "Interacting" ],
|
||||||
[ "PropertyGrid", "PropertyGridDemo", "Brush.PropertyGridEditorPart", "", "DataEntry" ],
|
[ "PropertyGrid", "PropertyGridDemo", "Brush.PropertyGridEditorPart", "", "DataEntry" ],
|
||||||
[ "ImageSelector", "ImageSelectorDemo", "Brush.Image", "", "DataEntry" ],
|
[ "ImageSelector", "ImageSelectorDemo", "Brush.Image", "", "DataEntry" ],
|
||||||
[ "ImageBlock", "ImageBlockDemo", "Brush.ImageStack", "", "DataDisplay" ],
|
[ "ImageBlock", "ImageBlockDemo", "Brush.ImageStack", "", "DataDisplay" ],
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
[ "Divider", "DividerDemo", "Brush.DottedSplitter", "", "Layout" ],
|
[ "Divider", "DividerDemo", "Brush.DottedSplitter", "", "Layout" ],
|
||||||
[ "CirclePanel", "CirclePanelDemo", "Brush.ColorWheel", "", "Layout" ],
|
[ "CirclePanel", "CirclePanelDemo", "Brush.ColorWheel", "", "Layout" ],
|
||||||
[ "Grid", "GridDemo", "Brush.GridPane", "", "Layout" ],
|
[ "Grid", "GridDemo", "Brush.GridPane", "", "Layout" ],
|
||||||
[ "FlexPanel", "FlexPanelDemo", "Brush.FlowLayoutPanel", "1", "Layout" ],
|
[ "FlexPanel", "FlexPanelDemo", "Brush.FlowLayoutPanel", "", "Layout" ],
|
||||||
[ "UniformSpacingPanel", "UniformSpacingPanelDemo", "Brush.IncreaseHorizontalSpacing", "1", "Layout" ],
|
[ "UniformSpacingPanel", "UniformSpacingPanelDemo", "Brush.IncreaseHorizontalSpacing", "1", "Layout" ],
|
||||||
[ "RelativePanel", "RelativePanelDemo", "Brush.TransformRelative", "", "Layout" ],
|
[ "RelativePanel", "RelativePanelDemo", "Brush.TransformRelative", "", "Layout" ],
|
||||||
[ "WaterfallPanel", "WaterfallPanelDemo", "Brush.Panel", "", "Layout" ],
|
[ "WaterfallPanel", "WaterfallPanelDemo", "Brush.Panel", "", "Layout" ],
|
||||||
@@ -121,8 +122,8 @@
|
|||||||
"group": false,
|
"group": false,
|
||||||
"demoItemList": [
|
"demoItemList": [
|
||||||
[ "HatchBrushGenerator", "HatchBrushGeneratorDemo", "Brush.DataGenerator", "", "" ],
|
[ "HatchBrushGenerator", "HatchBrushGeneratorDemo", "Brush.DataGenerator", "", "" ],
|
||||||
[ "Morphing_Animation", "GeometryAnimationDemo", "Brush.Animation", "", "" ],
|
[ "MorphingAnimation", "GeometryAnimationDemo", "Brush.Animation", "", "" ],
|
||||||
[ "Effects", "EffectsDemo", "Brush.Effects", "", "" ]
|
[ "Effects", "EffectsDemo", "Brush.Effects", "", "" ],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -64,6 +64,9 @@
|
|||||||
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\AutoCompleteTextBoxDemo.xaml.cs">
|
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\AutoCompleteTextBoxDemo.xaml.cs">
|
||||||
<DependentUpon>AutoCompleteTextBoxDemo.xaml</DependentUpon>
|
<DependentUpon>AutoCompleteTextBoxDemo.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\ConfettiCannonDemo.xaml.cs">
|
||||||
|
<DependentUpon>ConfettiCannonDemo.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\ElementGroupDemo.xaml.cs">
|
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\ElementGroupDemo.xaml.cs">
|
||||||
<DependentUpon>ElementGroupDemo.xaml</DependentUpon>
|
<DependentUpon>ElementGroupDemo.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -551,6 +554,7 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="$(MSBuildThisFileDirectory)UserControl\Controls\ConfettiCannonDemo.xaml" />
|
||||||
<Page Include="$(MSBuildThisFileDirectory)UserControl\Controls\ElementGroupDemo.xaml">
|
<Page Include="$(MSBuildThisFileDirectory)UserControl\Controls\ElementGroupDemo.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|||||||
@@ -1387,5 +1387,17 @@
|
|||||||
</DrawingGroup>
|
</DrawingGroup>
|
||||||
</DrawingBrush.Drawing>
|
</DrawingBrush.Drawing>
|
||||||
</DrawingBrush>
|
</DrawingBrush>
|
||||||
|
|
||||||
|
<DrawingBrush o:Freeze="True" x:Key="Brush.Favorite">
|
||||||
|
<DrawingBrush.Drawing>
|
||||||
|
<DrawingGroup>
|
||||||
|
<DrawingGroup.Children>
|
||||||
|
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
|
||||||
|
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M7.2729,-0.000199999999999534L5.6649,4.9998 -9.99999999997669E-05,4.9998 -9.99999999997669E-05,6.5148 4.2109,9.5218 2.1279,15.9998 3.8809,15.9998 7.9999,12.3378 12.1199,15.9998 13.8719,15.9998 11.7899,9.5218 15.9999,6.5148 15.9999,4.9998 10.3369,4.9998 8.7289,-0.000199999999999534z" />
|
||||||
|
<GeometryDrawing Brush="#FF424242" Geometry="F1M10.6143,9.1328L12.5003,14.9998 8.0003,10.9998 3.5003,14.9998 5.3863,9.1328 1.0003,5.9998 6.3933,5.9998 8.0013,0.9998 9.6073,5.9998 15.0003,5.9998z" />
|
||||||
|
</DrawingGroup.Children>
|
||||||
|
</DrawingGroup>
|
||||||
|
</DrawingBrush.Drawing>
|
||||||
|
</DrawingBrush>
|
||||||
<!--endregion-->
|
<!--endregion-->
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<UserControl x:Class="HandyControlDemo.UserControl.ConfettiCannonDemo"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
hc:ConfettiCannon.Token="ConfettiCannonDemo"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol">
|
||||||
|
<hc:TransitioningContentControl>
|
||||||
|
<hc:ScrollViewer IsInertiaEnabled="True"
|
||||||
|
Margin="32">
|
||||||
|
<hc:UniformSpacingPanel Orientation="Vertical"
|
||||||
|
Spacing="16">
|
||||||
|
<Button Content="Basic Cannon"
|
||||||
|
Click="ButtonBasicCannonOnClick" />
|
||||||
|
<Button Content="Random Direction"
|
||||||
|
Click="ButtonRandomDirectionOnClick" />
|
||||||
|
<Button Content="Realistic Look"
|
||||||
|
Click="ButtonRealisticLookClick" />
|
||||||
|
<Button Content="Fireworks"
|
||||||
|
Click="ButtonFireworksOnClick" />
|
||||||
|
<Button Content="Stars"
|
||||||
|
Click="ButtonStarsOnClick" />
|
||||||
|
<Button Content="Snow"
|
||||||
|
Click="ButtonSnowOnClick" />
|
||||||
|
<Button Content="School Pride"
|
||||||
|
Click="ButtonSchoolPrideOnClick" />
|
||||||
|
<Button Content="Custom Canvas"
|
||||||
|
Click="ButtonCustomCanvasOnClick" />
|
||||||
|
<hc:ConfettiCannonContainer>
|
||||||
|
<Border MinWidth="500"
|
||||||
|
Height="300"
|
||||||
|
Style="{StaticResource BorderRegion}" />
|
||||||
|
</hc:ConfettiCannonContainer>
|
||||||
|
</hc:UniformSpacingPanel>
|
||||||
|
</hc:ScrollViewer>
|
||||||
|
</hc:TransitioningContentControl>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
// adapted from https://www.kirilv.com/canvas-confetti
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using HandyControl.Controls;
|
||||||
|
|
||||||
|
namespace HandyControlDemo.UserControl;
|
||||||
|
|
||||||
|
public partial class ConfettiCannonDemo
|
||||||
|
{
|
||||||
|
public ConfettiCannonDemo()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonBasicCannonOnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
ParticleCount = 100,
|
||||||
|
Spread = 70,
|
||||||
|
Origin = new Point(0.5, 0.6)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonRandomDirectionOnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
Angle = random.Next(55, 125),
|
||||||
|
Spread = random.Next(50, 70),
|
||||||
|
ParticleCount = random.Next(50, 100),
|
||||||
|
Origin = new Point(0.5, 0.6)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonRealisticLookClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
const int count = 200;
|
||||||
|
|
||||||
|
Fire(0.25, new ConfettiCannon.Options { Spread = 26, StartVelocity = 55 });
|
||||||
|
Fire(0.2, new ConfettiCannon.Options { Spread = 60 });
|
||||||
|
Fire(0.35, new ConfettiCannon.Options { Spread = 100, Decay = 0.91, Scalar = 0.8 });
|
||||||
|
Fire(0.1, new ConfettiCannon.Options { Spread = 120, StartVelocity = 25, Decay = 0.92, Scalar = 1.2 });
|
||||||
|
Fire(0.1, new ConfettiCannon.Options { Spread = 120, StartVelocity = 45 });
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
void Fire(double particleRatio, ConfettiCannon.Options options)
|
||||||
|
{
|
||||||
|
options.ParticleCount = (int) Math.Floor(count * particleRatio);
|
||||||
|
ConfettiCannon.Fire(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonFireworksOnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
const int duration = 15 * 1000;
|
||||||
|
|
||||||
|
var random = new Random();
|
||||||
|
DateTime animationEnd = DateTime.Now + TimeSpan.FromMilliseconds(duration);
|
||||||
|
|
||||||
|
var timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle)
|
||||||
|
{
|
||||||
|
Interval = TimeSpan.FromMilliseconds(250)
|
||||||
|
};
|
||||||
|
|
||||||
|
timer.Tick += OnTimerOnTick;
|
||||||
|
timer.Start();
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
void OnTimerOnTick(object o, EventArgs _)
|
||||||
|
{
|
||||||
|
double timeLeft = (animationEnd - DateTime.Now).TotalMilliseconds;
|
||||||
|
|
||||||
|
if (timeLeft <= 0)
|
||||||
|
{
|
||||||
|
timer.Tick -= OnTimerOnTick;
|
||||||
|
timer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
var particleCount = (int) (50 * (timeLeft / duration));
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
StartVelocity = 30,
|
||||||
|
Spread = 360,
|
||||||
|
Ticks = 60,
|
||||||
|
ParticleCount = particleCount,
|
||||||
|
Origin = new Point
|
||||||
|
{
|
||||||
|
X = RandomInRange(0.1, 0.3),
|
||||||
|
Y = random.NextDouble() - 0.2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
StartVelocity = 30,
|
||||||
|
Spread = 360,
|
||||||
|
Ticks = 60,
|
||||||
|
ParticleCount = particleCount,
|
||||||
|
Origin = new Point
|
||||||
|
{
|
||||||
|
X = RandomInRange(0.7, 0.9),
|
||||||
|
Y = random.NextDouble() - 0.2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonStarsOnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SetTimeout(Shoot, 0);
|
||||||
|
SetTimeout(Shoot, 100);
|
||||||
|
SetTimeout(Shoot, 200);
|
||||||
|
return;
|
||||||
|
|
||||||
|
void Shoot()
|
||||||
|
{
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
Spread = 360,
|
||||||
|
Ticks = 50,
|
||||||
|
Gravity = 0,
|
||||||
|
Decay = 0.94,
|
||||||
|
StartVelocity = 30,
|
||||||
|
Colors = ["#FFE400", "#FFBD00", "#E89400", "#FFCA6C", "#FDFFB8"],
|
||||||
|
ParticleCount = 40,
|
||||||
|
Scalar = 1.2,
|
||||||
|
Shapes = ["star"]
|
||||||
|
});
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
Spread = 360,
|
||||||
|
Ticks = 50,
|
||||||
|
Gravity = 0,
|
||||||
|
Decay = 0.94,
|
||||||
|
StartVelocity = 30,
|
||||||
|
Colors = ["#FFE400", "#FFBD00", "#E89400", "#FFCA6C", "#FDFFB8"],
|
||||||
|
ParticleCount = 10,
|
||||||
|
Scalar = 0.75,
|
||||||
|
Shapes = ["circle"]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeout(Action action, int delayMilliseconds)
|
||||||
|
{
|
||||||
|
var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(delayMilliseconds) };
|
||||||
|
timer.Tick += OnTimerOnTick;
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTimerOnTick(object o, EventArgs _)
|
||||||
|
{
|
||||||
|
Shoot();
|
||||||
|
|
||||||
|
if (o is not DispatcherTimer timer)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Tick -= OnTimerOnTick;
|
||||||
|
timer.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSnowOnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
const int duration = 15 * 1000;
|
||||||
|
|
||||||
|
var random = new Random();
|
||||||
|
DateTime animationEnd = DateTime.Now + TimeSpan.FromMilliseconds(duration);
|
||||||
|
double skew = 1;
|
||||||
|
|
||||||
|
new AnimationFrame().Start(_ =>
|
||||||
|
{
|
||||||
|
double timeLeft = (animationEnd - DateTime.Now).TotalMilliseconds;
|
||||||
|
|
||||||
|
if (timeLeft <= 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ticks = (int) Math.Max(200, 500 * (timeLeft / duration));
|
||||||
|
// ReSharper disable once AccessToModifiedClosure
|
||||||
|
skew = Math.Max(0.8, skew - 0.001);
|
||||||
|
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
ParticleCount = 1,
|
||||||
|
StartVelocity = 0,
|
||||||
|
Ticks = ticks,
|
||||||
|
Origin = new Point
|
||||||
|
{
|
||||||
|
X = random.NextDouble(),
|
||||||
|
Y = random.NextDouble() * skew - 0.2
|
||||||
|
},
|
||||||
|
Colors = ["#ffffff"],
|
||||||
|
Shapes = ["circle"],
|
||||||
|
Gravity = RandomInRange(0.4, 0.6),
|
||||||
|
Scalar = RandomInRange(0.4, 1),
|
||||||
|
Drift = RandomInRange(-0.4, 0.4),
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonSchoolPrideOnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
const int duration = 15 * 1000;
|
||||||
|
|
||||||
|
DateTime animationEnd = DateTime.Now + TimeSpan.FromMilliseconds(duration);
|
||||||
|
|
||||||
|
new AnimationFrame().Start(_ =>
|
||||||
|
{
|
||||||
|
double timeLeft = (animationEnd - DateTime.Now).TotalMilliseconds;
|
||||||
|
if (timeLeft <= 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
ParticleCount = 2,
|
||||||
|
Angle = 60,
|
||||||
|
Spread = 55,
|
||||||
|
Origin = new Point(0, 0.5),
|
||||||
|
Colors = ["#bb0000", "#ffffff"],
|
||||||
|
});
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
ParticleCount = 2,
|
||||||
|
Angle = 120,
|
||||||
|
Spread = 55,
|
||||||
|
Origin = new Point(1, 0.5),
|
||||||
|
Colors = ["#bb0000", "#ffffff"],
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonCustomCanvasOnClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ConfettiCannon.Fire(new ConfettiCannon.Options
|
||||||
|
{
|
||||||
|
Spread = 70,
|
||||||
|
Origin = new Point(0.5, 1.2),
|
||||||
|
Token = "ConfettiCannonDemo"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double RandomInRange(double min, double max)
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
return random.NextDouble() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AnimationFrame
|
||||||
|
{
|
||||||
|
private Func<double, bool> _callback;
|
||||||
|
private long _lastTicks;
|
||||||
|
|
||||||
|
public void Start(Func<double, bool> callback)
|
||||||
|
{
|
||||||
|
_callback = callback;
|
||||||
|
_lastTicks = DateTime.Now.Ticks;
|
||||||
|
CompositionTarget.Rendering += OnRendering;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRendering(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
long nowTicks = DateTime.Now.Ticks;
|
||||||
|
double deltaTime = (nowTicks - _lastTicks) / 10000000.0;
|
||||||
|
_lastTicks = nowTicks;
|
||||||
|
|
||||||
|
if (_callback?.Invoke(deltaTime) == true)
|
||||||
|
{
|
||||||
|
CompositionTarget.Rendering -= OnRendering;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,473 @@
|
|||||||
|
// adapted from https://github.com/catdad/canvas-confetti
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using HandyControl.Tools;
|
||||||
|
|
||||||
|
namespace HandyControl.Controls;
|
||||||
|
|
||||||
|
public class ConfettiCannon : DependencyObject
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached(
|
||||||
|
"Token", typeof(string), typeof(ConfettiCannon), new PropertyMetadata(null, OnTokenChanged));
|
||||||
|
|
||||||
|
public static void SetToken(DependencyObject element, string value)
|
||||||
|
=> element.SetValue(TokenProperty, value);
|
||||||
|
|
||||||
|
public static string GetToken(DependencyObject element)
|
||||||
|
=> (string) element.GetValue(TokenProperty);
|
||||||
|
|
||||||
|
private static readonly ConcurrentDictionary<string, ConfettiCannon> ConfettiCannons = new();
|
||||||
|
private static readonly Dictionary<string, FrameworkElement> ContainerDict = new();
|
||||||
|
private static readonly Lazy<ConfettiCannon> DefaultConfettiCannon = new(() => new ConfettiCannon(), true);
|
||||||
|
private static readonly Random Random = new();
|
||||||
|
|
||||||
|
private readonly DrawingVisual _offscreenVisual = new();
|
||||||
|
private readonly ConcurrentQueue<Confetti> _confettis = [];
|
||||||
|
|
||||||
|
private readonly EllipseGeometry _sharedCircleGeometry = new()
|
||||||
|
{
|
||||||
|
Transform = new RotateTransform()
|
||||||
|
};
|
||||||
|
|
||||||
|
private Rect _renderRect;
|
||||||
|
private AdornerLayer _currentAdornerLayer;
|
||||||
|
|
||||||
|
private ConfettiCannon()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fire()
|
||||||
|
{
|
||||||
|
Fire(new Options());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Fire(Options options)
|
||||||
|
{
|
||||||
|
ConfettiCannon confettiCannon = EnsureConfettiCannon(options.Token);
|
||||||
|
confettiCannon.ShowContainer(options.Token);
|
||||||
|
confettiCannon.AddConfettis(options);
|
||||||
|
confettiCannon.StartAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Register(string token, FrameworkElement element)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token) || element is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerDict[token] = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Unregister(FrameworkElement element)
|
||||||
|
{
|
||||||
|
if (element is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var first = ContainerDict.FirstOrDefault(item => ReferenceEquals(element, item.Value));
|
||||||
|
Unregister(first.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Unregister(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainerDict.Remove(token);
|
||||||
|
ConfettiCannons.TryRemove(token, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfettiCannon EnsureConfettiCannon(string token)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
return DefaultConfettiCannon.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfettiCannons.TryGetValue(token, out ConfettiCannon confettiCannon))
|
||||||
|
{
|
||||||
|
return confettiCannon;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfettiCannons[token] = new ConfettiCannon();
|
||||||
|
return ConfettiCannons[token];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (d is not FrameworkElement element)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.NewValue is null)
|
||||||
|
{
|
||||||
|
Unregister(element);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Register(e.NewValue.ToString(), element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AdornerLayer GetAdornerLayer(string token)
|
||||||
|
{
|
||||||
|
AdornerDecorator decorator;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(token))
|
||||||
|
{
|
||||||
|
decorator = VisualHelper.GetChild<AdornerDecorator>(WindowHelper.GetActiveWindow());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ContainerDict.TryGetValue(token, out FrameworkElement element);
|
||||||
|
decorator = element is System.Windows.Window
|
||||||
|
? VisualHelper.GetChild<AdornerDecorator>(element)
|
||||||
|
: VisualHelper.GetChild<ConfettiCannonContainer>(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decorator?.AdornerLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Brush HexToBrush(string hex)
|
||||||
|
{
|
||||||
|
return new SolidColorBrush((Color) ColorConverter.ConvertFromString(hex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateConfetti(DrawingContext context, Confetti confetti)
|
||||||
|
{
|
||||||
|
confetti.Point = new Point(
|
||||||
|
confetti.Point.X + Math.Sin(confetti.Angle2D) * confetti.Velocity + confetti.Drift,
|
||||||
|
confetti.Point.Y + Math.Cos(confetti.Angle2D) * confetti.Velocity + confetti.Gravity
|
||||||
|
);
|
||||||
|
confetti.Velocity *= confetti.Decay;
|
||||||
|
|
||||||
|
if (confetti.Flat)
|
||||||
|
{
|
||||||
|
confetti.Wobble = 0;
|
||||||
|
confetti.WobbleX = confetti.Point.X + 10 * confetti.Scalar;
|
||||||
|
confetti.WobbleY = confetti.Point.Y + 10 * confetti.Scalar;
|
||||||
|
|
||||||
|
confetti.TiltSin = 0;
|
||||||
|
confetti.TiltCos = 0;
|
||||||
|
confetti.RandomValue = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
confetti.Wobble += confetti.WobbleSpeed;
|
||||||
|
confetti.WobbleX = confetti.Point.X + 10 * confetti.Scalar * Math.Cos(confetti.Wobble);
|
||||||
|
confetti.WobbleY = confetti.Point.Y + 10 * confetti.Scalar * Math.Sin(confetti.Wobble);
|
||||||
|
|
||||||
|
confetti.TiltAngle += 0.1;
|
||||||
|
confetti.TiltSin = Math.Sin(confetti.TiltAngle);
|
||||||
|
confetti.TiltCos = Math.Cos(confetti.TiltAngle);
|
||||||
|
confetti.RandomValue = Random.NextDouble() + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
double progress = (double) confetti.Tick++ / confetti.TotalTicks;
|
||||||
|
confetti.Brush.Opacity = 1 - progress;
|
||||||
|
|
||||||
|
double x1 = confetti.Point.X + confetti.RandomValue * confetti.TiltCos;
|
||||||
|
double y1 = confetti.Point.Y + confetti.RandomValue * confetti.TiltSin;
|
||||||
|
double x2 = confetti.WobbleX + confetti.RandomValue * confetti.TiltCos;
|
||||||
|
double y2 = confetti.WobbleY + confetti.RandomValue * confetti.TiltSin;
|
||||||
|
|
||||||
|
if ("circle".Equals(confetti.Shape))
|
||||||
|
{
|
||||||
|
double radiusX = Math.Abs(x2 - x1) * confetti.OvalScalar;
|
||||||
|
double radiusY = Math.Abs(y2 - y1) * confetti.OvalScalar;
|
||||||
|
double angle = 18 * confetti.Wobble;
|
||||||
|
|
||||||
|
var geometry = new EllipseGeometry(new Rect(
|
||||||
|
confetti.Point.X - radiusX,
|
||||||
|
confetti.Point.Y - radiusY,
|
||||||
|
radiusX * 2,
|
||||||
|
radiusY * 2
|
||||||
|
))
|
||||||
|
{
|
||||||
|
Transform = new RotateTransform(angle, confetti.Point.X, confetti.Point.Y)
|
||||||
|
};
|
||||||
|
|
||||||
|
context.DrawGeometry(confetti.Brush, null, geometry);
|
||||||
|
}
|
||||||
|
else if ("star".Equals(confetti.Shape))
|
||||||
|
{
|
||||||
|
double rot = Math.PI / 2 * 3;
|
||||||
|
double innerRadius = 4 * confetti.Scalar;
|
||||||
|
double outerRadius = 8 * confetti.Scalar;
|
||||||
|
int spikes = 5;
|
||||||
|
double step = Math.PI / spikes;
|
||||||
|
bool isFirst = true;
|
||||||
|
|
||||||
|
var geometry = new StreamGeometry();
|
||||||
|
using (StreamGeometryContext sgc = geometry.Open())
|
||||||
|
{
|
||||||
|
while (spikes-- > 0)
|
||||||
|
{
|
||||||
|
double x = confetti.Point.X + Math.Cos(rot) * outerRadius;
|
||||||
|
double y = confetti.Point.Y + Math.Sin(rot) * outerRadius;
|
||||||
|
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
sgc.BeginFigure(new Point(x, y), true, true);
|
||||||
|
isFirst = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sgc.LineTo(new Point(x, y), false, false);
|
||||||
|
rot += step;
|
||||||
|
|
||||||
|
x = confetti.Point.X + Math.Cos(rot) * innerRadius;
|
||||||
|
y = confetti.Point.Y + Math.Sin(rot) * innerRadius;
|
||||||
|
sgc.LineTo(new Point(x, y), false, false);
|
||||||
|
rot += step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.DrawGeometry(confetti.Brush, null, geometry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var geometry = new StreamGeometry();
|
||||||
|
using (StreamGeometryContext sgc = geometry.Open())
|
||||||
|
{
|
||||||
|
sgc.BeginFigure(new Point(Math.Floor(confetti.Point.X), Math.Floor(confetti.Point.Y)), true, true);
|
||||||
|
sgc.LineTo(new Point(Math.Floor(confetti.WobbleX), Math.Floor(y1)), false, false);
|
||||||
|
sgc.LineTo(new Point(Math.Floor(x2), Math.Floor(y2)), false, false);
|
||||||
|
sgc.LineTo(new Point(Math.Floor(x1), Math.Floor(confetti.WobbleY)), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.DrawGeometry(confetti.Brush, null, geometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartAnimation()
|
||||||
|
{
|
||||||
|
CompositionTarget.Rendering -= CompositionTargetOnRendering;
|
||||||
|
CompositionTarget.Rendering += CompositionTargetOnRendering;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CompositionTargetOnRendering(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
RecycleConfettis();
|
||||||
|
|
||||||
|
using (DrawingContext context = _offscreenVisual.RenderOpen())
|
||||||
|
{
|
||||||
|
context.PushClip(new RectangleGeometry(_renderRect));
|
||||||
|
foreach (Confetti confetti in _confettis)
|
||||||
|
{
|
||||||
|
UpdateConfetti(context, confetti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_confettis.Count == 0)
|
||||||
|
{
|
||||||
|
HideContainer();
|
||||||
|
CompositionTarget.Rendering -= CompositionTargetOnRendering;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecycleConfettis()
|
||||||
|
{
|
||||||
|
int initialCount = _confettis.Count;
|
||||||
|
|
||||||
|
for (int i = 0; i < initialCount; i++)
|
||||||
|
{
|
||||||
|
if (_confettis.TryDequeue(out var confetti))
|
||||||
|
{
|
||||||
|
if (!confetti.IsCompleted)
|
||||||
|
{
|
||||||
|
_confettis.Enqueue(confetti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddConfettis(Options options)
|
||||||
|
{
|
||||||
|
double radAngle = (270 - options.Angle) * (Math.PI / 180);
|
||||||
|
double radSpread = options.Spread * (Math.PI / 180);
|
||||||
|
|
||||||
|
Point origin = options.Origin;
|
||||||
|
double startVelocity = options.StartVelocity;
|
||||||
|
int ticks = options.Ticks;
|
||||||
|
double decay = options.Decay;
|
||||||
|
double draft = options.Drift;
|
||||||
|
double gravity = options.Gravity;
|
||||||
|
double scalar = options.Scalar;
|
||||||
|
bool flat = options.Flat;
|
||||||
|
List<Brush> brushes = options.GetFinalBrushes();
|
||||||
|
|
||||||
|
for (int i = 0; i < options.ParticleCount; i++)
|
||||||
|
{
|
||||||
|
_confettis.Enqueue(new Confetti
|
||||||
|
{
|
||||||
|
Point = new Point(origin.X * _renderRect.Width, origin.Y * _renderRect.Height),
|
||||||
|
Wobble = Random.NextDouble() * 10,
|
||||||
|
WobbleSpeed = Math.Min(0.11, Random.NextDouble() * 0.1 + 0.05),
|
||||||
|
Velocity = startVelocity * 0.5 + Random.NextDouble() * startVelocity,
|
||||||
|
Angle2D = -radAngle + (0.5 * radSpread - Random.NextDouble() * radSpread),
|
||||||
|
TiltAngle = (Random.NextDouble() * (0.75 - 0.25) + 0.25) * Math.PI,
|
||||||
|
Brush = brushes[i % brushes.Count],
|
||||||
|
Shape = options.Shapes[Random.Next(options.Shapes.Count)],
|
||||||
|
TotalTicks = ticks,
|
||||||
|
Decay = decay,
|
||||||
|
Drift = draft,
|
||||||
|
RandomValue = Random.NextDouble() + 2,
|
||||||
|
Gravity = gravity * 3,
|
||||||
|
OvalScalar = 0.6,
|
||||||
|
Scalar = scalar,
|
||||||
|
Flat = flat,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowContainer(string token)
|
||||||
|
{
|
||||||
|
AdornerLayer layer = GetAdornerLayer(token);
|
||||||
|
if (layer is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderRect = new Rect(layer.RenderSize);
|
||||||
|
|
||||||
|
if (ReferenceEquals(_currentAdornerLayer, layer))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentAdornerLayer = layer;
|
||||||
|
|
||||||
|
layer.Add(new VisualAdornerContainer(layer)
|
||||||
|
{
|
||||||
|
Child = _offscreenVisual
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideContainer()
|
||||||
|
{
|
||||||
|
var adorner = VisualHelper.GetParent<VisualAdornerContainer>(_offscreenVisual);
|
||||||
|
if (adorner != null)
|
||||||
|
{
|
||||||
|
adorner.Child = null;
|
||||||
|
_currentAdornerLayer?.Remove(adorner);
|
||||||
|
_currentAdornerLayer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Options
|
||||||
|
{
|
||||||
|
private static readonly List<string> DefaultColors =
|
||||||
|
[
|
||||||
|
"#26ccff",
|
||||||
|
"#a25afd",
|
||||||
|
"#ff5e7e",
|
||||||
|
"#88ff5a",
|
||||||
|
"#fcff42",
|
||||||
|
"#ffa62d",
|
||||||
|
"#ff36ff"
|
||||||
|
];
|
||||||
|
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
public int ParticleCount { get; set; } = 50;
|
||||||
|
|
||||||
|
public double Angle { get; set; } = 90;
|
||||||
|
|
||||||
|
public double Spread { get; set; } = 45;
|
||||||
|
|
||||||
|
public double StartVelocity { get; set; } = 45;
|
||||||
|
|
||||||
|
public double Decay { get; set; } = 0.9;
|
||||||
|
|
||||||
|
public double Gravity { get; set; } = 1;
|
||||||
|
|
||||||
|
public double Drift { get; set; }
|
||||||
|
|
||||||
|
public int Ticks { get; set; } = 200;
|
||||||
|
|
||||||
|
public Point Origin { get; set; } = new(0.5, 0.5);
|
||||||
|
|
||||||
|
public List<string> Shapes { get; set; } = ["square", "circle"];
|
||||||
|
|
||||||
|
public List<Brush> Brushes { get; set; } = [];
|
||||||
|
|
||||||
|
public List<string> Colors { get; set; } = DefaultColors;
|
||||||
|
|
||||||
|
public double Scalar { get; set; } = 1;
|
||||||
|
|
||||||
|
public bool Flat { get; set; }
|
||||||
|
|
||||||
|
internal List<Brush> GetFinalBrushes()
|
||||||
|
{
|
||||||
|
if (Brushes is not null && Brushes.Count != 0)
|
||||||
|
{
|
||||||
|
return Brushes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Colors is null || Colors.Count == 0)
|
||||||
|
{
|
||||||
|
return DefaultColors.Select(HexToBrush).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Colors.Select(HexToBrush).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Confetti
|
||||||
|
{
|
||||||
|
public Point Point { get; set; }
|
||||||
|
|
||||||
|
public double Wobble { get; set; }
|
||||||
|
|
||||||
|
public double WobbleSpeed { get; set; }
|
||||||
|
|
||||||
|
public double Velocity { get; set; }
|
||||||
|
|
||||||
|
public double Angle2D { get; set; }
|
||||||
|
|
||||||
|
public double TiltAngle { get; set; }
|
||||||
|
|
||||||
|
public Brush Brush { get; set; }
|
||||||
|
|
||||||
|
public object Shape { get; set; }
|
||||||
|
|
||||||
|
public int Tick { get; set; }
|
||||||
|
|
||||||
|
public int TotalTicks { get; set; }
|
||||||
|
|
||||||
|
public double Decay { get; set; }
|
||||||
|
|
||||||
|
public double Drift { get; set; }
|
||||||
|
|
||||||
|
public double RandomValue { get; set; }
|
||||||
|
|
||||||
|
public double TiltSin { get; set; }
|
||||||
|
|
||||||
|
public double TiltCos { get; set; }
|
||||||
|
|
||||||
|
public double WobbleX { get; set; }
|
||||||
|
|
||||||
|
public double WobbleY { get; set; }
|
||||||
|
|
||||||
|
public double Gravity { get; set; }
|
||||||
|
|
||||||
|
public double Scalar { get; set; }
|
||||||
|
|
||||||
|
public double OvalScalar { get; set; }
|
||||||
|
|
||||||
|
public bool Flat { get; set; }
|
||||||
|
|
||||||
|
public bool IsCompleted => Tick >= TotalTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using System.Windows.Documents;
|
||||||
|
|
||||||
|
namespace HandyControl.Controls;
|
||||||
|
|
||||||
|
public class ConfettiCannonContainer : AdornerDecorator;
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace HandyControl.Controls;
|
||||||
|
|
||||||
|
public class VisualAdornerContainer(UIElement child) : Adorner(child)
|
||||||
|
{
|
||||||
|
private Visual _child = child;
|
||||||
|
|
||||||
|
public Visual Child
|
||||||
|
{
|
||||||
|
get => _child;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
RemoveVisualChild(_child);
|
||||||
|
// ReSharper disable once ExpressionIsAlwaysNull
|
||||||
|
_child = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AddVisualChild(value);
|
||||||
|
_child = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override int VisualChildrenCount => _child != null ? 1 : 0;
|
||||||
|
|
||||||
|
protected override Visual GetVisualChild(int index)
|
||||||
|
{
|
||||||
|
if (index == 0 && _child != null)
|
||||||
|
{
|
||||||
|
return _child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.GetVisualChild(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,8 @@
|
|||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Block\RunningBlock\RunningBorder.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Block\RunningBlock\RunningBorder.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Button\SplitButton.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Button\SplitButton.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Carousel\CarouselItem.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Carousel\CarouselItem.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\ConfettiCannon\ConfettiCannon.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\ConfettiCannon\ConfettiCannonContainer.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Dialog\DialogContainer.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Dialog\DialogContainer.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Drawer\DrawerContainer.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Drawer\DrawerContainer.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Growl\GrowlWindow.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Growl\GrowlWindow.cs" />
|
||||||
@@ -56,6 +58,7 @@
|
|||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Card.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Card.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Notification.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Notification.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Poptip.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Poptip.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\VisualAdornerContainer.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Watermark.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Other\Watermark.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Panel\AxleCanvas.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Panel\AxleCanvas.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Panel\ClipGrid.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Controls\Panel\ClipGrid.cs" />
|
||||||
@@ -953,6 +956,7 @@
|
|||||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.Col.icon.bmp" />
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.Col.icon.bmp" />
|
||||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.ColorPicker.icon.bmp" />
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.ColorPicker.icon.bmp" />
|
||||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.CompareSlider.icon.bmp" />
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.CompareSlider.icon.bmp" />
|
||||||
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.ConfettiCannon.icon.bmp" />
|
||||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.ContextMenuButton.icon.bmp" />
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.ContextMenuButton.icon.bmp" />
|
||||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.ContextMenuToggleButton.icon.bmp" />
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.ContextMenuToggleButton.icon.bmp" />
|
||||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.CoverFlow.icon.bmp" />
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\Icons\HandyControl.Controls.CoverFlow.icon.bmp" />
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 824 B |
Reference in new issue
Block a user