65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 T4 模板生成 CSS 精灵图

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (16投票s)

2013年10月5日

CPOL

10分钟阅读

viewsIcon

30999

downloadIcon

325

使用一些 ASP.NET MVC 4 辅助方法

引言

在本文中,我想介绍一种简单的方法来处理如今网站上使用的许多小图像。这种方法将使用 T4 模板来制作 CSS 精灵和其他辅助文件。本文的主要目的是说明如何通过 T4 提高开发人员的生产力。 

所有文件均可在 GitHub 上获取:  

https://github.com/regiuskornel/T4CSS-Sprite  

背景

一个“设计良好”的网站充满了图标,这会带来很多问题。每个图标都会向网站发出单独的请求。30-100 个额外请求会花费大量时间才能完全渲染整个 HTML 页面。在最坏的情况下,这段时间最多会造成 1-10 秒的延迟,并消耗网络和 Web 服务器资源。我们需要将这些额外请求减少到一个。

对于 CSS 和 JS 文件,我们有一个 ASP.NET 4 中的方便的“打包”(bundling)服务,实现在 System.Web.Optimization DLL 中。该服务将这些文本文件打包在一起,并以一个请求发送到浏览器。CSS 精灵也是一样,只是针对图像。我们将所有小图像放在一个大图像上,只将这个大图像发送到浏览器。此外,如果我们以智能的方式收集图像,我们可以在整个 Web 应用程序中重用它。这样,浏览器甚至不需要再次下载该图片,因为它可以在本地缓存中找到该图像。它只会发送一个几十秒的检查请求,询问自上次下载以来图像是否已修改。

问题所在

,谁将在何时组装这个马赛克,这个精灵?假设我们有 150 个小图像……桌面工具和在线精灵生成器早已存在。使用它们,我们通常可以做两件事:将所有图像放在一个表面上,或者定位它们,然后保存生成的精灵。更好的工具也会创建 CSS 文件,基于背景位置坐标和图标的名称。例如:  

.t4SpriteVar { background-image: url("SpriteVar.png")}
.t4accept {background-position:0px -418px;width: 48px;height: 48px;}
.t4accept.disabled {background-position:-48px -418px;width: 48px;height: 48px;} 
使用工具有什么问题?对我来说,问题在于它们是外部工具、Web 应用程序、exe 文件,需要正确的处理和参数化。然而,最大的问题仍然是它们与版本处理环境(TFS、SVN)不兼容,这使得在团队合作中使用它们很不方便。想想看,为了给我们的用户换一个按钮上的新图标,我们需要花费多少时间?只需将其中一个替换为更大的。手动检出、修改+生成、签入过程。相比之下,对于没有精灵的开发人员来说,这是多么容易。他将图像复制到一个文件夹,在 Visual Studio(或其他工具)的 .aspx 或 .cshtml 文件中将其拖放到编辑器中,就完成了。我明白为什么开发人员更喜欢单个图标而不是精灵。

解决问题 

这让我想。如何才能消除所有这些问题,同时保留 CSS 精灵的优势?如果可能,我们甚至不想离开 Visual Studio 的窗口。让我们看看要求:

  • 我们有一个包含许多小图像的文件夹。它们的扩展名(png、jpg、gif)和/或大小可能不同。 
  • 我们想从中创建一个精灵;因此,我们需要一个图像生成器。 
  • 我们需要一个 CSS 文件,其中包含以 background-position 格式的图像的尺寸和坐标。 
  • 最好能有一个页面提供图像及其名称的概述,以便进行搜索。 
  • 一个静态的 C# 类也将非常有用,以便能够以类型安全的方式引用图片名称。这样,如果图片丢失,我们将在编译时收到通知。此外,我们还将获得 .cshtml 和 .aspx 页面上图片名称的 IntelliSense 支持。 

一个图像文件和三个文本文件。这就是我们将使用 T4 模板的目的。  

T4 代表 Text Template Transformation Toolkit。它最常用于生成
文本文件,尽管其名称并未将其定义为唯一的目的。顾名思义,我们有一个文本模板,其语法类似于 Web Forms 的 .aspx 文件,我们可以使用它来在 Visual Studio 中生成内容。模板——一如既往——包含静态部分和动态内容生成部分。此外,我们还可以在其中定义普通的 C# 或 VB.NET 代码块。
T4 的巨大优势在于 VS 本身会运行模板。因此,我们可以将生成的文件分配给项目,并从 T4 源代码通知版本控制系统。
 

 
在接下来的章节中,我不会深入 T4 的细节,而是描述演示项目中包含的 T4 文件的功能和用法。我相信,我将要展示的演示并非一个完美的解决方案,而更像是一个 POC 项目。它对添加和扩展是开放的,这在 GitHub 上并不难做到。由于 VS 不为 T4 文件提供代码格式化(着色)或 IntelliSense,因此建议从 Visual Studio Gallery 下载一个外部 VS 插件。在开发过程中,我使用了DevArt T4-Editor  

T4 文件的一个特性——在我看来是明智的——是导入其他 T4 文件的可能性。通过这种方式,我们可以定义代码集合。include 指令: 

 <#@ include file="T4SpriteMaker.ttinclude"#> 

对于 T4 代码集合,让它生成文件是个坏主意。如果我们希望 VS 不运行 T4 文件,我们必须在文件属性窗口中删除“Custom Tool”的值。

 

 

最重要的文件将是这个 T4SpriteMaker.ttinclude,其余的将只包含用于图片生成的自定义参数化。 

我还需要提到,创建精灵和 CSS 的基本代码/算法不是我的作品。您可以在 Code Project 上找到原始版本:Image Sprites and CSS Classes Creator。我采用了 V2.0 的源代码,并修改了它以匹配 T4 的逻辑,因为原始版本是一个控制台应用程序。如果您认为,可以给瑞士的 AlexCode 的杰作至少投“优秀”一票。他的代码中最棒的是它简单、快速、简短。这就是我选择它的原因,因为在 T4 文件中编写和调试长而复杂的代码有点困难。 

T4 文件的另一个属性是它们不能从外部参数化。我们有 VS 提供的文件属性窗口,但我们无法走得太远。因此,我们必须在一个文件中设置参数,这就是为什么我将代码库与参数化 T4 文件分开。让我们来看一个参数化 .tt 文件,看看每个设置的作用。根据 AlexCode 的模式,设置必须作为属性放在 SCEngineOptions 类中: 

.SourceDirectory = T4Folder+"\\Images16"; 

此属性定义源图像所在的文件夹。'T4Folder' 是 .tt 文件所在的文件夹,这使我们能够为源图像文件夹设置相对路径。

.CssClassPrefix = "t4";  

建议为定义图像在精灵中位置的 CSS 类名添加一个前缀,以防止名称与手动创建的普通 CSS 类(例如 .t4label 而不是 .label)发生冲突。

.BinPackingLevel = 1; 

图像定位算法的代码编号。目标是使精灵文件大小最小。AlexCode 的代码中有三种算法,我原封不动地采纳了它们。

1 - 基本矩形(最快)

2 - Cygon packager(平均大小)

3 - ArevaloRectanglePacker(可能是最小尺寸)。 

有人可能会认为,“最佳”就是最好的,但事实并非如此。这就是为什么您有这个设置,以便您可以选择。适合特定情况的最佳算法取决于图片的大小、尺寸的变化和图片的数量。糟糕的是,找到每个图片包最小精灵的唯一方法就是自己运行算法。除非我们的图片大小相同。在这种情况下,1. 基本算法肯定最好。 

.FilesName = Path.GetFileNameWithoutExtension(T4FileName);

生成的 .png 精灵、CSS、HTML、.cs 文件、C# 静态类和 CSS 基本类的名称。如您所见,在这种情况下,它来自 .tt 文件名。然而,您可以用任何静态名称覆盖它。

.ImageVariants.Add(new ImageVariant() {
            GreyScale = true,            //is gray scaled image?
            Width = 0,                   //Resized image width, 0 means image won't resize
            Height = 0,
            CssClassName= "disabled",    //additional CSS class for this variant
            DisplayName = "Disabled"     //Column title in demo/test html 
});  

为了更进一步,我创建了一个扩展,允许我们通过图像转换从源图像创建图像变体。这解决了常见问题,即工具箱图标有多个视觉状态:可点击(彩色)、禁用(灰色)和按下(高亮)。有时,我们希望使用不同大小的图像,如 48x48、32x32 或 16x16。由于上面所示的定义,每个图标都会有一个灰度版本。您还可以为每个变体分配自定义 CSS 类。它的巨大优势在于,可以通过 CSS 类的存在或不存在轻松定义视觉状态(启用或禁用)。 

 

已启用(左图标): <span class="image24 t4icon t4SpriteVar t4clock"></span> 

已禁用(右图标): <span class="image24 t4icon t4SpriteVar t4clock disabled"></span> 

如果您想进行多次转换,可以将多个 CSS 类放入 CssClassName 中。这就是我制作底部右侧四个“时钟”的普通 128x128 / 禁用 128x128 / 普通 24x24 / 禁用 24x24 图标的方式: 

.ImageVariants.Add(new ImageVariant() {
            GreyScale = true,
            Width = 24,
            Height = 24,
            CssClassName= "image24 disabled",
            DisplayName = "24x24 dis."
            });  

这将使它们变灰,将它们调整为 24x24,并创建两个类以区分原始 128x128 和普通 24x24 图像。   

您可以在 ImageVariants 集合中放入多个变体定义。有关更多示例,请参阅“SpriteVar.tt”中的设置,其中定义了创建多个尺寸和灰度版本。

其余的参数化 T4 文件包含生成 CSS、.cs 和概述 HTML 文件的部分。   

如何使用 

我们将 T4SpriteMaker.ttinlude 文件放在我们想要的位置。然后,我们必须确保在参数化 T4 文件中设置链接,以通过相对路径访问 .ttinclude 文件。

<#@ include file="..\path\T4SpriteMaker.ttinclude"#>

 

如果它与 .tt 文件在同一个文件夹中,那么我们什么都不用做。include 语句将使用默认值。

 

之后,我们创建图像文件夹并将所需内容放入其中。我们为 .tt 文件命名,因为这将是生成文件的默认名称(例如,Sprite32.tt,因为我们在本演示中使用 .FilesName 的值)。

 

在参数化和保存 .tt 文件后,其他文件将被生成。 

.cs 文件将包含包裹在静态类中的图像名称常量。命名空间将来自属性窗口中的“Custom Tool Namespace”设置。

namespace MvcApp.Content.Images 
{
	public static class Sprite32
	{
		public const string t4accept = "t4accept";
		public const string t4add = "t4add";
		public const string t4block = "t4block";
		public const string t4chart_pie = "t4chart_pie";
		public const string t4users = "t4users";
		//…
	}
}    

在 .css 中,您将找到位置代码(稍作压缩)。 

.t4Sprite32{background-image: url("Sprite32.png")}
.t4accept{background-position:0px -162px;width:32px;height:32px;}
.t4accept.disabled{background-position:-32px -162px;width:32px;height:32px;}
.t4add{background-position:-64px -162px;width:32px;height:32px;}
.t4add.disabled{background-position:-96px -162px;width:32px;height:32px;} 

.html 文件将包含概述页面和标记中使用的示例。表格的标题将包含 .DisplayName 参数的值。例如,在右侧的图片中,.DisplayName = "Disabled" 设置导致了灰度图标列的标题为“Disabled”。   

 

(您还会在演示应用程序中找到几个额外的 .tt 文件,可用于测试目的。这些是为了应对诸如错误的图像文件、空的图像文件夹或缺少图像文件夹等情况(SpriteInvalidImages、SpriteEmptyDir、SpriteWrongPath)而创建的。)  

演示应用程序还包含在 ASP.NET MVC 4 框架中的一个可能用法,以及自定义的 Html 和 Ajax 链接辅助方法。 一些方法调用变体: 

@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index")
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", ButtonMode.Disabled)
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home")
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home", ButtonMode.Disabled)
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home", null, null)
@Html.SPActionLink(SpriteVar.t4block, "Block button", "Index", "Home", null, null, 
                   ButtonMode.Disabled)

@Html.SPActionLink(SpriteVar.t4help, "Help button", "Index", "Home", null, null, 
                   ButtonMode.Default | ButtonMode.IconOnly)
@Html.SPActionLink(SpriteVar.t4help, "Help button", "Index", "Home", null, null, 
                   ButtonMode.Disabled | ButtonMode.IconOnly)  

 值得关注的点  

我希望我已经成功地引起了您对 T4 模板在构建动态 UI 方面能提供巨大帮助的兴趣。这种结构足够灵活,可以防止因图像元素频繁更改而可能出现的障碍和麻烦。 
本文和演示应用程序中的源图像来自dryicons.com 

© . All rights reserved.