C# Winform 水波纹效果

//添加自定义控件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

namespace My.UControl
{
    public partial class WaveControl : Panel// Control
    {
        public WaveControl()
        {
            InitializeComponent();
            this.Effect.Tick += new EventHandler(Effect_Tick);
            this.Paint += new PaintEventHandler(WavesControl_Paint);
            this.MouseMove += new MouseEventHandler(WavesControl_MouseMove);

            this.Effect.Enabled = true;
            this.Effect.Interval = 50;
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            this.BackColor = Color.White;
        }
        public WaveControl(Bitmap bmp)
            : this()
        {
            this.bmpImage = bmp;
        }
        #region Fields or Properties
        private int scale = 1;
        /// <summary>
        /// The scale of the wave matrix compared to the size of the image.
        /// Use it for large images to reduce processor load.
        ///
        /// 0 : wave resolution is the same than image resolution
        /// 1 : wave resolution is half the image resolution
        /// ...and so on
        /// </summary>
        public int Scale
        {
            get { return scale; }
            set { scale = value; }
        }
        private Bitmap bmpImage;
        /// <summary>
        /// Background image
        /// </summary>
        public Bitmap BmpImage
        {
            get { return bmpImage; }
            set
            {
                if (value == null)
                    return;
                bmpImage = value;
                bmpHeight = bmpImage.Height;
                bmpWidth = bmpImage.Width;
                waveHeight = bmpHeight >> scale;
                waveWidth = bmpWidth >> scale;

                waves = new short[waveWidth, waveHeight, 2];

                bmpBytes = new byte[bmpWidth * bmpHeight * 4];

                bmpBitmapData = bmpImage.LockBits(new Rectangle(0, 0, bmpWidth, bmpHeight),
                    System.Drawing.Imaging.ImageLockMode.ReadWrite,
                    System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                Marshal.Copy(bmpBitmapData.Scan0, bmpBytes, 0, bmpWidth * bmpHeight * 4);
            }
        }

        private int bmpHeight { get; set; }
        private int bmpWidth { get; set; }

        private int waveWidth { get; set; }
        private int waveHeight { get; set; }

        private short[, ,] waves { get; set; }
        private byte[] bmpBytes { get; set; }

        private System.Drawing.Imaging.BitmapData bmpBitmapData { get; set; }
        private bool IsWaves = false;

        private int activedBuf = 0;
        #endregion

        void WavesControl_MouseMove(object sender, MouseEventArgs e)
        {
            int realX = (int)((e.X / (double)this.ClientRectangle.Width) * waveWidth);
            int realY = (int)((e.Y / (double)this.ClientRectangle.Height) * waveHeight);
            this.PutDrop(realX, realY, 200);
        }
        /// <summary>
        /// This function is used to start a wave by simulating a round drop
        /// </summary>
        /// <param name="realX">x position of the drop</param>
        /// <param name="realY">y position of the drop</param>
        /// <param name="height">Height position of the drop</param>
        private void PutDrop(int realX, int realY, int height)
        {
            this.IsWaves = true;
            int radius =20;
            double dist;
            for (int i = -radius; i <= radius; i++)
            {
                for (int j = -radius; j <= radius; j++)
                {
                    if (((realX + i >= 0) && (realX + i < waveWidth - 1)) && ((realY + j >= 0) && (realY + j < waveHeight - 1)))
                    {
                        dist = Math.Sqrt(i * i + j * j);
                        if (dist < radius)
                            waves[realX + i, realY + j, activedBuf] = (short)(Math.Cos(dist * Math.PI / radius) * height);
                    }
                }
            }
        }
        /// <summary>
        /// Paint handler
        /// Calculates the final effect-image out of
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void WavesControl_Paint(object sender, PaintEventArgs e)
        {
            if (bmpImage == null) return;
            using (Bitmap tmpBmp = bmpImage.Clone() as Bitmap)
            {

                int xOffset, yOffset;
                byte alpha;
                if (IsWaves)
                {
                    BitmapData tmpBitmapData = tmpBmp.LockBits(new Rectangle(0, 0, bmpWidth, bmpHeight),
                        ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                    byte[] tmpBytes = new byte[bmpWidth * bmpHeight * 4];
                    Marshal.Copy(tmpBitmapData.Scan0, tmpBytes, 0, bmpWidth * bmpHeight * 4);
                    for (int x = 1; x < bmpWidth - 1; x++)
                    {
                        for (int y = 1; y < bmpHeight - 1; y++)
                        {
                            int waveX = (int)x >> scale;
                            int waveY = (int)y >> scale;
                            ///check bounds
                            waveX = waveX <= 0 ? 1 : waveX;
                            waveY = waveY <= 0 ? 1 : waveY;
                            waveX = waveX >= waveWidth - 1 ? waveWidth - 2 : waveX;
                            waveY = waveY >= waveHeight - 1 ? waveHeight - 2 : waveY;
                            ///this gives us the effect of water breaking the light
                            xOffset = (waves[waveX - 1, waveY, activedBuf] - waves[waveX + 1, waveY, activedBuf]) >> 3;
                            yOffset = (waves[waveX, waveY - 1, activedBuf] - waves[waveX, waveY + 1, activedBuf]) >> 3;

                            if ((xOffset != 0) || (yOffset != 0))
                            {
                                ///check bounds
                                if (x + xOffset >= bmpWidth - 1)
                                    xOffset = bmpWidth - x - 1;
                                if (y + yOffset >= bmpHeight - 1)
                                    yOffset = bmpHeight - y - 1;
                                if (x + xOffset < 0) xOffset = -x;
                                if (y + yOffset < 0) yOffset = -y;
                                ///generate alpha
                                alpha = (byte)(200 - xOffset);
                                if (alpha < 0) alpha = 0;
                                if (alpha > 255) alpha = 254;
                                ///set colors
                                tmpBytes[4 * (x + y * bmpWidth)] = bmpBytes[4 * (x + xOffset + (y + yOffset) * bmpWidth)];
                                tmpBytes[4 * (x + y * bmpWidth) + 1] = bmpBytes[4 * (x + xOffset + (y + yOffset) * bmpWidth) + 1];
                                tmpBytes[4 * (x + y * bmpWidth) + 2] = bmpBytes[4 * (x + xOffset + (y + yOffset) * bmpWidth) + 2];
                                tmpBytes[4 * (x + y * bmpWidth) + 3] = alpha;
                            }
                        }
                    }
                    ///copy data back
                    Marshal.Copy(tmpBytes, 0, tmpBitmapData.Scan0, bmpWidth * bmpHeight * 4);
                    tmpBmp.UnlockBits(tmpBitmapData);
                }
                e.Graphics.DrawImage(tmpBmp, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
            }
        }

        /// <summary>
        /// This is the method that actually does move the waves around and simulates the
        /// behaviour of water.
        /// </summary>
        private void Waves()
        {
            int newBuf = this.activedBuf == 0 ? 1 : 0;
            bool wavesFound = false;
            for (int x = 1; x < waveWidth - 1; x++)
            {
                for (int y = 1; y < waveHeight - 1; y++)
                {
                    waves[x, y, newBuf] = (short)(((
                        waves[x - 1, y - 1, activedBuf] +
                        waves[x - 1, y, activedBuf] +
                        waves[x - 1, y + 1, activedBuf] +
                        waves[x, y - 1, activedBuf] +
                        waves[x, y + 1, activedBuf] +
                        waves[x + 1, y - 1, activedBuf] +
                        waves[x + 1, y, activedBuf] +
                        waves[x + 1, y + 1, activedBuf]) >> 2) - waves[x, y, newBuf]);
                    ///damping
                    if (waves[x, y, newBuf] != 0)
                    {
                        waves[x, y, newBuf] -= (short)(waves[x, y, newBuf] >> 4);
                        wavesFound = true;
                    }
                }
            }
            IsWaves = wavesFound;
            activedBuf = newBuf;
        }

        void Effect_Tick(object sender, EventArgs e)
        {
            if (IsWaves)
            {
                Invalidate();
                Waves();
            }
        }
        //protected override void OnPaint(PaintEventArgs pe)
        //{
        //    base.OnPaint(pe);
        //}
    }
}

参考资料[含程序,源码,算法]

http://pan.baidu.com/s/1dD3s2xN

http://www.cnblogs.com/worldreason/archive/2008/05/09/1189648.html

http://www.codeproject.com/Articles/1073/Interactive-water-effect

时间: 2024-11-25 16:34:08

C# Winform 水波纹效果的相关文章

水波纹效果

水波纹效果 Code 1.-(void)drawRect:(CGRect)rect {2. [super drawRect:rect];3. [[UIColor colorWithRed:22 / 255.0 green:163 / 255.0 blue:130 / 255.0 alpha:1] setFill];4. UIRectFill(rect);5.6. NSInteger pulsingCount = 6; // 脉冲个数7. double animationDuration = 3;

如何使用 HTML5 Canvas 制作水波纹效果

原文:如何使用 HTML5 Canvas 制作水波纹效果 今天,我们继续分享 JavaScript 实现的效果例子,这篇文章会介绍使用 JavaScript 实现水波纹效果.水波效果以图片为背景,点击图片任意位置都会触发.有时候,我们使用普通的 Javascript 就可以创建一个很有趣的解决功能. 在线演示      源码下载 Step 1. HTML 和以前一样,首先是 HTML 代码: <!DOCTYPE html> <html> <head> <meta

自定义view实现水波纹效果

今天看到一篇自定view 实现水波纹效果 觉得真心不错 学习之后再次写下笔记和心得.但是感觉原作者写得有些晦涩难懂,也许是本人愚笨 所以重写此作者教程.原作者博文大家可以去看下,感觉他在自定义view方面非常厉害,本文是基于此作者原文重新改写,拥有大量像相似部分 先看下效果吧: 1. 效果1: 2. 效果2 我先们来学习效果1: 效果1实现本质:用一张波形图和一个圆形图的图片,然后圆形图在波形图上方,然后使用安卓的图片遮罩模式desIn(不懂?那么先记住有这样一个遮罩模式).(只显示上部图像和下

Android 实现RippleEffect水波纹效果

最近看到360.UC.网易新闻客户端都应用了水波纹效果,就在私下里也研究了一下,参照GIT上大神的分享,自己也跟着做了一个示例,下面先看效果: 1.RippleEffect核心实现类 package com.example.RippleEffect; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphi

Android自定义View——实现水波纹效果类似剩余流量球

最近突然手痒就想搞个贝塞尔曲线做个水波纹效果玩玩,终于功夫不负有心人最后实现了想要的效果,一起来看下吧: 效果图镇楼 一:先一步一步来分解一下实现的过程 需要绘制一个正弦曲线(sin)或者余弦曲线(cos) 通过水平平移曲线来的到像水波波动的效果 水平移动的同时还需要有水位上涨,也就是向上平移 裁剪画布为圆形,在圆形区域绘制曲线 通过上面4步就可以实现了 二:现在就来实现第一步,绘制一个sin曲线:这里画了一张图来帮助理解,在PhotoShop中我们绘制一个贝塞尔曲线可以清楚的看到它的控制点如图

自定义控件三部曲之绘图篇(六)——Path之贝赛尔曲线和手势轨迹、水波纹效果

前言:好想义无反顾地追逐梦想 相关文章:<Android自定义控件三部曲文章索引> 从这篇开始,我将延续androidGraphics系列文章把图片相关的知识给大家讲完,这一篇先稍微进阶一下,给大家把<android Graphics(二):路径及文字>略去的quadTo(二阶贝塞尔)函数,给大家补充一下. 本篇最终将以两个例子给大家演示贝塞尔曲线的强大用途: 1.手势轨迹 利用贝塞尔曲线,我们能实现平滑的手势轨迹效果 2.水波纹效果 电池充电时,有些手机会显示水波纹效果,就是这样

Android特效专辑(十)——点击水波纹效果实现,逻辑清晰实现简单

Android特效专辑(十)--点击水波纹效果实现,逻辑清晰实现简单 这次做的东西呢,和上篇有点类似,就是用比较简单的逻辑思路去实现一些比较好玩的特效,最近也是比较忙,所以博客更新的速度还得看时间去推演,但是也能保证一周三更的样子,现在也还是以小功能,或者说是一些小入门级别的博客为主,我也不算是什么很厉害的人,很多细节的支持处理的仍然还是不到位,所以也是一直在弥补,话不多说,来看看今天的效果 实现起来很简单吧,那我们就来看一下他是怎么实现的咯! OnclickRuning package com

[转]Android自定义控件系列五:自定义绚丽水波纹效果

出处:http://www.2cto.com/kf/201411/353169.html 今天我们来利用Android自定义控件实现一个比较有趣的效果:滑动水波纹.先来看看最终效果图: 图一 效果还是很炫的:饭要一口口吃,路要一步步走,这里我们将整个过程分成几步来实现 一.实现单击出现水波纹单圈效果: 图二 照例来说,还是一个自定义控件,这里我们直接让这个控件撑满整个屏幕(对自定义控件不熟悉的可以参看我之前的一篇文章:Android自定义控件系列二:自定义开关按钮(一)).观察这个效果,发现应该

Android Ripple 按钮水波纹效果(二)优化

上一篇中我们讲了自定义ripple 水波纹效果,先来回顾一下效果吧! 看了以后感觉没甚么问题,我一开始也觉得很满意了,那好,我们拿Android 5.0自带的效果来对比一下 发现了不同之处没?点击中间的时候是看不出什么区别,但是点击两边的时候,就很明显了,我们自定义的效果,波纹向两边同速度的扩散,所以就会出现,如果点击点不在中心的时候,距离短的一边波纹先到达,而距离长的一边后到达,不能同时到达边缘!而系统自带的则不存在这种情况,所以这是一个优化点;另一个优化点是:我们自定义的效果,在波纹全部覆盖