如何在Windows桌面上创建暴风雪?

Vil*_*lx- 11 windows animation weather

除了实用之外,如何(如果可能的话)你可以在运行Windows的桌面PC上产生"下雪"效果吗?最好只使用原始C/C++和WinAPI.

对雪的要求是:

  • 出现在显示的所有其他内容上(注意:永远在顶部的窗口可能仍然在雪上,这没关系.我知道任何应用程序都没有"绝对顶部"标志)
  • 雪花很小,可能是简单的点或几个白色像素的簇;
  • 不打扰使用计算机(单击雪花将点击发送到底层窗口);
  • 用户拖动窗户很好地播放;
  • 多显示器功能.

以下任何功能的奖励积分:

  • 雪积聚在窗口或任务栏的下边缘(如果它位于屏幕的底部);
  • 积雪也积聚在顶层窗户上.或者也许是积雪,有些积雪继续向下,积累在每个带有标题栏的窗户上;
  • 当窗户被拖动时,积累在窗户上的积雪会被"甩掉";
  • 任务栏上积累的积雪可以识别Vista/7下的扩展"开始"按钮.
  • 雪花有阴影/轮廓,所以它们在白色背景上可见;
  • 雪花具有复杂的类似雪的形状(它们仍然必须很小).
  • 单击雪花会将点击发送到底层窗口,但雪花会以一点酷动画消失;

大多数这些效果都很简单,除了雪是点击的部分,并且可以很好地拖动窗户.在我的早期,我已经实现了一个实现,它利用了你从中获得的HDC GetDesktopWindow(),这是点击,但是用户拖动窗口时遇到问题(渲染在它们上面的雪花被"拖拽").

该解决方案可能使用Vista/7 Aero功能,但当然,首选通用解决方案.有任何想法吗?

And*_*res 6

为了简洁和简洁起见,这个答案已经被修改为一组有限的要求.扩展它并使其更加健壮是微不足道的.

这个答案在Windows XP上使用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>
Run Code Online (Sandbox Code Playgroud)

在此窗口中,我们将添加定义如下的Snowflakes:

<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>
Run Code Online (Sandbox Code Playgroud)

雪花具有默认的UserControl代码隐藏,没有任何更改.

最后,Window CodeBehind

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;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我不得不使用一些表单代码来获取多屏幕内容,我必须在项目中包含对程序集的引用.

我没有对它进行过多次测试,但它确实可以在我的系统上运行,完成后雪就位于屏幕的底部.

将此引用用于点击行为.

一个比我更专注的人应该能够适应这一点,加上一些边缘检测到让雪落在别处的任务.

请注意,在此示例中永远不会清理雪花,并且在运行足够长的时间之后,您可能会注意到一些减速.

玩得开心!