11

実用的な用途はさておき、(可能であれば)Windowsを実行しているデスクトップPCに「雪」の効果をどのように作成できますか?できれば、生のC /C++とWinAPIだけを使用してください。

雪の要件は次のとおりです。

  • 表示されている他のすべての上に表示されます(注:常に上部にあるウィンドウはまだ雪の上にある可能性があります。それで問題ありません。どのアプリにも「絶対上部」フラグがないことを理解しています)
  • 雪片は小さく、おそらく単純な点またはいくつかの白いピクセルのクラスターです。
  • コンピューターでの作業に煩わされることはありません(スノーフレークをクリックすると、クリックスルーが下のウィンドウに送信されます)。
  • ユーザーがウィンドウをドラッグするとうまく機能します。
  • マルチモニター対応。

次の機能のいずれかのボーナスポイント:

  • ウィンドウまたはタスクバーの下端(画面の下部にある場合)に雪が積もります。
  • トップレベルの窓にも雪が積もります。あるいは雪が積もったり、雪が降り続けたりして、タイトルバーのあるすべてのウィンドウに積もります。
  • 窓に積もった雪は、窓をドラッグすると「振り落とされ」ます。
  • タスクバーに積もった雪は、Vista/7の下にある拡張された[スタート]ボタンを認識しています。
  • 雪片には影/輪郭があるため、白い背景に表示されます。
  • 雪片は複雑な雪のような形をしています(まだ小さい必要があります)。
  • スノーフレークをクリックすると、下にあるウィンドウにクリックスルーが送信されますが、スノーフレークは少しクールなアニメーションで蒸発します。

これらの効果のほとんどは、雪がクリックスルーし、ウィンドウのドラッグでうまく機能する部分を除いて、十分に単純です。私の初期の頃は、GetDesktopWindow()クリックスルーであるHDCを利用する実装を作成しましたが、ユーザーがウィンドウをドラッグする際に問題が発生しました(ウィンドウにレンダリングされた雪片が「ドラッグ」されました)。

このソリューションはVista/7 Aero機能を使用する場合がありますが、もちろん、ユニバーサルソリューションが推奨されます。何か案は?

4

1 に答える 1

6

簡潔さと単純さのために、この回答は限られた一連の要件に合わせて調整されています。これを拡張してより堅牢にするのは簡単です。

この回答では、WindowsXPでWPFを使用しています。最大2台のモニターで動作し、他のWindowsシステムでも動作するはずです。

それは単純なウィンドウから始まります:

<Window x:Class="TestDump.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" WindowStartupLocation="Manual" Loaded="Window_Loaded"
WindowStyle="None" AllowsTransparency="True" Background="Transparent"
>
    <Grid x:Name="FieldOfSnow"/>
</Window>

このウィンドウに、次のように定義されたスノーフレークを追加します。

<UserControl x:Class="TestDump.SnowFlake"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="5" Width="5">
    <Border Background="White" CornerRadius="2" BorderThickness="1" BorderBrush="LightGray"/>
</UserControl>

雪片にはデフォルトのUserControlコードがあり、変更はありません。

最後に、ウィンドウコードビハインド

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace TestDump
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        private TimeSpan _lastRender;

        public Window1()
        {
            InitializeComponent();
            _lastRender = TimeSpan.FromTicks(0);
            CompositionTarget.Rendering += SnowflakeTick;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.Topmost = true;
            this.Top = 0;
            this.Left = 0;

            this.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
            this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;

            if (System.Windows.Forms.SystemInformation.MonitorCount == 2)
            {
                System.Drawing.Rectangle SecondScreenArea = System.Windows.Forms.Screen.AllScreens[1].Bounds;

                this.Width += SecondScreenArea.Width;
                this.Height = this.Height > SecondScreenArea.Height ? this.Height : SecondScreenArea.Height;
            }
        }

        public const int WS_EX_TRANSPARENT = 0x00000020;
        public const int GWL_EXSTYLE = (-20);

        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr hwnd, int index);

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            // Get this window's handle
            IntPtr hwnd = new WindowInteropHelper(this).Handle;

            // Change the extended window style to include WS_EX_TRANSPARENT
            int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
            SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
        }

        List<TranslateTransform> Flakes = new List<TranslateTransform>();
        Random rand = new Random();

        private void SnowflakeTick(object sender, EventArgs e)
        {
            RenderingEventArgs renderingArgs = (RenderingEventArgs)e;
            TimeSpan dTime = (renderingArgs.RenderingTime - _lastRender);
            double deltaTime = dTime.TotalMilliseconds;
            _lastRender = renderingArgs.RenderingTime;

            if ( _lastRender.Milliseconds < deltaTime)
            {
                TranslateTransform SnowPos = new TranslateTransform(this.Width * rand.Next(1000) / 1000.0 - this.Width/2, -this.Height/2);

                SnowFlake sf = new SnowFlake();
                sf.RenderTransform = SnowPos;

                // The flakes are centered when added, so all positions must be translated with this in mind.
                FieldOfSnow.Children.Add(sf);
                Flakes.Add(SnowPos);
            }

            foreach (TranslateTransform Flake in Flakes)
            {
                double ScreenHeight = this.Height / 2 - 2;

                if (Flake.Y < ScreenHeight)
                {
                    Flake.Y += deltaTime / 50;
                }
            }
        }
    }
}

マルチスクリーンのものを取得するには、フォームコードを少し使用する必要があり、プロジェクトにアセンブリへの参照を含める必要がありました。

私はそれをあまりテストしていませんが、それは私のシステムで動作し、雪は画面の下部にあります。

クリックスルー動作にこのリファレンスを使用しました。

私がこれを適応させることができるはずのよりも熱心な仲間に加えて、雪を他の場所に座らせるタスクにエッジ検出を加えます。

この例では雪片がクリーンアップされることはなく、十分に長く実行した後、速度が低下する場合があることに注意してください。

楽しんで!

于 2011-01-11T20:19:48.543 に答える