类似于CSS3的盒子阴影实现/算法 [英] CSS3-like box shadow implementation / algorithm

查看:124
本文介绍了类似于CSS3的盒子阴影实现/算法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找或尝试实现一种算法来绘制框阴影(如CSS 3规范所示),该算法接受以下参数:

I am looking for or trying to implement an algorithm to draw box shadows (as in the CSS 3 specifiction) which accepts the following parameters:

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

从哪里开始.

我一直在寻找Firefox/Chrome源代码,以查看是否可以从那里获取实现,没有运气!

I have looked for Firefox / Chrome source code to see if I can pull an implementation from there, no such luck!

我研究了线性渐变算法,用一个盒子绘制它们,除了圆角矩形外,它在阴影中留下了空像素,这大概是由于边缘的半径.

I have looked into linear gradient algorithms, drawing them with a box, which kind of works, except with rounded rectangles it leaves empty pixels in the shadow, presumably due to the radius of the edge.

我正在使用GDI +在.NET中进行此操作.我的目的不是为图像创建阴影.我已经看过有关此的文章.我想为使用GDI +绘制的形状创建阴影.

I am doing this in .NET with GDI+. My aim is NOT to create drop shadows for images. I have already seen articles on this. I want to create drop shadows for shapes drawn with GDI+.

任何帮助表示赞赏!

推荐答案

我为您编写了一个DropShadowPanel,用于处理其中的控件并根据控件的Tag要求添加阴影(外部或内部).

I coded for you a DropShadowPanel that handles controls inside it and adds the shadows (outer or inner) as required by the Tag of the control.

正如您在图像控件中看到的那样,将其阴影定义为:

As you can see in the image controls get their shadows as defined:

标签:

文本框: DropShadow:5、5、5、10,#000000,noinset

日历: DropShadow:10、10、80、30,#0000FF,无内陷

图片框左上角: DropShadow:-50,20,50,10,#888888,noinset

图片框左下角: DropShadow:10、10、20、20,#442200,插图

图片框右下角: DropShadow:0,0,50,50,#442200,noinset

这是面板的代码: (它会在绘制到控件gdi对象之前使用中间的图形来绘制图像,以免使表单爬网-实际上运行起来非常快)

Here is the code for the Panel: (it uses intermediate drawings into an image before drawing to the control gdi object, to not make the form crawl - this actually works pretty fast)

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);
        }
    }
    }
}

代码写得很快,无需过多检查,因此可能会出现错误,尤其是在控件上设置扭曲标记的情况下.

Code is written fast without much review so there may be bugs especially if you set wring tags on controls).

PS.您可能会注意到内部阴影不适用于某些控件.这是因为它们是Windows系统控件的包装.小组无法独自克服这一问题,但是您可以像下面这样:

PS. You may notice that inner shadow does not work for some controls. This is because they are wrappers around windows system controls. The panel cannot overcome this by itself, but you may do it like here: http://www.codeproject.com/Articles/4548/Generating-missing-Paint-event-for-TreeView-and-Li

这篇关于类似于CSS3的盒子阴影实现/算法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆