当前位置:网站首页>WPF achieves star effect
WPF achieves star effect
2022-06-22 03:46:00 【Dotnet cross platform】
This article is authorized by the original author to share it twice in an original way , Welcome to reprint 、 Share .
Original author : Ordinary earthman
Original address :https://www.cnblogs.com/tsliwei/p/6282183.html
Github Address :https://github.com/WPFDevelopersOrg/WPFDevelopers
effect
I saw... A while ago ay Spider web effect and landing page background , I think the effect is very cool . I also want to write a . So the writing became like this . The girl's dream is to catch up with her feet . I am a stingy man with a girlish heart ;
The realization idea is divided into two parts :
1) The stars roam endlessly ;
2) The line between the stars ;
Don't put stars and wires in two containers , For separate operation ;
The stars
Decompose the motion of the stars into
XAxis andYTwo incoherent motions of an axis , Separate operation . Operation is to randomly generate a speed , Randomly generate a time . After the movement, a random speed is generated , Randomly generate a time ...... Infinite loop ;The same is true of the rotation of stars ;
attachment
First, explain the rules of connection . A line between two stars , Each star has a wired sphere of influence , Is the width times the line magnification , This connection ratio can be set in the window . When two spheres of influence intersect , Just connect ;
example :
star 1 Width 5,star 2 Width 10,Connection magnification is 3, Then the distance between the two stars is less than5*3+10*3=45Connect as soon as possible , Greater than45Disconnect when . If the connection magnification is set to4, Then the distance between the two stars is less than5*4+10*4=60Time connection , Greater than60Disconnect when ;
Realization and resource occupancy
There are two ways to realize the motion of stars :
1) be based on Grid and TranslateTransform use DoubleAnimation Animation controls the displacement of stars .
2) be based on Canvas Control through frame animation Canvas Of X,Y.
There are also two ways to implement the connection :
1) Simple and crude . Empty the connection container at each frame . And then double circle the stars , Reconnect all stars ( Conform to the connection rules ).
2) Loop the line at each frame , Judge the connection rules . Match the... That changes this connection X1,Y1,X2,Y2 Instead of trying again new attachment . Those that do not conform to the rules are removed . And then there's still a double circle of stars , See if there is a line between the two stars that conform to the rules , If you don't have it, just new One .
as everyone knows ,WPF The share of animation resources is still high , Written so many implementations , It's also because of this .
Basically, it is based on Canvas The implementation of takes a little less resources . But there's a problem , If you add a blur effect to the stars , be based on Canvas The realized resource share will not soar , Instead, the number of frames is significantly reduced .( It may also be the reason for my computer environment )
It can not be said that the realization is good or bad , The specific operating environment may be different , Parameter settings are different , Each implementation has a different representation .
Then on the question of resource occupancy , At my current level , That's the end of it . Bloggers can choose for themselves .
Source code is as follows
1)StarrySky.cs The code is as follows ;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace WPFDevelopers.Controls
{
[TemplatePart(Name = GridTemplateName, Type = typeof(Grid))]
[TemplatePart(Name = CanvasTemplateName, Type = typeof(Canvas))]
public class StarrySky : Control
{
private const string GridTemplateName = "PART_GridLineContainer";
private const string CanvasTemplateName = "PART_CanvasStarContainer";
private Grid _grid;
private Canvas _canvas;
public static readonly DependencyProperty StarCountProperty =
DependencyProperty.Register("StarCount", typeof(int), typeof(StarrySky), new UIPropertyMetadata(10));
public static readonly DependencyProperty StarSizeMinProperty =
DependencyProperty.Register("StarSizeMin", typeof(int), typeof(StarrySky), new UIPropertyMetadata(5));
public static readonly DependencyProperty StarSizeMaxProperty =
DependencyProperty.Register("StarSizeMax", typeof(int), typeof(StarrySky), new UIPropertyMetadata(20));
public static readonly DependencyProperty StarVMinProperty =
DependencyProperty.Register("StarVMin", typeof(int), typeof(StarrySky), new UIPropertyMetadata(10));
public static readonly DependencyProperty StarVMaxProperty =
DependencyProperty.Register("StarVMax", typeof(int), typeof(StarrySky), new UIPropertyMetadata(20));
public static readonly DependencyProperty StarRVMinProperty =
DependencyProperty.Register("StarRVMin", typeof(int), typeof(StarrySky), new UIPropertyMetadata(90));
public static readonly DependencyProperty StarRVMaxProperty =
DependencyProperty.Register("StarRVMax", typeof(int), typeof(StarrySky), new UIPropertyMetadata(360));
public static readonly DependencyProperty LineRateProperty =
DependencyProperty.Register("LineRate", typeof(int), typeof(StarrySky), new UIPropertyMetadata(3));
private readonly Random _random = new Random();
private StarInfo[] _stars;
static StarrySky()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(StarrySky),
new FrameworkPropertyMetadata(typeof(StarrySky)));
}
public StarrySky()
{
Loaded += delegate
{
CompositionTarget.Rendering += delegate
{
StarRoamAnimation();
AddOrRemoveStarLine();
MoveStarLine();
};
};
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_grid = GetTemplateChild(GridTemplateName) as Grid;
_canvas = GetTemplateChild(CanvasTemplateName) as Canvas;
}
public int StarCount
{
get => (int)GetValue(StarCountProperty);
set => SetValue(StarCountProperty, value);
}
public int StarSizeMin
{
get => (int)GetValue(StarSizeMinProperty);
set => SetValue(StarSizeMinProperty, value);
}
public int StarSizeMax
{
get => (int)GetValue(StarSizeMaxProperty);
set => SetValue(StarSizeMaxProperty, value);
}
public int StarVMin
{
get => (int)GetValue(StarVMinProperty);
set => SetValue(StarVMinProperty, value);
}
public int StarVMax
{
get => (int)GetValue(StarVMaxProperty);
set => SetValue(StarVMaxProperty, value);
}
public int StarRVMin
{
get => (int)GetValue(StarRVMinProperty);
set => SetValue(StarRVMinProperty, value);
}
public int StarRVMax
{
get => (int)GetValue(StarRVMaxProperty);
set => SetValue(StarRVMaxProperty, value);
}
public int LineRate
{
get => (int)GetValue(LineRateProperty);
set => SetValue(LineRateProperty, value);
}
public void InitStar()
{
// Empty the star container
_stars = new StarInfo[StarCount];
_canvas.Children.Clear();
_grid.Children.Clear();
// Make stars
for (var i = 0; i < StarCount; i++)
{
double size = _random.Next(StarSizeMin, StarSizeMax + 1); // Star size
var starInfo = new StarInfo
{
X = _random.Next(0, (int)_canvas.ActualWidth),
XV = (double)_random.Next(-StarVMax, StarVMax) / 60,
XT = _random.Next(6, 301), // frame
Y = _random.Next(0, (int)_canvas.ActualHeight),
YV = (double)_random.Next(-StarVMax, StarVMax) / 60,
YT = _random.Next(6, 301), // frame
StarLines = new Dictionary<StarInfo, Line>()
};
var star = new Path
{
Data = Application.Current.Resources["PathStarrySky"] as Geometry,
Width = size,
Height = size,
Stretch = Stretch.Fill,
Fill = GetRandomColorBursh(),
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = new RotateTransform { Angle = 0 }
};
Canvas.SetLeft(star, starInfo.X);
Canvas.SetTop(star, starInfo.Y);
starInfo.StarRef = star;
// Animate star rotation
SetStarRotateAnimation(star);
// Add to container
_stars[i] = starInfo;
_canvas.Children.Add(star);
}
}
private void SetStarRotateAnimation(Path star)
{
double v = _random.Next(StarRVMin, StarRVMax + 1); // Speed
double a = _random.Next(0, 360 * 5); // angle
var t = a / v; // Time
var dur = new Duration(new TimeSpan(0, 0, 0, 0, (int)(t * 1000)));
var sb = new Storyboard
{
Duration = dur
};
// Animation completion event Animate this again
sb.Completed += (S, E) => { SetStarRotateAnimation(star); };
var da = new DoubleAnimation
{
To = a,
Duration = dur
};
Storyboard.SetTarget(da, star);
Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
sb.Children.Add(da);
sb.Begin(this);
}
private SolidColorBrush GetRandomColorBursh()
{
var r = (byte)_random.Next(128, 256);
var g = (byte)_random.Next(128, 256);
var b = (byte)_random.Next(128, 256);
return new SolidColorBrush(Color.FromRgb(r, g, b));
}
/// <summary>
/// Star walkthrough animation
/// </summary>
private void StarRoamAnimation()
{
if (_stars == null)
return;
foreach (var starInfo in _stars)
{
//X Axial motion
if (starInfo.XT > 0)
{
// The exercise time is longer than 0, Keep moving
if (starInfo.X >= _canvas.ActualWidth || starInfo.X <= 0)
// Touch the edge , The speed is reversed
starInfo.XV = -starInfo.XV;
// Displacement plus , Less time
starInfo.X += starInfo.XV;
starInfo.XT--;
Canvas.SetLeft(starInfo.StarRef, starInfo.X);
}
else
{
// The exercise time is less than 0, Reset the speed and time
starInfo.XV = (double)_random.Next(-StarVMax, StarVMax) / 60;
starInfo.XT = _random.Next(100, 1001);
}
//Y Axial motion
if (starInfo.YT > 0)
{
// The exercise time is longer than 0, Keep moving
if (starInfo.Y >= _canvas.ActualHeight || starInfo.Y <= 0)
// Touch the edge , The speed is reversed
starInfo.YV = -starInfo.YV;
// Displacement plus , Less time
starInfo.Y += starInfo.YV;
starInfo.YT--;
Canvas.SetTop(starInfo.StarRef, starInfo.Y);
}
else
{
// The exercise time is less than 0, Reset the speed and time
starInfo.YV = (double)_random.Next(-StarVMax, StarVMax) / 60;
starInfo.YT = _random.Next(100, 1001);
}
}
}
/// <summary>
/// Add or remove lines between stars
/// </summary>
private void AddOrRemoveStarLine()
{
// There are no stars Go straight back to
if (_stars == null || StarCount != _stars.Length)
return;
// Create a line between the stars
for (var i = 0; i < StarCount - 1; i++)
for (var j = i + 1; j < StarCount; j++)
{
var star1 = _stars[i];
var x1 = star1.X + star1.StarRef.Width / 2;
var y1 = star1.Y + star1.StarRef.Height / 2;
var star2 = _stars[j];
var x2 = star2.X + star2.StarRef.Width / 2;
var y2 = star2.Y + star2.StarRef.Height / 2;
var s = Math.Sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1)); // The distance between two stars
var threshold = star1.StarRef.Width * LineRate + star2.StarRef.Width * LineRate;
if (s <= threshold)
{
if (!star1.StarLines.ContainsKey(star2))
{
var line = new Line
{
X1 = x1,
Y1 = y1,
X2 = x2,
Y2 = y2,
Stroke = GetStarLineBrush(star1.StarRef, star2.StarRef)
};
star1.StarLines.Add(star2, line);
_grid.Children.Add(line);
}
}
else
{
if (star1.StarLines.ContainsKey(star2))
{
_grid.Children.Remove(star1.StarLines[star2]);
star1.StarLines.Remove(star2);
}
}
}
}
/// <summary>
/// Move the line between the stars
/// </summary>
private void MoveStarLine()
{
// There are no stars Go straight back to
if (_stars == null)
return;
foreach (var star in _stars)
foreach (var starLine in star.StarLines)
{
var line = starLine.Value;
line.X1 = star.X + star.StarRef.Width / 2;
line.Y1 = star.Y + star.StarRef.Height / 2;
line.X2 = starLine.Key.X + starLine.Key.StarRef.Width / 2;
line.Y2 = starLine.Key.Y + starLine.Key.StarRef.Height / 2;
}
}
/// <summary>
/// Get the star line color brush
/// </summary>
/// <param name="star0"> Starting star </param>
/// <param name="star1"> The end star </param>
/// <returns>LinearGradientBrush</returns>
private LinearGradientBrush GetStarLineBrush(Path star0, Path star1)
{
return new LinearGradientBrush
{
GradientStops = new GradientStopCollection
{
new GradientStop { Offset = 0, Color = (star0.Fill as SolidColorBrush).Color },
new GradientStop { Offset = 1, Color = (star1.Fill as SolidColorBrush).Color }
}
};
}
}
/// <summary>
/// The stars
/// </summary>
internal class StarInfo
{
/// <summary>
/// X coordinate
/// </summary>
public double X { get; set; }
/// <summary>
/// X Shaft speed ( Unit distance / frame )
/// </summary>
public double XV { get; set; }
/// <summary>
/// X Coordinates to X The time the shaft speed runs ( frame )
/// </summary>
public int XT { get; set; }
/// <summary>
/// Y coordinate
/// </summary>
public double Y { get; set; }
/// <summary>
/// Y Shaft speed ( Unit distance / frame )
/// </summary>
public double YV { get; set; }
/// <summary>
/// Y Coordinates to Y The time the shaft speed runs ( frame )
/// </summary>
public int YT { get; set; }
/// <summary>
/// References to stars
/// </summary>
public Path StarRef { get; set; }
public Dictionary<StarInfo, Line> StarLines { get; set; }
}
}2)StarrySky.xaml The code is as follows ;
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WPFDevelopers.Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Basic/ControlBasic.xaml"/>
</ResourceDictionary.MergedDictionaries>
<RadialGradientBrush x:Key="StarrySkyRadialGradientBrush" GradientOrigin="0.5,0" Center="0.5,0.3" RadiusX="0.7">
<GradientStop Color="#FF04040E" Offset="0"/>
<GradientStop Color="#FF24315D" Offset="1"/>
</RadialGradientBrush>
<Style TargetType="{x:Type controls:StarrySky}"
BasedOn="{StaticResource ControlBasicStyle}">
<Setter Property="Background" Value="{StaticResource StarrySkyRadialGradientBrush}"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:StarrySky}">
<Grid Background="{TemplateBinding Background}">
<Grid x:Name="PART_GridLineContainer"/>
<Canvas x:Name="PART_CanvasStarContainer"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>3)StarrySkyExample.xaml The code is as follows ;
<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.StarrySkyExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"
xmlns:ws="https://github.com/WPFDevelopersOrg.WPFDevelopers.Minimal"
xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="FontSize" Value="14"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="Margin" Value="2"></Setter>
</Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="2"></Setter>
</Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="ws:ElementHelper.Watermark" Value=" Input content "></Setter>
<Setter Property="Margin" Value="-30,0"></Setter>
<Setter Property="Width" Value="100"></Setter>
</Style>
</UserControl.Resources>
<Grid>
<!--<wpfdev:StarrySky StarCount="{Binding ElementName=tbx_starCount,Path=Text}"
StarSizeMin="{Binding ElementName=tbx_starSizeMin,Path=Text}"
StarSizeMax="{Binding ElementName=tbx_starSizeMax,Path=Text}"
StarVMin="{Binding ElementName=tbx_starVMin,Path=Text}"
StarVMax="{Binding ElementName=tbx_starVMax,Path=Text}"
StarRVMin="{Binding ElementName=tbx_starRVMin,Path=Text}"
StarRVMax="{Binding ElementName=tbx_starRVMax,Path=Text}"
LineRate="{Binding ElementName=tbx_lineRate,Path=Text}"
Name="myStarrySky">
</wpfdev:StarrySky>-->
<wpfdev:StarrySky Name="myStarrySky">
</wpfdev:StarrySky>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel Orientation="Horizontal">
<TextBlock Text=" The number of stars :"></TextBlock>
<TextBox x:Name="tbx_starCount" Text="{Binding ElementName=myStarrySky,Path=StarCount}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" The minimum size :"></TextBlock>
<TextBox Name="tbx_starSizeMin" Text="{Binding ElementName=myStarrySky,Path=StarSizeMin}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" Maximum size :"></TextBlock>
<TextBox Name="tbx_starSizeMax" Text="{Binding ElementName=myStarrySky,Path=StarSizeMax}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" Minimum speed :"></TextBlock>
<TextBox Name="tbx_starVMin" Text="{Binding ElementName=myStarrySky,Path=StarVMin}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" Maximum speed :"></TextBlock>
<TextBox Name="tbx_starVMax" Text="{Binding ElementName=myStarrySky,Path=StarVMax}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" Minimum speed :"></TextBlock>
<TextBox Name="tbx_starRVMin" Text="{Binding ElementName=myStarrySky,Path=StarRVMin}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" Maximum speed :"></TextBlock>
<TextBox Name="tbx_starRVMax" Text="{Binding ElementName=myStarrySky,Path=StarRVMax}"></TextBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" Connection magnification :"></TextBlock>
<TextBox Name="tbx_lineRate" Text="{Binding ElementName=myStarrySky,Path=LineRate}"></TextBox>
</StackPanel>
<Button Name="btn_render" Content=" Generate " Click="btn_render_Click"/>
</StackPanel>
</Grid>
</UserControl>4)StarrySkyExample.xaml.cs The code is as follows ;
using System.Windows.Controls;
namespace WPFDevelopers.Samples.ExampleViews
{
/// <summary>
/// StarrySkyExample.xaml Interaction logic of
/// </summary>
public partial class StarrySkyExample : UserControl
{
public StarrySkyExample()
{
InitializeComponent();
}
private void btn_render_Click(object sender, System.Windows.RoutedEventArgs e)
{
myStarrySky.InitStar();
}
}
}Source code 1[1]Gtihub[2]Gitee[3]
Reference material
[1]
Source code : https://files.cnblogs.com/files/tsliwei/StarrySkyBasedOnCanvasYOUHUA.zip
[2]Gtihub: https://github.com/WPFDevelopersOrg/WPFDevelopers
[3]gitee: https://gitee.com/WPFDevelopersOrg/WPFDevelopers
边栏推荐
- Beifu twincat3 third-party servo motor -- Taking Huichuan is620n servo as an example
- VIM from dislike to dependence (18) -- advanced search mode
- [netding Cup 2018] fakebook1 reference
- Docker install redis
- Sword finger offer 68 - I. nearest common ancestor of binary search tree
- 76. minimum covering substring sliding window method
- WPF 实现星空效果
- Mysql 45讲学习笔记(二)SQL更新语句的执行
- 1299. replace each element with the largest element on the right
- IIR filter design basis and MATLAB design example
猜你喜欢

Wireshark数据包分析——wireshark0051.pcap

倍福TwinCAT3伺服控制常用功能块的实现

逆向crackme之ESp定律脱壳

Flutter-渲染原理&三棵树详解

倍福TwinCAT3中PLC程序变量定义和硬件IO关联

MySQL 45 lecture learning notes (II) execution of SQL update statements

3DE robot suction cup grabbing box

How to install laravel

3DE recover from design

倍福TwinCAT3第三方伺服电机——以汇川IS620N伺服为例子
随机推荐
svn高效管理怎么实现
DM达梦数据的关键字与表的字段冲突的解决办法
存算一体芯片离普及还有多远?听听从业者怎么说 | 对撞派 x 后摩智能
Wechat applet ChAT expression
128陷阱——源码分析
Kubernetes 集群日志管理
webview报错
力扣:用两个栈实现一个队列
Sword finger offer 68 - ii Nearest common ancestor of binary tree
Dart异步是怎么实现
倍福TwinCAT设置PLC的扫描周期,运行周期方法
多线程 interrupt用法
Use of shutter stream
倍福TwinCAT3第三方伺服电机——以汇川IS620N伺服为例子
Rabbmitmq publish subscribe mode < 2 >
Ads communication between Beifu twincat3 controller and controller
The difference between quick failure and safe failure
How to randomly assign 1000 to 10 numbers
试用了多款报表工具,终于找到了基于.Net 6开发的一个了
Flutter-渲染原理&三棵树详解