・M.S.社のMystifyスクリーンセーバのSkiaSharp WPF版。
・だんだん崩れるので展開後、指定秒後に再描画してます。
1.キー操作説明
-
[F]...................フルスクリーン表示。(起動直後にFキー)
-
[PageUp].....間隔増 一辺が10の線で構成されているがその線同士の間隔可変
-
[PageDw].....間隔減
-
[マウス左]....再描画(リセット)
-
[マウス右]....終了
2.動画 YouTube
3.コードについて
・非同期で表示本数を可変させてます。
( _=Task.RUN() MainPaintSurface()内の表示For値)
・意外と簡単に出来ました。他にも色んな手法があるので作ってみて下さい。
4.WPF Sourcecode
/////////////////////////////////////////////////////////////////////////////////////
// Mystify LineDemo WPF+SkiaSharp (skiasharp from nuget install)
// MOUSE_RIGHT...Exit,LEFT...RESTART / [PageUp,Down]...Width CHG / [F]...FullSCREEN.
// (c)inf102 S.H. 2024.
/////////////////////////////////////////////////////////////////////////////////////
using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System;
using System.Threading.Tasks;
using System.Windows;
namespace SkiaSharpSample {
public partial class MainWindow : Window {
///////////////////////////////////////////////////////////////////
const int WAIT=15; // 画面更新間隔(mmSec) 2以上
static int L_WIDTH=1; // 線と線の幅 PgUP/DOWN で可変
const int EXT=6; // 線の伸び 一回の更新で伸ばすドット数 少ないと滑らかに増えるが遅くなる
const int THICK=1; // 線の太さ 2以上は重くなる
const int MAINTAIN = 10000; // 10本引かれた後、その状態を維持する時間(mmSec)
const int INC_DEC=140; // 1~10本の線を引く間隔(mmSec)
///////////////////////////////////////////////////////////////////
const int MAX_LINE=40;
static int LD1=0;
static int LD2=10;
static int LD3=20;
static int LDFLG=0;
// SKIA_LINEDATA
static SKPaint A_LINE= new SKPaint{
Style = SKPaintStyle.Stroke,
IsAntialias=true,
StrokeWidth = THICK
};
static bool FULL_SCREEN=false;
// キャンパスサイズ
static int CAX,CAY;
// 表示データ クラス
DSet[] Line_data= new DSet[2];
public MainWindow() {
InitializeComponent();
// 四角
Line_data[0]=new DSet();
// 三角
Line_data[1]=new DSet();
}
// 表示データ格納クラス
public class DSet {
public struct POINT_XY{
public int START_X,START_Y; // 開始位置
public int L; // 開始位置からの半径
public int RAG; // 開始位置からの角度
public int CUR_X,CUR_Y; // 現在位置
}
public POINT_XY[] XYDATA= new POINT_XY[MAX_LINE];
public void Dx(){
System.Threading.Thread.Sleep(1);
Random rnd = new Random(DateTime.Now.Millisecond);
int XY,RA;
///////////////////////////////////
RA=rnd.Next(0,360);
if (CAY<CAX ) XY=rnd.Next(CAY/2,CAX/2);
else XY=rnd.Next(CAX/2,CAY/2);
for (int d=0;d<10;d++){
XYDATA[d].L=1;
XYDATA[d].RAG = RA++;
XYDATA[d].START_X = XY-25;
XYDATA[d].START_Y = XY-110;
}
///////////////////////////////////
RA=rnd.Next(0,360);
if (CAY<CAX ) XY=rnd.Next(CAY/2,CAX/2);
else XY=rnd.Next(CAX/2,CAY/2);
for (int d=10;d<20;d++){
XYDATA[d].L=1;
XYDATA[d].RAG = RA++;
XYDATA[d].START_X = XY-25;
XYDATA[d].START_Y = XY-110;
}
//////////////////////////////////
RA=rnd.Next(0,360);
if (CAY<CAX ) XY=rnd.Next(CAY/2,CAX/2);
else XY=rnd.Next(CAX/2,CAY/2);
for (int d=20;d<30;d++){
XYDATA[d].L=1;
XYDATA[d].RAG = RA++;
XYDATA[d].START_X = XY-25;
XYDATA[d].START_Y = XY-110;
}
////////////////////////////////
RA=rnd.Next(0,360);
if (CAY<CAX ) XY=rnd.Next(CAY/2,CAX/2);
else XY=rnd.Next(CAX/2,CAY/2);
for (int d=30;d<40;d++){
XYDATA[d].L=1;
XYDATA[d].RAG = RA++;
XYDATA[d].START_X = XY-25;
XYDATA[d].START_Y = XY-110;
}
////////////////////////////////
}
}
private async void Window_Loaded(object sender, RoutedEventArgs e) {
_=Task.Run ( ()=> {
while (true){
System.Threading.Thread.Sleep (INC_DEC);
if (LDFLG == 0){
LD1++;
LD2++;
LD3++;
}
if (LDFLG == 2) {
LD1--;
LD2--;
LD3--;
}
// 0本になった
if(LD1<=0 && LDFLG == 2) {
LD1=0;
LD2=10;
LD3=20;
LDFLG=0;
Line_data[0].Dx();
Line_data[1].Dx();
}
// 10本引かれた
if(LD1>=10 && LDFLG == 0) {
System.Threading.Thread.Sleep (MAINTAIN);
LD1=10;
LD2=20;
LD3=30;
LDFLG=2;
}
}
});
// MAIN_LOOP
while(true){
await Task.Delay (WAIT);
// 表示データ更新、衝突判定
Col();
// 描画
MainCanvas.InvalidateVisual();
}
}
// 衝突判定
void Col(){
// 次の表示位置算出
for (int x=0;x<MAX_LINE;x++){
for (int m=0;m<2;m++){
Line_data[m].XYDATA[x].CUR_X= (int)(Line_data[m].XYDATA[x].START_X+ Line_data[m].XYDATA[x].L*Math.Sin(Line_data[m].XYDATA[x].RAG*Math.PI/180));
Line_data[m].XYDATA[x].CUR_Y= (int)(Line_data[m].XYDATA[x].START_Y- Line_data[m].XYDATA[x].L*Math.Cos(Line_data[m].XYDATA[x].RAG*Math.PI/180));
}
}
// 壁衝突判定 衝突の場合は新たな反射角等を設定
for (int x=0;x<MAX_LINE;x++){
for (int m=0;m<2;m++){
// 上 up
if (Line_data[m].XYDATA[x].CUR_Y <= 0){
Line_data[m].XYDATA[x].L=1;
Line_data[m].XYDATA[x].RAG=180-Line_data[m].XYDATA[x].RAG;
Line_data[m].XYDATA[x].START_Y=Line_data[m].XYDATA[x].CUR_Y;
Line_data[m].XYDATA[x].START_X=Line_data[m].XYDATA[x].CUR_X;
}
// 下 down
if (Line_data[m].XYDATA[x].CUR_Y >= CAY){
Line_data[m].XYDATA[x].L=1;
Line_data[m].XYDATA[x].RAG=180-Line_data[m].XYDATA[x].RAG;
Line_data[m].XYDATA[x].START_Y=Line_data[m].XYDATA[x].CUR_Y;
Line_data[m].XYDATA[x].START_X=Line_data[m].XYDATA[x].CUR_X;
}
// 右 right
if (Line_data[m].XYDATA[x].CUR_X >= CAX){
Line_data[m].XYDATA[x].L=1;
Line_data[m].XYDATA[x].RAG=360-Line_data[m].XYDATA[x].RAG;
Line_data[m].XYDATA[x].START_Y=Line_data[m].XYDATA[x].CUR_Y;
Line_data[m].XYDATA[x].START_X=Line_data[m].XYDATA[x].CUR_X;
}
// 左 left
if (Line_data[m].XYDATA[x].CUR_X <= 0){
Line_data[m].XYDATA[x].L=1;
Line_data[m].XYDATA[x].RAG=360-Line_data[m].XYDATA[x].RAG;
Line_data[m].XYDATA[x].START_Y=Line_data[m].XYDATA[x].CUR_Y;
Line_data[m].XYDATA[x].START_X=Line_data[m].XYDATA[x].CUR_X;
}
// 線を伸ばす
Line_data[m].XYDATA[x].L+=EXT;
}
}
}
// WindowSize CHG
private void skiaCanvas_SizeChanged(object sender, SizeChangedEventArgs e) {
Line_data[0].Dx();
Line_data[1].Dx();
}
// MOUSE L-BUTTON. RESET
private void MainCanvas_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
Line_data[0].Dx();
Line_data[1].Dx();
}
// MOUSE R-BUTTON. EXIT
private void MainCanvas_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {
Application.Current.Shutdown();
}
private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) {
// FULL SCREEN
if (e.Key == System.Windows.Input.Key.F) {
if (FULL_SCREEN == false){
FULL_SCREEN=!FULL_SCREEN;
this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
Line_data[0].Dx();
Line_data[1].Dx();
return;
}
else {
FULL_SCREEN=!FULL_SCREEN;
this.WindowStyle = WindowStyle.SingleBorderWindow;
this.WindowState = WindowState.Normal;
this.Height=600;
this.Width=1000;
Line_data[0].Dx();
Line_data[1].Dx();
}
}
if (e.Key == System.Windows.Input.Key.PageUp) {
L_WIDTH++;
Line_data[0].Dx();
Line_data[1].Dx();
}
if (e.Key == System.Windows.Input.Key.PageDown) {
L_WIDTH--;
if (L_WIDTH <=0 ) L_WIDTH=1;
Line_data[0].Dx();
Line_data[1].Dx();
}
}
// 描画
void MainPaintSurface(object sender, SKPaintSurfaceEventArgs args){
CAX=args.Info.Width;
CAY=args.Info.Height;
Random rnd = new System.Random();
args.Surface.Canvas.Clear(SKColors.Black);
for (int x=0;x<MAX_LINE;x++) {
// 四角
A_LINE.Color=new SKColor((byte)rnd.Next(180,255),(byte)rnd.Next(100,200),(byte)rnd.Next(100,200));
for(int xx = 0; xx < LD1;xx++)
args.Surface.Canvas.DrawLine(Line_data[0].XYDATA[xx].CUR_X, Line_data[0].XYDATA[xx].CUR_Y, Line_data[0].XYDATA[xx+10].CUR_X, Line_data[0].XYDATA[xx+10].CUR_Y, A_LINE);
A_LINE.Color=new SKColor((byte)rnd.Next(100,200),(byte)rnd.Next(180,255),(byte)rnd.Next(100,200));
for(int xx = 10; xx < LD2; xx++)
args.Surface.Canvas.DrawLine(Line_data[0].XYDATA[xx].CUR_X, Line_data[0].XYDATA[xx].CUR_Y, Line_data[0].XYDATA[xx+10].CUR_X, Line_data[0].XYDATA[xx+10].CUR_Y, A_LINE);
A_LINE.Color=new SKColor((byte)rnd.Next(100,200),(byte)rnd.Next(100,200),(byte)rnd.Next(180,255));
for(int xx = 0; xx < LD1; xx++)
args.Surface.Canvas.DrawLine(Line_data[0].XYDATA[xx].CUR_X, Line_data[0].XYDATA[xx].CUR_Y, Line_data[0].XYDATA[xx+30].CUR_X, Line_data[0].XYDATA[xx+30].CUR_Y, A_LINE);
A_LINE.Color=new SKColor((byte)rnd.Next(50,255),(byte)rnd.Next(50,255),(byte)rnd.Next(50,255));
for(int xx = 20; xx < LD3; xx++)
args.Surface.Canvas.DrawLine(Line_data[0].XYDATA[xx].CUR_X, Line_data[0].XYDATA[xx].CUR_Y, Line_data[0].XYDATA[xx+10].CUR_X, Line_data[0].XYDATA[xx+10].CUR_Y, A_LINE);
// 三角
A_LINE.Color=new SKColor((byte)rnd.Next(00,255),(byte)rnd.Next(00,255),(byte)rnd.Next(0,255));
for(int xx = 0; xx < LD1; xx++)
args.Surface.Canvas.DrawLine(Line_data[1].XYDATA[xx].CUR_X, Line_data[1].XYDATA[xx].CUR_Y,Line_data[1].XYDATA[xx+10].CUR_X, Line_data[1].XYDATA[xx+10].CUR_Y, A_LINE);
A_LINE.Color=new SKColor((byte)rnd.Next(00,255),(byte)rnd.Next(0,255),(byte)rnd.Next(00,255));
for(int xx = 10; xx < LD2; xx++)
args.Surface.Canvas.DrawLine(Line_data[1].XYDATA[xx].CUR_X, Line_data[1].XYDATA[xx].CUR_Y,Line_data[1].XYDATA[xx+10].CUR_X, Line_data[1].XYDATA[xx+10].CUR_Y, A_LINE);
A_LINE.Color=new SKColor((byte)rnd.Next(00,255),(byte)rnd.Next(00,255),(byte)rnd.Next(0,255));
for(int xx = 0; xx < LD1; xx++)
args.Surface.Canvas.DrawLine(Line_data[1].XYDATA[xx].CUR_X, Line_data[1].XYDATA[xx].CUR_Y,Line_data[1].XYDATA[xx+20].CUR_X, Line_data[1].XYDATA[xx+20].CUR_Y, A_LINE);
}
}
}
}
5.WPF XAML
<Window x:Class="SkiaSharpSample.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SkiaSharpSample" xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF"
xmlns:wpf="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" mc:Ignorable="d"
Title="LineDemo WPF+SkiaSharp. PageUp,Down=SpaceChenage , MOUSE-LEFT=Resart , RIGHT=Shitdown ,F=FullScreen" Loaded="Window_Loaded" Height="800" Width="1200" KeyDown="Window_KeyDown" >
<Grid >
<wpf:SKElement x:Name="MainCanvas" PaintSurface="MainPaintSurface" Grid.Column="0" Grid.Row="0" SizeChanged="skiaCanvas_SizeChanged" PreviewMouseLeftButtonDown="MainCanvas_PreviewMouseLeftButtonDown" PreviewMouseRightButtonDown="MainCanvas_PreviewMouseRightButtonDown" />
</Grid>
</Window>