2012-10-16 119 views
4

我要寻找或试图实现的算法得出盒阴影(如CSS 3 specifiction),它接受下列参数:CSS3样箱阴影执行/算法

  • 水平偏移
  • 垂直偏移
  • 插图
  • 传播
  • 模糊
  • 颜色
  • (可选:不透明度)。

从哪里开始。

我特地为Firefox/Chrome的源代码,看看我是否可以从那里拉的实现,没有这样的运气!

我已经看过成线性渐变的算法,一个盒子,它留下何种作品,除与圆角矩形的阴影空像素,这可能是由于边缘的半径画他们。

我在.NET中使用GDI +这样做。我的目标不是为图像创建阴影。我已经看到有关这方面的文章。我想为使用GDI +绘制的形状创建阴影。

任何帮助表示赞赏!

+0

*“...我的目的不是为图像创建阴影,我已经看到了有关这个的文章...”我也在寻找那些,并且发现很少,其中很不好,你能告诉我你在这个主题上找到了哪些文章吗? –

+1

@UweKeim,我没有在文章或实现方面找到太多东西,但是我修改了下面选择的答案以适合我的需要。工作也很好。 – series0ne

回答

11

我编码你来处理在其内部的控制,并增加了所要求的控制的Tag的阴影(外或内)一个DropShadowPanel。

正如您可以在图像控件看得到他们的身影定义为:

标签:

文本框:阴影效果:5,5,5,10,#000000,noinset

日历:阴影效果:10,10,80,30,#0000FF,noinset

PictureBox的左上角:阴影效果:-50,20,50,10,#888888,noinset

图片框左下:阴影效果:10,10,20,20,#442200,插图

PictureBox的右下:阴影效果:0,0,50,50,#442200,noinset

enter image description here

下面是面板的代码: (它使用中间附图成的图像绘制到控制GDI对象之前,不使所述形式爬行 - 这实际工作相当快)

using System; 
using System.Collections.Generic; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace WindowsFormsApplication4 
{ 
    public class DropShadowPanel : Panel 
    { 
     protected override void OnControlAdded(ControlEventArgs e) 
     { 
      e.Control.Paint += new PaintEventHandler(Control_Paint); 
      base.OnControlAdded(e); 
     } 

    void Control_Paint(object sender, PaintEventArgs e) 
    { 
     CheckDrawInnerShadow(sender as Control, e.Graphics); 
    } 

    private void CheckDrawInnerShadow(Control sender, Graphics g) 
    { 
     var dropShadowStruct = GetDropShadowStruct(sender); 

     if (dropShadowStruct == null || !dropShadowStruct.Inset) 
     { 
      return; 
     } 

     DrawInsetShadow(sender as Control, g); 

    } 

    protected override void OnControlRemoved(ControlEventArgs e) 
    { 
     e.Control.Paint -= new PaintEventHandler(Control_Paint); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     base.OnPaint(e); 
     DrawShadow(Controls.OfType<Control>().Where(c => c.Tag != null && c.Tag.ToString().StartsWith("DropShadow")), e.Graphics); 
    } 

    void DrawInsetShadow(Control control, Graphics g) 
    { 
     var dropShadowStruct = GetDropShadowStruct(control); 

     var rInner = new Rectangle(Point.Empty, control.Size); 

     var img = new Bitmap(rInner.Width, rInner.Height, g); 
     var g2 = Graphics.FromImage(img); 

     g2.CompositingMode = CompositingMode.SourceCopy; 
     g2.FillRectangle(new SolidBrush(dropShadowStruct.Color), 0, 0, control.Width, control.Height); 

     rInner.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow); 
     rInner.Inflate(dropShadowStruct.Blur, dropShadowStruct.Blur); 
     rInner.Inflate(-dropShadowStruct.Spread, -dropShadowStruct.Spread); 

     double blurSize = dropShadowStruct.Blur; 
     double blurStartSize = blurSize; 

     do 
     { 
      var transparency = blurSize/blurStartSize; 
      var color = Color.FromArgb(((int)(255 * (transparency * transparency))), dropShadowStruct.Color);    
      rInner.Inflate(-1,-1); 
      DrawRoundedRectangle(g2, rInner, (int)blurSize, Pens.Transparent, color); 
      blurSize--; 
     } while (blurSize > 0); 

     g.DrawImage(img, 0, 0); 
     g.Flush(); 

     g2.Dispose(); 
     img.Dispose(); 
    } 

    void DrawShadow(IEnumerable<Control> controls, Graphics g) 
    { 
     foreach (var control in controls) 
     { 
      var dropShadowStruct = GetDropShadowStruct(control); 

      if (dropShadowStruct.Inset) 
      { 
       continue; // must be handled by the control itself 
      } 

      DrawOutsetShadow(g, dropShadowStruct, control); 
     } 
    } 

    // drawing the loop on an image because of speed 
    private void DrawOutsetShadow(Graphics g, dynamic dropShadowStruct, Control control) 
    { 
     var rOuter = control.Bounds; 
     var rInner = control.Bounds; 
     rInner.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow); 
     rInner.Inflate(-dropShadowStruct.Blur, -dropShadowStruct.Blur); 
     rOuter.Inflate(dropShadowStruct.Spread, dropShadowStruct.Spread); 
     rOuter.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow); 
     var originalOuter = rOuter; 

     var img = new Bitmap(originalOuter.Width, originalOuter.Height, g); 
     var g2 = Graphics.FromImage(img); 

     var currentBlur = 0; 

     do 
     { 
      var transparency = (rOuter.Height - rInner.Height)/(double) (dropShadowStruct.Blur*2 + dropShadowStruct.Spread*2); 
      var color = Color.FromArgb(((int)(255 * (transparency * transparency))), dropShadowStruct.Color); 
      var rOutput = rInner; 
      rOutput.Offset(-originalOuter.Left, -originalOuter.Top); 
      DrawRoundedRectangle(g2, rOutput, currentBlur, Pens.Transparent, color); 
      rInner.Inflate(1, 1); 
      currentBlur = (int) ((double) dropShadowStruct.Blur*(1 - (transparency*transparency))); 
     } while (rOuter.Contains(rInner)); 

     g2.Flush(); 
     g2.Dispose(); 

     g.DrawImage(img, originalOuter); 

     img.Dispose(); 
    } 

    private static dynamic GetDropShadowStruct(Control control) 
    { 
     if (control.Tag == null || !(control.Tag is string) || !control.Tag.ToString().StartsWith("DropShadow")) 
      return null; 

     string[] dropShadowParams = control.Tag.ToString().Split(':')[1].Split(','); 
     var dropShadowStruct = new 
           { 
            HShadow = Convert.ToInt32(dropShadowParams[0]), 
            VShadow = Convert.ToInt32(dropShadowParams[1]), 
            Blur = Convert.ToInt32(dropShadowParams[2]), 
            Spread = Convert.ToInt32(dropShadowParams[3]), 
            Color = ColorTranslator.FromHtml(dropShadowParams[4]), 
            Inset = dropShadowParams[5].ToLowerInvariant() == "inset" 
           }; 
     return dropShadowStruct; 
    } 

    private void DrawRoundedRectangle(Graphics gfx, Rectangle bounds, int cornerRadius, Pen drawPen, Color fillColor) 
    { 
     int strokeOffset = Convert.ToInt32(Math.Ceiling(drawPen.Width)); 
     bounds = Rectangle.Inflate(bounds, -strokeOffset, -strokeOffset); 

     var gfxPath = new GraphicsPath(); 
     if (cornerRadius > 0) 
     { 
      gfxPath.AddArc(bounds.X, bounds.Y, cornerRadius, cornerRadius, 180, 90); 
      gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y, cornerRadius, cornerRadius, 270, 90); 
      gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y + bounds.Height - cornerRadius, cornerRadius, 
          cornerRadius, 0, 90); 
      gfxPath.AddArc(bounds.X, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90); 
     } 
     else 
     { 
      gfxPath.AddRectangle(bounds); 
     } 
     gfxPath.CloseAllFigures(); 

     gfx.FillPath(new SolidBrush(fillColor), gfxPath); 
     if (drawPen != Pens.Transparent) 
     { 
      var pen = new Pen(drawPen.Color); 
      pen.EndCap = pen.StartCap = LineCap.Round; 
      gfx.DrawPath(pen, gfxPath); 
     } 
    } 
    } 
} 

代码写得很快,没有太多的回顾,所以可能会有错误,尤其是如果你在控件上设置wring标签)。

PS。您可能会注意到内部阴影对某些控件不起作用。这是因为它们是Windows系统控件的包装。面板本身无法克服这一点,但你可以这样做:http://www.codeproject.com/Articles/4548/Generating-missing-Paint-event-for-TreeView-and-Li

+2

谢谢!这对我有很大的帮助! :-) – series0ne

+0

刚创建[一个代码的Pastebin](http://pastebin.com/25ATLXGj),以便更容易复制。 –

+0

伟大的工作,它可以适用于Winforms以外的解决方案,只需最小的调整。 – Dested