2011-01-11 6 views
11

Dejando de lado los usos prácticos, ¿cómo (si es posible) podría crear un efecto de "nevada" en su PC de escritorio con Windows? Preferiblemente, con nada más que C/C++ en bruto y WinAPI.¿Cómo crear una tormenta de nieve en su escritorio de Windows?

Los requisitos para la nieve son:

  • Aparece sobre todo lo demás se muestra (Nota: siempre-on-top ventanas pueden llegar a la cima de la nieve aún, eso está bien entiendo que no puede haber ". absoluto en la parte superior "bandera para cualquier aplicación)
  • Los copos de nieve son pequeños, posiblemente puntos simples o grupos de unos pocos píxeles blancos;
  • No molesta trabajar con la computadora (al hacer clic en un copo de nieve se envía el clic a la ventana subyacente);
  • Se juega muy bien con los usuarios que arrastran ventanas;
  • Compatible con múltiples monitores.

puntos de bonificación para cualquiera de las siguientes características:

  • nieve se acumula en el borde inferior de la ventana o la barra de tareas (si es en la parte inferior de la pantalla);
  • La nieve se acumula también en las ventanas de nivel superior. O quizás se acumula algo de nieve, algunos continúan hacia abajo, acumulándose en cada ventana con una barra de título;
  • La nieve acumulada en Windows se "agita" cuando se arrastran las ventanas;
  • Nieve acumulada en la barra de tareas es consciente del botón extendido "Inicio" en Vista/7.
  • Los copos de nieve tienen sombras/contornos, por lo que son visibles en fondos blancos;
  • Los copos de nieve tienen formas complejas semejantes a las de las nieves (aún deben ser pequeñas).
  • Al hacer clic en un copo de nieve se envía el clic a la ventana subyacente, pero el copo de nieve se evapora con una pequeña animación genial;

La mayoría de estos efectos son lo suficientemente directos, excepto en la parte en que la nieve es un clic y funciona bien con el arrastre de ventanas. En mis inicios hice una implementación que se basa en el HDC que obtienes de GetDesktopWindow(), que era click-through, pero tenía problemas con los usuarios al arrastrar ventanas (los copos de nieve renderizados en ellos se "arrastraban").

La solución puede utilizar Vista/7 características Aero, pero, por supuesto, se prefiere una solución universal. ¿Algunas ideas?

+10

Puedo satisfacer todas las 5 características requeridas al dejar mi computadora en los escalones frente a mi casa en Nueva York esta tarde. –

+0

@Larry Lustig - punto tomado, sin embargo, esperaba una solución de software más, con la publicación en SO y todo ... –

+0

Para el detractor (votante cercano) - ¿qué no entiendes? ¿Debo aclarar más? –

Respuesta

6

En aras de la brevedad y la simplicidad, esta respuesta se ha reducido a un conjunto limitado de requisitos. Es trivial expandir esto y hacerlo más robusto.

Esta respuesta usa WPF en Windows XP. Debería funcionar en hasta 2 monitores, y debería funcionar en otros sistemas de Windows también.

Se inicia con una simple ventana:

<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> 

Para esta ventana, vamos a añadir los copos de nieve definidos de la siguiente manera:

<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> 

Los copos de nieve tienen el control de usuario por defecto de código subyacente sin cambios.

Por último, la ventana 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; 
       } 
      } 
     } 
    } 
} 

tuve que usar un poco de código formas para conseguir las cosas multipantalla, y he tenido que incluir las referencias a las Asambleas en mi proyecto.

No lo he probado mucho, pero funciona en mi sistema, y ​​la nieve se queda en la parte inferior de la pantalla cuando termina.

He usado this referencia para el comportamiento de clic.

Un compañero más dedicado de lo que debería ser capaz de adaptar this, además de un poco de detección de bordes a la tarea de hacer que la nieve se siente en otro lugar.

Tenga en cuenta que los copos de nieve nunca se limpian en este ejemplo, y después de ejecutarlo el tiempo suficiente, puede notar cierta desaceleración.

Have Fun!

+0

Veo que su truco también es WS_EX_TRANSPARENT (también conocido como Windows en capas). Gracias por confirmar que esto funciona! :) –

+0

muy buena respuesta, +1 –

+0

¿Alguna idea de cómo hacerlo en Java? – Alpine

Cuestiones relacionadas