C#实现黑客帝国字幕特效

看过电影《黑客帝国》得大概都会觉得里面那种黑底绿字的效果特别炫,闲来无事,就自己用C#写了个特效控件,并可以通过调整参数达到其他效果。
先看看《黑客帝国》经典的特效好了(图片经过压缩,可能不是很清楚):
           
再看看项目实现后的效果(参数设置的好的话,效果还是相当不错的):

       


实现方案:

实现并不是特别复杂,每条显示的字符串都作为一个WordInfo对象,该对象包含了速度、颜色、字体大小、字符个数等属性。在控件类WordRain中在该项目中,定义了两个Timer,一个是m_timer用来定时重绘控件,一个是m_newtimer用来定时创建新的WordInfo对象。在WordRain中还维持了两个ArrayList,一个是m_selectlist用来显示要绘制的字符(每次重绘控件的时候都会随机获取m_charlist中的一个字符用于显示),一个是m_charlist用来保存当前显示在控件中的WordInfo对象(超出控件范围的对象要及时删除)。


注意点和相关处理如下:

1. 字体较大字符串的必需在字体小的字符串之后画,避免字体小的字符串遮住字体大的字符串,影响效果。
    要达到这样的效果必须在每次重绘的时候,对m_charlist中的所有WordInfo对象按字体大小从小到大排序。实现如下:

    public class WordInfoSorter : IComparer
    {
        int IComparer.Compare(object x, object y)
        {
            if (((WordInfo)x).m_fontsize < ((WordInfo)y).m_fontsize) return -1;
            else if (((WordInfo)x).m_fontsize == ((WordInfo)y).m_fontsize) return 0;
            else return 1;
        }
    }

    public partial class WordRain : UserControl
    {
        //...
        private WordInfoSorter m_sorter;
        //...
        public WordRain()
        {
            //...
            m_sorter = new WordInfoSorter();
            //...
        }
        //...
        private void WordRain_Paint(object sender, PaintEventArgs e)
        {
            //...
            m_charlist.Sort(m_sorter);
            //...
        }
        //...
    }

2. 为了保证效果,字体较大的字符串颜色要接近前景色m_min_charcolor,字体较小的字符串颜色要更接近背景色m_max_charcolor。颜色根据字体的最大值和最小值来计算)。详见类WordInfo的SetFontAndColor()方法。

3. 同样为了保证效果,每个字符串有 m_count个字符,显示在最下面的字符颜色100%不透明,显示在最上面的字符颜色最接近透明。详见类WordInfo的GetColor()方法。


整个控件的代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Runtime.InteropServices;

namespace myControl
{
    public partial class WordRain : UserControl
    {
        private Timer m_timer;  //重绘定时器
        private Timer m_newtimer;   //新建字符定时器
        private Graphics m_g;     //画布
        private ArrayList m_charlist;   
        private ArrayList m_selectlist;
        private WordInfoSorter m_sorter;
        private Random m_random;

        public WordRain()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.Selectable, true);
            this.BackColor = Color.Black;
            
            m_charlist = new ArrayList();
            WordInfo wordinfo = new WordInfo();
            wordinfo.SetValue();
            m_charlist.Add(wordinfo);
            m_sorter = new WordInfoSorter();
            string[] arr = new string[] {"0","1","2","3","4","5","6","7","8","9"};
            m_selectlist = new ArrayList(arr);
            m_random = new Random();
            m_timer = new Timer();
            m_newtimer = new Timer();
            m_timer.Interval = 10;
            m_newtimer.Interval = 50;
            m_timer.Enabled = true;
            m_newtimer.Enabled = true;
            m_timer.Tick += new EventHandler(m_timer_Tick);
            m_newtimer.Tick += new EventHandler(m_newtimer_Tick);         
        }

        private string GetStringFromArrayList()
        {
            return (string)m_selectlist[m_random.Next(m_selectlist.Count)];
        }

        void m_newtimer_Tick(object sender, EventArgs e)
        {
            WordInfo wordinfo = new WordInfo();
            wordinfo.SetValue();
            m_charlist.Add(wordinfo);
        }

        void m_timer_Tick(object sender, EventArgs e)
        {
            this.Invalidate();
            ArrayList ObjToDel = new ArrayList(); 
            foreach (WordInfo temp in m_charlist)
            {
                temp.m_posy += temp.m_runspeed;
                if (temp.m_posy > this.Height + temp.m_fontsize * temp.m_count)
                    ObjToDel.Add(temp);
            }
            foreach (WordInfo temp in ObjToDel)
            {
                m_charlist.Remove(temp);
            }
            ObjToDel.Clear();
        }

        private void WordRain_Paint(object sender, PaintEventArgs e)
        {
            m_g = e.Graphics;
            m_charlist.Sort(m_sorter);
            SetXRange(this.Width);                   
            foreach (WordInfo temp in m_charlist)
            {
                using (Font font = new Font("Arial", temp.m_fontsize, GraphicsUnit.Pixel))
                {
                    using(SolidBrush brush = new SolidBrush(temp.m_charcolor))                
                    {
                        for (int i = 0; i < temp.m_count; i++)
                        {
                            brush.Color = temp.GetColor(i);
                            m_g.DrawString(GetStringFromArrayList(), font, brush, temp.m_posx, temp.m_posy-temp.m_fontsize*i);  
                        }
                    }
                }
            }

        }

        //设置字体大小范围
        public void SetFontSizeRange(int minsize, int maxsize)
        {
            if (minsize > maxsize)
            {
                throw new ArgumentException("参数minsize必须小于等于参数maxsize");
            }
            if (minsize < 1 || maxsize > 50)
            {
                throw new ArgumentException("参数minsize和maxsize必须在区间[1,50]内");
            }
            WordInfo.m_min_fontsize = minsize;
            WordInfo.m_max_fontsize = maxsize;
        }
        //设置字体渐变颜色范围
        public void SetFontColorRange(Color firstColor, Color lastColor)
        {
            WordInfo.m_min_charcolor = firstColor;
            WordInfo.m_max_charcolor = lastColor;
        }
        //设置X轴所能到达的最大长度(即控件的宽度)
        public void SetXRange(int value)
        {
            WordInfo.m_max_xpos = value;
        }
        //设置速度
        public void SetSpeedRange(float minspeed, float maxspeed)
        {
            if (minspeed > maxspeed)
            {
                throw new ArgumentException("参数minspeed必须小于等于参数maxspeed");
            }
            if (minspeed < 0.1 || maxspeed > 100.0)
            {
                throw new ArgumentException("参数minspeed和maxspeed必须在区间[0.1,100.0]内");
            }
            WordInfo.m_min_runspeed = minspeed;
            WordInfo.m_min_runspeed = maxspeed;
        }
        //设置字符个数范围
        public void SetCountRange(int mincount, int maxcount)
        {
            if (mincount > maxcount)
            {
                throw new ArgumentException("参数mincount必须小于等于参数maxcount");
            }
            if (mincount < 1 || maxcount > 100)
            {
                throw new ArgumentException("参数mincount和maxcount必须在区间[1,100]内");
            }
            WordInfo.m_min_count = mincount;
            WordInfo.m_max_count = maxcount;
        }
        //设置字符列表
        public void SetArrayValue(ArrayList list)
        {
            if (list.Count == 0)
            {
                throw new ArgumentException("参数list中的元素个数不能为0");
            }
            m_selectlist.Clear();
            m_selectlist = list;
        }
    }


    public class WordInfoSorter : IComparer
    {
        int IComparer.Compare(object x, object y)
        {
            if (((WordInfo)x).m_fontsize < ((WordInfo)y).m_fontsize) return -1;
            else if (((WordInfo)x).m_fontsize == ((WordInfo)y).m_fontsize) return 0;
            else return 1;
        }
    }

    public class WordInfo
    {
        public int m_fontsize;      //字体大小
        public float m_runspeed;    //移动速度
        public Color m_charcolor;   //最下面的字的颜色,最上面的字的颜色为背景色
        public float m_posx;        //横坐标
        public float m_posy;        //纵坐标
        public int m_count;         //字符个数
        public ArrayList m_charList;  //要显示的字符列表

        public static int m_max_xpos = 600;
        public static int m_min_count = 10;
        public static int m_max_count = 20;
        public static int m_min_fontsize = 4;
        public static int m_max_fontsize = 20;
        public static float m_min_runspeed = 0.6F;
        public static float m_max_runspeed = 4.2F;
        public static Color m_min_charcolor = Color.FromArgb(0, 255, 0);
        public static Color m_max_charcolor = Color.FromArgb(0, 55, 0);
        public static Random m_random = new Random();

        public WordInfo()
        {
            this.m_count = 1;
            this.m_fontsize = 0;
            this.m_runspeed = 0;
            this.m_posx = 0;
            this.m_posy = 0;
            this.m_charcolor = Color.FromArgb(0, 255, 0);
        }


        //获取字体大小的随机值
        private int GetFontSize()
        {
            int i = m_random.Next();
            return i % (m_max_fontsize - m_min_fontsize) + m_min_fontsize;
        }

        //获取速度的随机值
        private float GetSpeed()
        {
            int i = m_random.Next();
            int j = m_random.Next();
            return i % (m_max_runspeed - m_min_runspeed) + m_min_runspeed+ (j%10)/10.0F;
        }

        //获取颜色的随机值
        private Color GetColor()
        {
            int min_r = m_min_charcolor.R;
            int min_g = m_min_charcolor.G;
            int min_b = m_min_charcolor.B;
            int max_r = m_max_charcolor.R;
            int max_g = m_max_charcolor.G;
            int max_b = m_max_charcolor.B;
            int r = m_random.Next((min_r > max_r ? max_r : min_r), (min_r < max_r ? max_r : min_r));
            int g = m_random.Next((min_g > max_g ? max_g : min_g), (min_g < max_g ? max_g : min_g));
            int b = m_random.Next((min_b > max_b ? max_b : min_b), (min_b < max_b ? max_b : min_b));
            return Color.FromArgb(r, g, b);
        }

        //设置字体和颜色(字体越小,颜色越接近m_max_charcolor)
        private void SetFontAndColor()
        {
            int i = m_random.Next();
            int fontpart = i % (m_max_fontsize - m_min_fontsize);
            this.m_fontsize = m_min_fontsize + fontpart;
            float percent = ((float)fontpart) / ((float)(m_max_fontsize - m_min_fontsize));
            int min_r = m_min_charcolor.R;
            int min_g = m_min_charcolor.G;
            int min_b = m_min_charcolor.B;
            int max_r = m_max_charcolor.R;
            int max_g = m_max_charcolor.G;
            int max_b = m_max_charcolor.B;
            int rpart = (int)(Math.Abs(min_r - max_r) * percent);
            int gpart = (int)(Math.Abs(min_g - max_g) * percent);
            int bpart = (int)(Math.Abs(min_b - max_b) * percent);
            int r = min_r > max_r ? (max_r + rpart) : (max_r - rpart);
            int g = min_g > max_g ? (max_g + gpart) : (max_g - gpart);
            int b = min_b > max_b ? (max_b + bpart) : (max_b - bpart);
            this.m_charcolor = Color.FromArgb(r, g, b);
        }

        //获取x轴坐标
        private float GetXPos()
        {
            int i = m_random.Next(m_max_xpos-1);
            return (float)(i + m_random.Next(10) / 10.0F);

        }

        //获取随机个数
        private int GetCount()
        {
            return m_random.Next(m_min_count, m_max_count);
        }

        //设置相关值
        public void SetValue()
        {
            this.m_count = this.GetCount();
            this.SetFontAndColor();
            this.m_runspeed = this.GetSpeed();
            this.m_posy = m_count*m_fontsize*(-1.0F);
            this.m_posx = this.GetXPos();
        }

        //获取颜色
        public Color GetColor(int index)
        {
            if (this.m_count == 1)
                return this.m_charcolor;
            if (index == 0)
                return this.m_charcolor;
            int rpart = (Math.Abs(m_charcolor.R - m_max_charcolor.R)) / m_count * index;
            int gpart = (Math.Abs(m_charcolor.G - m_max_charcolor.G)) / m_count * index;
            int bpart = (Math.Abs(m_charcolor.B - m_max_charcolor.B)) / m_count * index;
            int r = m_charcolor.R > m_max_charcolor.R ? (m_charcolor.R - rpart) : (m_charcolor.R + rpart);
            int g = m_charcolor.G > m_max_charcolor.G ? (m_charcolor.G - gpart) : (m_charcolor.G + gpart);
            int b = m_charcolor.B > m_max_charcolor.B ? (m_charcolor.B - bpart) : (m_charcolor.B + bpart);
            return Color.FromArgb(255-index*255/m_count, m_charcolor);
        }
    }
}

项目地址如下:
     http://yun.baidu.com/share/link?shareid=1966732572&uk=3508115909
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值