“胶片盒”或目录视图相册示例






4.88/5 (50投票s)
一种照片图像画廊浏览模板 - 目录视图风格。
引言
本文演示了一个照片画廊模板,类似于 Windows XP 幻灯片文件夹视图,具有幻灯片播放功能和运行时“即时”生成缩略图图像的功能。作为一个选项,还演示了如何使用基本的 XMLHttpRequest
通过 HttpHandler
从服务器获取所需数据。演示位于此处。
背景
网络上有很多照片画廊,但令我惊讶的是,我无法找到一个小型项目所需的确切功能。大多数照片相册都显示类似于“图标”视图的缩略图画廊,当图像数量较多时,这通常会占用大量屏幕空间。无论如何,我的客户要求以缩略图“条带”的形式显示图像,并可滚动浏览,同时在下方显示一些带有详细信息的大图像,这给了我开发此功能的理由。在我看来,这种类型的画廊非常适合,例如,展示物品目录等。
这里的主要思想是在一行中显示画廊中的小图像,并可以来回滚动,并在下方放置一个大图像,类似于 WinXP 文件夹的“幻灯片视图”,或类似于目录视图。当用户点击小图像时,他会得到一个大图像和一些关于产品、物品或照片的详细信息,这些信息是从数据库或数据文件中提取的。(将数据与图像链接是另一个问题。)
使用代码
页面包含以下控件:一个带有 HyperLink
项的 Datalist
,用于显示文件夹列表并在画廊之间导航。它绑定一个包含照片/图像的文件夹数组。主照片文件夹的名称在 Web.config 文件中设置。
<add key="photofolder" value="photo"/>
页面加载时,Datalist
将填充主照片文件夹中的子目录名称,每个 DataItem
都包含指向相应文件夹的链接。子文件夹名称作为参数添加到 TargetUrl
。例如,将我们的页面切换到所需文件夹的 URL 看起来像 PhotoGallery.aspx?G="foldername"。如果没有参数,页面将使用 GetPhotos()
方法加载第一个画廊和其中的第一张图像。
所以,我们有主照片目录中的文件夹列表。接下来是什么?我们只想显示部分内容,例如 10 张图像,并表明还有更多内容可供显示。我使用 Repeater
控件,因为它是一个简单的列表控件,我只需要在其中列出图像。主要的挑战是为 Repeater
控件创建“页面”,但在网上搜索后,我找到了Harrison Enholm 的一篇文章。主要思想是首先将我的图像地址数组绑定到 PagedDataSource
,设置 PageSize
、PageNumber
等,然后将 Repeater
绑定到它。简单但有效。
现在,我们可以绑定数据并使用 ViewState 来跟踪页面等。如果只有一个页面,导航控件可以根据客户端偏好隐藏或禁用。我在这里放置了一些导航控件,如“转到页面:#”、“转到最后一页/第一页”等。
GetPhotos()
是根据页面参数从目录获取所需图像的主要方法。
此方法将遍历所选目录(取决于参数 ?G=""),并使用 System.IO.Directory.GetFiles
方法获取图像名称的 string
数组。有了名称,我们可以将它们用作图像控件的源、大图像的链接地址等。然后,我们将此数组用作 Repeater
控件的数据源。同时,我们将用页码填充 DropDownList dlPages
,我们将使用它直接跳转到任何页面。例如,当您有超过 10 页时,此选项很好。
在 Repeater
中,每个项目都是一个带有 src
属性的 <img>
<a><img id=iPhoto runat=server
src='<%# "ThumbGen.ashx?path="+Container.DataItem%>'
title='<%# Container.DataItem %>'
width="69" height="90" style="cursor:hand"
class=photoover>
</a>
private void GetPhotos()
{
string photodir =
System.Configuration.ConfigurationSettings.AppSettings["photofolder"];
//or get from config file
string curGallery="";
// use convention G is parameters to switch between galleries
if (Request["G"]!=null)
{
curGallery=Request["G"]; //find where we are
}
else
{
curGallery=
System.IO.Directory.GetDirectories(Server.MapPath(photodir))[0];
//if no parameters show first directory
string parent=(string) ViewState["parent"];
curGallery=curGallery.Substring(parent.Length);
}
// try get all files in folder
try
{
//get thumbnails and photos arrays in current directory
string[] photos = System.IO.Directory.GetFiles(
Server.MapPath(photodir+"/"+curGallery),"*.jpg");
// this array used if we have separate files as
// thumbnail images. Convention here to add _thumb to file name
//string[] thumb=System.IO.Directory.GetFiles(
// Server.MapPath("photo/"+curGallery),"*_thumb*");
//get directory info itself
int i=System.IO.Directory.GetParent(
Server.MapPath(photodir)).FullName.Length;
//
//get files names/ relative path
//just get actual files names for thumbnails and photos
//just removing extra path, leaving only catalog and file name
for (int ix=0; ix<photos.Length; ix++)
{
photos[ix]=photos[ix].Substring(i+1);
}
//ArrayList gallery=new ArrayList(photos);
//find how many photos and how many pages to create pages
int num=photos.Length;//it is number of photos
//int num=thumb.Length; old usage
lblPage.InnerText=num.ToString();
if (num>9) imgbNext.Visible=true;
//here we are using paged datasource
PagedDataSource objPds = new PagedDataSource();
//
objPds.DataSource = photos; //we will show thumbnails
// Indicate that the data should be paged
objPds.AllowPaging = true;
// Set the number of items you wish to display per page
objPds.PageSize = 10;
//fixed page size, other option
//to control through drop down box.
objPds.CurrentPageIndex = CurrentPage;
ViewState["Pages"]=objPds.PageCount;
//fill pages values for dropdown list
//do it only first time
if (!Page.IsPostBack)
{
dlPages.Items.Clear();
for (int ip=1;ip<=objPds.PageCount;ip++)
{
dlPages.Items.Add(ip.ToString());
}
}
else
{
//list already there, so just set the right page
//dlPages.SelectedIndex=CurrentPage;
}
lblPage.InnerText=" Page "+(CurrentPage+1).ToString() +
" of "+objPds.PageCount.ToString();
//control visibility of next/prev buttons
imgbPrev.Enabled = !objPds.IsFirstPage;
//imgbPrev.Visible=!objPds.IsFirstPage;
imgbNext.Enabled = !objPds.IsLastPage;
//imgbNext.Visible = !objPds.IsLastPage;
repPhotoG.DataSource=objPds;
//
//DIV1.InnerText=thumb.
//gallery.Add()
repPhotoG.DataBind();
//we also will use item data bind events
//to add some links to thumbnails
}
catch (Exception ex)
{
//do something with error
}
//imgLarge.Attributes["onfilterchange"]="fade()";
}
最初,我创建了具有一些独特名称的独立缩略图图像,然后解析文件名以仅选择目录中的缩略图而不是大图像,并将列表绑定到我的 Repeater
模板。然而,从逻辑上讲,即时生成它们似乎更好,可以方便用户。因此,我使用了 System.Drawing
类和 getThumbnail
方法来获取小图像。现在图像的 src
属性看起来像 “src=<% ''ThumbGen.ashx?path="+Container.DataItem %>
”。ThumbGen.ashx 是一个 HttpHandler
,用于处理即时创建图像。它是一个相当简单的处理程序,其任务是创建一个小图像并使用图像路径作为输入参数返回字节流。一些详细信息如下所示。
public class ThumbGen : IHttpHandler
{
public void ProcessRequest (HttpContext context)
{
if (context.Request["path"]!=null)
//we have a pth try process image
{
string img=(string)context.Request["path"];
//here is path for application dir
string path1=context.Server.MapPath(context.Request.ApplicationPath);
path1=context.Server.MapPath(img);
//img=path1+"\\"+img; //full path to image
img=path1;
System.Drawing.Image.GetThumbnailImageAbort myCallback =
new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
//get bitmap from file
Bitmap bitmap = new Bitmap(img);
// process image, create thumbnail.
// Try use to reduce size, instead of GetThubmnail
ImageFormat imgformat=bitmap.RawFormat;
//we will use fixed size for picture, in our case 69x90
//but easy to add parameters such as width and calculate height
/////////////////this code borrowed from Rick Strahl,
// http://west-wind.com/weblog
//works better than NET method
//GetThumbnail() when need transparent gif
//Bitmap bmpOut=new Bitmap(70,90);
//Graphics g=Graphics.FromImage(bmpOut);
//g.InterpolationMode=System.Drawing.Drawing2D.
// InterpolationMode.HighQualityBicubic;
//g.FillRectangle(Brushes.White,0,0,69,90);
//g.DrawImage(bitmap,0,0,69,90);
///////////////////////////////////////////////////
// ALternatively you can use
System.Drawing.Image imgOut=
bitmap.GetThumbnailImage(70, 90, myCallback, IntPtr.Zero);
bitmap.Dispose();
//but seems creating Black background for transparent GIF's
//////////////////return our image back to stream
context.Response.ContentType="image/jpeg";
imgOut.Save(context.Response.OutputStream, ImageFormat.Jpeg);
//bmpOut.Save(context.Response.OutputStream, ImageFormat.Jpeg);
//bmpOut.Dispose();
imgOut.Dispose();
}
我保留了注释掉的代码,我也用过,所以你可以尝试不同的方法来生成缩略图。我使用了 .NET 框架中可用的标准方法。另一种方法是首先创建白色背景,然后在其上加载位图。同时,我使用了硬编码的缩略图图像大小,但同样,它也可以作为可选参数传递给处理程序,或者根据图像方向(横向或纵向)进行计算。例如,可以添加更多参数,如缩略图图像的大小、宽度或高度等。在我的情况下,横向图像会看起来扭曲。最后,每个小图像都将“即时”生成并返回到页面。
此外,还有一些用于缩略图和大照片的微软滤镜。所有缩略图照片都显示为灰色,并在鼠标悬停时变为彩色。大图像在更改时具有淡入淡出效果。为了避免“闪烁”,页面过渡效果应用于整个页面,但更适合只应用于缩略图。(可以尝试的地方。)
获得缩略图画廊后,我们仍然需要决定如何显示大图像。为此,我使用 ItemDataBound
事件。在这里,我们可以在 Repeater
中找到图像,然后使用字符串操作,将绝对路径转换为相对路径(例如, вместо c:\\foldername\filename 只需 /<virtualfolder>/<filename>),并为每个图像添加一个 JavaScript 函数 OnClick
来显示大图像。相对图像路径名被传递给 JavaScript 函数。
img.Attributes["onclick"]="showImg('"+IMGpath(img.Src)+"')";
这是JavaScript函数
function showImg(imgName) {
imgOn = ("" + imgName);
document.imgLarge.src = imgOn;
hdr.innerHTML=imgName; document.imgLarge.filters[0].Apply();
document.imgLarge.filters[0].Play();
}
因此,当用户点击小图像时,大图像 imgLarge
的 src
属性将改变,并显示全尺寸照片。
同时,我决定检查第一张图片的大小,并利用这些信息来改变我的大图片的大小,以获得一个比例协调的图片。最初,我所有文件夹中的所有图片都是相同大小的,这对我来说很好,但在现实生活中,图片可能具有不同的大小和方向。在我的情况下,它们应该在一个文件夹中保持相同的大小。也许这不是目前最好的方法,最好在将其分配给大图片之前在客户端调整每张图片的大小。
为此(找出图像大小),我使用从第一张照片创建的 Bitmap
来获取宽度与高度的比例,并按比例改变大图像的大小。
private void repPhotoG_ItemDataBound(object sender,
System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
//finding repeater item Image
System.Web.UI.HtmlControls.HtmlImage img=
(System.Web.UI.HtmlControls.HtmlImage)e.Item.FindControl("iPhoto");
//create a relative path for image and add onclick function
img.Src=img.Src.Replace('\\', '/');
img.Attributes["onclick"]="showImg('"+IMGpath(img.Src)+"')";
//some visual effects using MS filters
img.Attributes["onmouseover"]="this.className='thumb'";
img.Attributes["title"]=img.Src;
img.Attributes["onmouseout"]="this.className='photoover'";
//here we are trying to get size of images
//within directiry to set a more proportional
//thumbnail. The rule to have all images within folder the same size.
if (e.Item.ItemIndex==0)
{
imgLarge.ImageUrl=IMGpath(img.Src); //show first photo in folder
string path=Server.MapPath(imgLarge.ImageUrl);
Bitmap newimg = new Bitmap(path);
double ratio= (double)newimg.Width/(double)newimg.Height;
lblName.InnerText="size:"+newimg.Width.ToString()
+"-"+newimg.Height.ToString();
lblName.InnerText=lblName.InnerText+"Resolution:"+
newimg.VerticalResolution.ToString();
imgLarge.Style["Width"]=(imgLarge.Width.Value*ratio).ToString();
newimg.Dispose();
}
}
就是这样。要在实际生活中使用此画廊,只需指定主文件夹名称,将 PhotGallery.aspx 放在 Web 服务器上,复制 bin 文件夹,并创建一个包含图像的子文件夹,并且不要忘记修改 Web.config 文件(添加配置键和 HttpHandler
部分)。应用程序将处理其他事项。
更新 (2005年10月24日)
增加了幻灯片播放功能。同样,创建幻灯片播放可能有几种不同的方法。我的意图是不使用任何回发,并在客户端完成所有操作。幻灯片播放背后的想法是在客户端有一个图像列表,然后根据当前选择动态更改我们的 image.src
属性。
为此,我在页面上放置了一个隐藏的 HtmlListBox
,以保留一个文件夹中所有图像信息的列表。这非常简单。当我们正在服务器端绑定 Repeater
时,我们将 ListBox
与图像路径绑定。当页面返回到客户端时,我们将拥有将在幻灯片播放中播放的完整图像列表。之后,我们只需要添加一些脚本来控制播放/停止和幻灯片播放的外观。有一组不同的 Java 函数负责幻灯片播放。所有幻灯片播放功能都基于 JavaScript/DHTML(文件 gallery.js)。
工作原理:点击“启动幻灯片播放”将激活幻灯片播放,并在图像顶部显示“幻灯片控制框”。 (上一张/下一张/暂停/全尺寸/停止)对于“全尺寸”功能,我们使用 DHTML,因此我们可以更改元素的布局和位置。因此,当用户点击“全尺寸”按钮时,他将获得图像在中间(位置将使用客户端屏幕尺寸计算)。
为了简化起见,停止幻灯片播放,我只使用了常规的 ASP.NET 按钮“回发”,它将刷新页面并重新加载画廊。否则,我们需要一个与“全尺寸”相反的函数,并将所有内容恢复。另一种选择可能是一个单独的“全屏”窗口。
////////////////
//start slideshow, using list of images from list box
function SlideShow()
{
document.imgLarge.filters[0].Duration=2.5;
document.imgLarge.filters[0].Apply();
//alert(document.getElementById("listImg").options[i].text);
document.getElementById('hdr').innerText=
document.getElementById("listImg").options[i].text;
document.imgLarge.src=document.getElementById("listImg").options[i].text;
document.imgLarge.filters[0].Play();
if (i>=((document.getElementById("listImg").length)-1))
{
i=0;
}
else
{
i=i+1;
}
}
“AJAX”方式创建幻灯片
为了探索AJAX技术,在HttmlHandler
中添加了新选项。如果我们使用参数“Dir'”(将显示“ThumbGen.asxh?Dir=___
”),这意味着我们要运行“幻灯片”的目录名称,处理程序将向响应流返回一个包含所有图像名称的XML文档。我们将使用它通过XtmlHTTP
请求从客户端进行远程回调,并获取所需的图像列表。我们得到的结果是一个数组,但请注意,我们将转换返回流以获取数据的XML表示。这将为您提供更多选项来处理客户端文档,尤其是当您有来自数据库的数据时。例如,如果我们有一个DataSet
,我们可以使用方法.GetXML()
来获取XML格式。否则,我们只需调用ThumbGen.ashx
并附带所需的参数,然后返回一个包含照片名称的“列表”。然后我们在客户端填充Arrays
。就是这样,我们有了幻灯片播放的源,并将使用与之前相同的脚本来运行它。
我尝试这种方式只是为了看看我们如何使用 XmlHtml
对象和 HtmlHandler
。以类似的方式,我们可以从数据库请求数据。在页面上,您还可以找到一个隐藏的下拉列表框,仅作为示例,尝试如何使用请求填充它。我没有将它用于任何其他目的。提示:例如,如果我们不指定图像的宽度,它将自动按比例缩放高度。
我相信还有很大的改进空间,但这只是一个构建画廊的模板,以及Javascript/DHTML和远程回调技术的不同示例。
function ProcessResponse()
{
if(req.readyState == 4)
{
if(req.status == 200) //analog "OK",
{
//here we are processing result,////////////////////////
//
var p=req.responseXML.documentElement;
///alert(req.responseXML); -testing purpose
var ddlTest = document.getElementById("test");
//var limg=document.getElementById("listImg");
for (var count = ddlTest.options.length-1;count >-1; count--)
{
ddlTest.options[count] = null;
}
var photos = p.getElementsByTagName('photos');
var text;
//here we are trying to create array in memory
//this example just to show how we can
//fill selection box from callback result.
//After callback we got XMl document-table
//and we can use it to fill table/select/list etc.
var listItem;
var l=photos.length;
for (var count = 0; count < photos.length; count++)
{
text = (photos[count].textContent ||
photos[count].innerText || photos[count].text);
listItem = new Option(text, text, false, false);
ddlTest.options[ddlTest.length] = listItem;
}
}
else
{
alert("Error retrieving data!" );
}
////// this script responsible to fill Album array
// by data returned from client in our case
// it is a images pathes.
for (i = 0; i<l; i++)
{
text2 = (photos[i].textContent ||
photos[i].innerText || photos[i].text);
Album[i]= new Image();
Album[i].src = text2;
}
//now we have array with images. call slide show
slide=0;
//call slide show and show menu on top of image
AjaxSlideShow();
SlideMenu();
}
function AjaxSlideShow() { StopSlide(); if (slide>=Album.length) { slide=0; } document.imgLarge.filters[0].Duration=2.5; document.imgLarge.filters[0].Apply(); document.imgLarge.src=Album[slide].src; document.imgLarge.filters[0].Play(); var tm=document.getElementById("slidetime").value*1000 oInterval=window.setTimeout("AjaxSlideShow()",tm); //nr=nr+1; slide++; }
关注点
我正在考虑通过添加一些方法来扩展处理程序,以从数据库获取图像并将图像与数据文件或数据库绑定。我仍在寻找改进缩略图加载的方法,以避免“闪烁”并获得更流畅的外观。我还留下了一个指向“幻灯片播放”功能的链接:播放目录中的所有文件——这有待实现。我还注意到,当页面刚加载时,滤镜最初不工作,但在下一次点击后才开始工作。我还想尝试将其转换为具有一些外部属性的用户控件。总有办法添加东西,改进。这就是我喜欢编程的原因,它是无止境的。
历史
- 2005年8月 - 基本项目。
- 2005年10月 - 为可用性重写。
- 2005年10月24日 - 添加了幻灯片播放功能。