Here’s an explosive behaviour that was first seen a while ago from Microsoft IT.
Here’s the source:
MainPage.xaml
<UserControl x:Class="Explosions.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Explosions="clr-namespace:Explosions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d" Width="400" Height="500">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Margin="20">
<Button x:Name="button" Content="Click me" Margin="20" Height="50" FontSize="29.333">
<i:Interaction.Behaviors>
<Explosions:ExplodeBehavior>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
<i:InvokeCommandAction CommandName="Ignite"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Explosions:ExplodeBehavior>
</i:Interaction.Behaviors>
</Button>
<TextBlock x:Name="textBlock" TextWrapping="Wrap" Text="Or click me" HorizontalAlignment="Center" Margin="20" FontSize="29.333" >
<i:Interaction.Behaviors>
<Explosions:ExplodeBehavior>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown" SourceObject="{Binding ElementName=textBlock}">
<i:InvokeCommandAction CommandName="Ignite"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Explosions:ExplodeBehavior>
</i:Interaction.Behaviors>
</TextBlock>
<Image x:Name="image" Height="200" Source="/hoff.PNG" Margin="20">
<i:Interaction.Behaviors>
<Explosions:ExplodeBehavior>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown" SourceObject="{Binding ElementName=image}">
<i:InvokeCommandAction CommandName="Ignite"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Explosions:ExplodeBehavior>
</i:Interaction.Behaviors>
</Image>
</StackPanel>
</Grid>
</UserControl>
ExplosionBehavior.cs
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Explosions
{
public class ExplodeBehavior : Behavior<FrameworkElement>
{
public static readonly DependencyProperty PowerProperty = DependencyProperty.Register("Power", typeof (double),
typeof (ExplodeBehavior),
new PropertyMetadata(50.0d));
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof (double),
typeof (ExplodeBehavior),
new PropertyMetadata(
20.0d));
public static readonly DependencyProperty DepthProperty = DependencyProperty.Register("Depth", typeof (double),
typeof (ExplodeBehavior),
new PropertyMetadata(
-20.0d));
public static readonly DependencyProperty ShrapnelAmountProperty = DependencyProperty.Register(
"ShrapnelAmount", typeof (double), typeof (ExplodeBehavior), new PropertyMetadata(500.0d));
public static readonly DependencyProperty EpicenterProperty = DependencyProperty.Register("Epicenter",
typeof (Point),
typeof (
ExplodeBehavior),
new PropertyMetadata(
new Point(.5, .5)));
public static readonly DependencyProperty ReverseDurationProperty =
DependencyProperty.Register("ReverseDuration", typeof (TimeSpan), typeof (ExplodeBehavior),
new PropertyMetadata(TimeSpan.FromSeconds(1)));
private readonly List<Shard> shards = new List<Shard>();
private Point? lastMousePoint;
private Popup popup;
public ExplodeBehavior()
{
}
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.MouseMove += this.HandleMouseMove;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.MouseMove -= this.HandleMouseMove;
}
public Point Epicenter
{
get { return (Point) this.GetValue(ExplodeBehavior.EpicenterProperty); }
set { this.SetValue(ExplodeBehavior.EpicenterProperty, value); }
}
public double ShrapnelAmount
{
get { return (double) this.GetValue(ExplodeBehavior.ShrapnelAmountProperty); }
set { this.SetValue(ExplodeBehavior.ShrapnelAmountProperty, value); }
}
public double Depth
{
get { return (double) this.GetValue(ExplodeBehavior.DepthProperty); }
set { this.SetValue(ExplodeBehavior.DepthProperty, value); }
}
public double Radius
{
get { return (double) this.GetValue(ExplodeBehavior.RadiusProperty); }
set { this.SetValue(ExplodeBehavior.RadiusProperty, value); }
}
public double Power
{
get { return (double) this.GetValue(ExplodeBehavior.PowerProperty); }
set { this.SetValue(ExplodeBehavior.PowerProperty, value); }
}
public TimeSpan ReverseDuration
{
get { return (TimeSpan) this.GetValue(ExplodeBehavior.ReverseDurationProperty); }
set { this.SetValue(ExplodeBehavior.ReverseDurationProperty, value); }
}
[DefaultTrigger(typeof (ButtonBase), typeof (System.Windows.Interactivity.EventTrigger), "Click"),
DefaultTrigger(typeof (UIElement), typeof (System.Windows.Interactivity.EventTrigger), "MouseLeftButtonDown")]
public ICommand Ignite
{
get
{
return new DelegateCommand(delegate(object args)
{
this.StartExplode();
});
}
}
public ICommand Implode
{
get
{
return new DelegateCommand(delegate(object args)
{
this.StartImplode();
});
}
}
private void HandleMouseMove(object sender, MouseEventArgs e)
{
this.lastMousePoint = e.GetPosition(this.AssociatedObject);
}
private void StartExplode()
{
if (this.AssociatedObject == null)
return;
Vector3D position;
if (this.ReadLocalValue(ExplodeBehavior.EpicenterProperty) == DependencyProperty.UnsetValue &&
this.lastMousePoint.HasValue)
position = new Vector3D(this.lastMousePoint.Value.X, this.lastMousePoint.Value.Y, this.Depth);
else
position = new Vector3D(this.AssociatedObject.ActualWidth*this.Epicenter.X,
this.AssociatedObject.ActualHeight*this.Epicenter.Y, this.Depth);
this.StartExplode(position);
}
public void StartExplode(double x, double y, double z)
{
this.StartExplode(new Vector3D(x, y, z));
}
private void StartExplode(Vector3D point)
{
if (this.shards.Count == 0)
this.PrepareShards(point);
this.ApplyForce(point);
}
private void StartImplode()
{
Storyboard sb = new Storyboard();
sb.Duration = new Duration(this.ReverseDuration);
foreach (Shard shard in this.shards)
shard.Reverse(this.ReverseDuration, sb);
sb.Completed += delegate
{
if (this.popup != null)
{
this.shards.Clear();
this.AssociatedObject.Opacity = 1;
this.popup.IsOpen = false;
this.popup = null;
}
;
};
sb.Begin();
}
private void PrepareShards(Vector3D point)
{
FrameworkElement content = this.AssociatedObject;
if (content == null)
return;
double width = content.ActualWidth;
double height = content.ActualHeight;
Grid container = new Grid();
this.popup = new Popup();
popup.Child = container;
Point popupOffset = content.TransformToVisual(Application.Current.RootVisual).Transform(new Point(0, 0));
popup.HorizontalOffset = popupOffset.X;
popup.VerticalOffset = popupOffset.Y;
popup.IsHitTestVisible = false;
container.IsHitTestVisible = false;
container.Width = width;
container.Height = height;
double rows = Math.Sqrt(this.ShrapnelAmount*height/width);
double columns = this.ShrapnelAmount/rows;
int rowCount = (int) Math.Round(rows);
int columnCount = (int) Math.Round(columns);
for (int x = 0; x < columnCount; ++x)
container.ColumnDefinitions.Add(new ColumnDefinition()
{
Width = new GridLength(1, GridUnitType.Star),
});
for (int y = 0; y < rowCount; ++y)
container.RowDefinitions.Add(new RowDefinition()
{
Height = new GridLength(1, GridUnitType.Star),
});
WriteableBitmap bitmap = new WriteableBitmap(content, null);
for (int x = 0; x < columnCount; ++x)
{
for (int y = 0; y < rowCount; ++y)
{
Rectangle element = new Rectangle();
ImageBrush brush = new ImageBrush()
{
ImageSource = bitmap,
};
ScaleTransform scale = new ScaleTransform()
{
ScaleX = columnCount,
ScaleY = rowCount,
};
TranslateTransform translation = new TranslateTransform()
{
X = -x/(double) columnCount,
Y = -y/(double) rowCount,
};
TransformGroup transform = new TransformGroup();
transform.Children.Add(translation);
transform.Children.Add(scale);
brush.RelativeTransform = transform;
element.Fill = brush;
Grid.SetColumn(element, x);
Grid.SetRow(element, y);
container.Children.Add(element);
Shard shard = new Shard(element,
new Point(width*(x + .5)/columnCount,
height*(y + .5)/rowCount));
this.shards.Add(shard);
}
}
content.Opacity = 0;
popup.IsOpen = true;
}
private void ApplyForce(Vector3D point)
{
Storyboard sb = new Storyboard()
{
Duration = new Duration(TimeSpan.FromSeconds(100)),
};
foreach (Shard shard in this.shards)
{
Vector3D position = shard.Position;
Vector3D delta = position - point;
double magnitude = -Math.Pow(delta.Length, 2)/(2*this.Radius*this.Radius);
double power = this.Power*Math.Pow(Math.E, magnitude);
delta.Normalize();
delta = delta*power*10;
shard.TranslationVelocity += delta;
Vector3D offset = point - position;
Vector3D rotation = new Vector3D(-offset.Y, -offset.X, offset.Z);
rotation.Normalize();
rotation = rotation*power*10;
rotation.Z = 0;
shard.RotationalVelocity += rotation;
shard.StartAnims(sb);
}
sb.Begin();
}
private class Shard
{
private readonly PlaneProjection projection = new PlaneProjection();
private Point initialPosition;
public Shard(FrameworkElement target, Point initialPosition)
{
this.Target = target;
this.initialPosition = initialPosition;
target.Projection = this.projection;
}
public FrameworkElement Target { get; private set; }
public Vector3D RotationalVelocity { get; set; }
public Vector3D TranslationVelocity { get; set; }
public void Update()
{
this.projection.RotationX += this.RotationalVelocity.X;
this.projection.RotationY += this.RotationalVelocity.Y;
this.projection.RotationZ += this.RotationalVelocity.Z;
this.projection.GlobalOffsetX += this.TranslationVelocity.X;
this.projection.GlobalOffsetY += this.TranslationVelocity.Y;
this.projection.GlobalOffsetZ += this.TranslationVelocity.Z;
}
public Vector3D Position
{
get
{
return new Vector3D(this.initialPosition.X + this.projection.GlobalOffsetX,
this.initialPosition.Y + this.projection.GlobalOffsetY,
this.projection.GlobalOffsetZ);
}
}
public void StartAnims(Storyboard sb)
{
TimeSpan duration = TimeSpan.FromSeconds(100);
sb.Duration = new Duration(duration);
DoubleAnimation tx = new DoubleAnimation()
{
To = this.TranslationVelocity.X*duration.TotalSeconds,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(tx, new PropertyPath(PlaneProjection.GlobalOffsetXProperty));
Storyboard.SetTarget(tx, this.projection);
sb.Children.Add(tx);
DoubleAnimation ty = new DoubleAnimation()
{
To = this.TranslationVelocity.Y*duration.TotalSeconds,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(ty, new PropertyPath(PlaneProjection.GlobalOffsetYProperty));
Storyboard.SetTarget(ty, this.projection);
sb.Children.Add(ty);
DoubleAnimation tz = new DoubleAnimation()
{
To = this.TranslationVelocity.Z*duration.TotalSeconds,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(tz, new PropertyPath(PlaneProjection.GlobalOffsetZProperty));
Storyboard.SetTarget(tz, this.projection);
sb.Children.Add(tz);
DoubleAnimation rx = new DoubleAnimation()
{
To = this.RotationalVelocity.X*duration.TotalSeconds,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(rx, new PropertyPath(PlaneProjection.RotationXProperty));
Storyboard.SetTarget(rx, this.projection);
sb.Children.Add(rx);
DoubleAnimation ry = new DoubleAnimation()
{
To = this.RotationalVelocity.Y*duration.TotalSeconds,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(ry, new PropertyPath(PlaneProjection.RotationYProperty));
Storyboard.SetTarget(ry, this.projection);
sb.Children.Add(ry);
DoubleAnimation rz = new DoubleAnimation()
{
To = this.RotationalVelocity.Z*duration.TotalSeconds,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(rz, new PropertyPath(PlaneProjection.RotationZProperty));
Storyboard.SetTarget(rz, this.projection);
sb.Children.Add(rz);
Storyboard.SetTarget(sb, this.Target);
}
public void Reverse(TimeSpan duration, Storyboard sb)
{
sb.Duration = new Duration(duration);
DoubleAnimation tx = new DoubleAnimation()
{
To = 0,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(tx, new PropertyPath(PlaneProjection.GlobalOffsetXProperty));
Storyboard.SetTarget(tx, this.projection);
sb.Children.Add(tx);
DoubleAnimation ty = new DoubleAnimation()
{
To = 0,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(ty, new PropertyPath(PlaneProjection.GlobalOffsetYProperty));
Storyboard.SetTarget(ty, this.projection);
sb.Children.Add(ty);
DoubleAnimation tz = new DoubleAnimation()
{
To = 0,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(tz, new PropertyPath(PlaneProjection.GlobalOffsetZProperty));
Storyboard.SetTarget(tz, this.projection);
sb.Children.Add(tz);
DoubleAnimation rx = new DoubleAnimation()
{
To = 0,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(rx, new PropertyPath(PlaneProjection.RotationXProperty));
Storyboard.SetTarget(rx, this.projection);
sb.Children.Add(rx);
DoubleAnimation ry = new DoubleAnimation()
{
To = 0,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(ry, new PropertyPath(PlaneProjection.RotationYProperty));
Storyboard.SetTarget(ry, this.projection);
sb.Children.Add(ry);
DoubleAnimation rz = new DoubleAnimation()
{
To = 0,
Duration = new Duration(duration),
};
Storyboard.SetTargetProperty(rz, new PropertyPath(PlaneProjection.RotationZProperty));
Storyboard.SetTarget(rz, this.projection);
sb.Children.Add(rz);
Storyboard.SetTarget(sb, this.Target);
}
}
private class DelegateCommand : ICommand
{
public delegate void Handler(object args);
private Handler handler;
public DelegateCommand(Handler handler)
{
this.handler = handler;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
this.handler(parameter);
}
protected virtual void OnCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
this.CanExecuteChanged(this, EventArgs.Empty);
}
}
private struct Vector3D
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public Vector3D(double x, double y, double z)
: this()
{
this.X = x;
this.Y = y;
this.Z = z;
}
public static Vector3D operator -(Vector3D a, Vector3D b)
{
return new Vector3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
public static Vector3D operator +(Vector3D a, Vector3D b)
{
return new Vector3D(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
}
public static Vector3D operator *(Vector3D vector, double scalar)
{
return new Vector3D(vector.X*scalar, vector.Y*scalar, vector.Z*scalar);
}
public static Vector3D operator /(Vector3D vector, double scalar)
{
return (Vector3D) (vector*(1.0/scalar));
}
public double Length
{
get { return Math.Sqrt(this.X*this.X + this.Y*this.Y + this.Z*this.Z); }
}
public void Normalize()
{
double x = Math.Abs(this.X);
double y = Math.Abs(this.Y);
double z = Math.Abs(this.Z);
if (z > x)
x = z;
if (y > x)
x = y;
this.X /= x;
this.Y /= x;
this.Z /= x;
double length = Math.Sqrt(this.X*this.X + this.Y*this.Y + this.Z*this.Z);
this = (Vector3D) (this/length);
}
public Vector3D Cross(Vector3D other)
{
Vector3D result = new Vector3D();
result.X = this.Y*other.Z - this.Z*other.Y;
result.Y = this.Z*other.X - this.X*other.Z;
result.Z = this.X*other.Y - this.Y*other.X;
return result;
}
public override string ToString()
{
return this.X + "," + this.Y + "," + this.Z;
}
}
}
}
Live demo: http://stevenhollidge.com/blog-source-code/explosions
Source: https://github.com/stevenh77/Explosions
No comments:
Post a Comment