feat: add ConfettiCannon.
This commit is contained in:
1 parent
94aa32cec1
commit
79b32d573c
13 files changed
+889
-13
No files matched your search
@@ -2,7 +2,7 @@
|
||||
{
|
||||
"title": "Styles",
|
||||
"selectedIndex": -1,
|
||||
"group": false,
|
||||
"group": false,
|
||||
"demoItemList": [
|
||||
[ "Brush", "BrushDemo", "Brush.Brush", "", "" ],
|
||||
[ "Button", "ButtonDemo", "Brush.ButtonClick", "", "" ],
|
||||
@@ -43,6 +43,7 @@
|
||||
"group": true,
|
||||
"demoItemList": [
|
||||
[ "FloatingBlock", "FloatingBlockDemo", "Brush.LikeFilled", "", "Interacting" ],
|
||||
[ "ConfettiCannon", "ConfettiCannonDemo", "Brush.Favorite", "1", "Interacting" ],
|
||||
[ "PropertyGrid", "PropertyGridDemo", "Brush.PropertyGridEditorPart", "", "DataEntry" ],
|
||||
[ "ImageSelector", "ImageSelectorDemo", "Brush.Image", "", "DataEntry" ],
|
||||
[ "ImageBlock", "ImageBlockDemo", "Brush.ImageStack", "", "DataDisplay" ],
|
||||
@@ -79,7 +80,7 @@
|
||||
[ "Divider", "DividerDemo", "Brush.DottedSplitter", "", "Layout" ],
|
||||
[ "CirclePanel", "CirclePanelDemo", "Brush.ColorWheel", "", "Layout" ],
|
||||
[ "Grid", "GridDemo", "Brush.GridPane", "", "Layout" ],
|
||||
[ "FlexPanel", "FlexPanelDemo", "Brush.FlowLayoutPanel", "1", "Layout" ],
|
||||
[ "FlexPanel", "FlexPanelDemo", "Brush.FlowLayoutPanel", "", "Layout" ],
|
||||
[ "UniformSpacingPanel", "UniformSpacingPanelDemo", "Brush.IncreaseHorizontalSpacing", "1", "Layout" ],
|
||||
[ "RelativePanel", "RelativePanelDemo", "Brush.TransformRelative", "", "Layout" ],
|
||||
[ "WaterfallPanel", "WaterfallPanelDemo", "Brush.Panel", "", "Layout" ],
|
||||
@@ -121,8 +122,8 @@
|
||||
"group": false,
|
||||
"demoItemList": [
|
||||
[ "HatchBrushGenerator", "HatchBrushGeneratorDemo", "Brush.DataGenerator", "", "" ],
|
||||
[ "Morphing_Animation", "GeometryAnimationDemo", "Brush.Animation", "", "" ],
|
||||
[ "Effects", "EffectsDemo", "Brush.Effects", "", "" ]
|
||||
[ "MorphingAnimation", "GeometryAnimationDemo", "Brush.Animation", "", "" ],
|
||||
[ "Effects", "EffectsDemo", "Brush.Effects", "", "" ],
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -64,6 +64,9 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\AutoCompleteTextBoxDemo.xaml.cs">
|
||||
<DependentUpon>AutoCompleteTextBoxDemo.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\ConfettiCannonDemo.xaml.cs">
|
||||
<DependentUpon>ConfettiCannonDemo.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UserControl\Controls\ElementGroupDemo.xaml.cs">
|
||||
<DependentUpon>ElementGroupDemo.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -551,6 +554,7 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)UserControl\Controls\ConfettiCannonDemo.xaml" />
|
||||
<Page Include="$(MSBuildThisFileDirectory)UserControl\Controls\ElementGroupDemo.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<ImageDrawing o:Freeze="True" ImageSource="/HandyControlDemo;component/Resources/Img/cloud.png" Rect="0,0,157,157"/>
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
|
||||
|
||||
<DrawingBrush o:Freeze="True" x:Key="MainContentForegroundDrawingBrush" RenderOptions.CacheInvalidationThresholdMinimum="0.5" RenderOptions.CacheInvalidationThresholdMaximum="2.0" RenderOptions.CachingHint="Cache" Viewport="0,0,16,16" ViewportUnits="Absolute" Stretch="None" TileMode="Tile">
|
||||
<DrawingBrush.Drawing>
|
||||
<GeometryDrawing o:Freeze="True" Brush="{DynamicResource MainContentForegroundBrush}">
|
||||
@@ -57,7 +57,7 @@
|
||||
<GeometryDrawing o:Freeze="True" Brush="#f06632" Geometry="{StaticResource LogoGeometry}"/>
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
|
||||
|
||||
<DrawingBrush o:Freeze="True" x:Key="Brush.SortAscending">
|
||||
<DrawingBrush.Drawing>
|
||||
<DrawingGroup>
|
||||
@@ -71,7 +71,7 @@
|
||||
</DrawingGroup>
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
|
||||
|
||||
<DrawingBrush o:Freeze="True" x:Key="Brush.Brush">
|
||||
<DrawingBrush.Drawing>
|
||||
<DrawingGroup>
|
||||
@@ -1387,5 +1387,17 @@
|
||||
</DrawingGroup>
|
||||
</DrawingBrush.Drawing>
|
||||
</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-->
|
||||
</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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user