jquery+.net实现类似开心网图像缩放截取功能(附代码下载)
好不容易完成了一个基于web2.0概念的项目管理系统,在系统的实现过程中用到了头像的缩放裁剪的功能模块,而且我发现在网络上很少有讨论这方面的文章,所以把我的实现方式贴出来,和大家分享一下,写的不好还请多多海涵。
我是用jquery ui的ui.draggable实现的。当然,本文所实现的方法不局限于jquery ui,只要能实现拖动的功能,任何库都可以。我用的jquery ui的版本号是1.6,这个版本已经改了很多的bug,渐趋完善,老实说以前的有些版本代码bug非常多,现在代码质量有了一定的提高,尤其bug改了不少。这个ui库完全兼容jquery的语法,也就是说隐式迭代、超级强大的selector等都可以无缝的使用,这比起dojo,ext等组件库使用起来要更为方便和轻量些。具体的使用情况如下:
(1)、初始状况: |
(2)、 缩放后:
|
(3)、 截取效果:
1、图片处理后台代码
要实现缩放和截取必须要知道原图片的width/height、缩放后后的图片实际的width/height、截取框的width/height和应截取距离左边(left)和顶部(TOP)的距离,代码如下,都加了注释了,不过这方面的代码网络上多的是。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
public class ImageHelp
{
/**//// <summary>
/// 获取图片中的各帧
/// </summary>
/// <param name="pPath">图片路径</param>
/// <param name="pSavePath">保存路径</param>
public void GetFrames(string pPath, string pSavedPath)
{
Image gif = Image.FromFile(pPath);
FrameDimension fd = new FrameDimension(gif.FrameDimensionsList[0]);
//获取帧数(gif图片可能包含多帧,其它格式图片一般仅一帧)
int count = gif.GetFrameCount(fd);
//以Jpeg格式保存各帧
for (int i = 0; i < count; i++)
{
gif.SelectActiveFrame(fd, i);
gif.Save(pSavedPath + "\\frame_" + i + ".jpg", ImageFormat.Jpeg);
}
}
/**//**/
/**//// <summary>
/// 获取图片缩略图
/// </summary>
/// <param name="pPath">图片路径</param>
/// <param name="pSavePath">保存路径</param>
/// <param name="pWidth">缩略图宽度</param>
/// <param name="pHeight">缩略图高度</param>
/// <param name="pFormat">保存格式,通常可以是jpeg</param>
public void GetSmaller(string pPath, string pSavedPath, int pWidth, int pHeight)
{
string fileSaveUrl = pSavedPath + "\\smaller.jpg";
using (FileStream fs = new FileStream(pPath, FileMode.Open))
{
MakeSmallImg(fs, fileSaveUrl, pWidth, pHeight);
}
}
//按模版比例生成缩略图(以流的方式获取源文件)
//生成缩略图函数
//顺序参数:源图文件流、缩略图存放地址、模版宽、模版高
//注:缩略图大小控制在模版区域内
public static void MakeSmallImg(System.IO.Stream fromFileStream, string fileSaveUrl, System.Double templateWidth, System.Double templateHeight)
{
//从文件取得图片对象,并使用流中嵌入的颜色管理信息
System.Drawing.Image myImage = System.Drawing.Image.FromStream(fromFileStream, true);
//缩略图宽、高
System.Double newWidth = myImage.Width, newHeight = myImage.Height;
//宽大于模版的横图
if (myImage.Width > myImage.Height || myImage.Width == myImage.Height)
{
if (myImage.Width > templateWidth)
{
//宽按模版,高按比例缩放
newWidth = templateWidth;
newHeight = myImage.Height * (newWidth / myImage.Width);
}
}
//高大于模版的竖图
else
{
if (myImage.Height > templateHeight)
{
//高按模版,宽按比例缩放
newHeight = templateHeight;
newWidth = myImage.Width * (newHeight / myImage.Height);
}
}
//取得图片大小
System.Drawing.Size mySize = new Size((int)newWidth, (int)newHeight);
//新建一个bmp图片
System.Drawing.Image bitmap = new System.Drawing.Bitmap(mySize.Width, mySize.Height);
//新建一个画板
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap);
//设置高质量插值法
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
//设置高质量,低速度呈现平滑程度
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//清空一下画布
g.Clear(Color.White);
//在指定位置画图
g.DrawImage(myImage, new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
new System.Drawing.Rectangle(0, 0, myImage.Width, myImage.Height),
System.Drawing.GraphicsUnit.Pixel);
/**////文字水印
System.Drawing.Graphics G = System.Drawing.Graphics.FromImage(bitmap);
System.Drawing.Font f = new Font("Lucida Grande", 6);
System.Drawing.Brush b = new SolidBrush(Color.Gray);
G.DrawString("Ftodo.com", f, b, 0, 0);
G.Dispose();
/**////图片水印
//System.Drawing.Image copyImage = System.Drawing.Image.FromFile(System.Web.HttpContext.Current.Server.MapPath("pic/1.gif"));
//Graphics a = Graphics.FromImage(bitmap);
//a.DrawImage(copyImage, new Rectangle(bitmap.Width-copyImage.Width,bitmap.Height-copyImage.Height,copyImage.Width, copyImage.Height),0,0, copyImage.Width, copyImage.Height, GraphicsUnit.Pixel);
//copyImage.Dispose();
//a.Dispose();
//copyImage.Dispose();
//保存缩略图
if (File.Exists(fileSaveUrl))
{
File.SetAttributes(fileSaveUrl, FileAttributes.Normal);
File.Delete(fileSaveUrl);
}
bitmap.Save(fileSaveUrl, System.Drawing.Imaging.ImageFormat.Jpeg);
g.Dispose();
myImage.Dispose();
bitmap.Dispose();
}
/**//**/
/**//// <summary>
/// 获取图片指定部分
/// </summary>
/// <param name="pPath">图片路径</param>
/// <param name="pSavePath">保存路径</param>
/// <param name="pPartStartPointX">目标图片开始绘制处的坐标X值(通常为)</param>
/// <param name="pPartStartPointY">目标图片开始绘制处的坐标Y值(通常为)</param>
/// <param name="pPartWidth">目标图片的宽度</param>
/// <param name="pPartHeight">目标图片的高度</param>
/// <param name="pOrigStartPointX">原始图片开始截取处的坐标X值</param>
/// <param name="pOrigStartPointY">原始图片开始截取处的坐标Y值</param>
/// <param name="pFormat">保存格式,通常可以是jpeg</param>
public void GetPart(string pPath, string pSavedPath, int pPartStartPointX, int pPartStartPointY, int pPartWidth, int pPartHeight, int pOrigStartPointX, int pOrigStartPointY)
{
string normalJpgPath = pSavedPath + "\\normal.jpg";
using (Image originalImg = Image.FromFile(pPath))
{
Bitmap partImg = new Bitmap(pPartWidth, pPartHeight);
Graphics graphics = Graphics.FromImage(partImg);
Rectangle destRect = new Rectangle(new Point(pPartStartPointX, pPartStartPointY), new Size(pPartWidth, pPartHeight));//目标位置
Rectangle origRect = new Rectangle(new Point(pOrigStartPointX, pOrigStartPointY), new Size(pPartWidth, pPartHeight));//原图位置(默认从原图中截取的图片大小等于目标图片的大小)
/**////文字水印
System.Drawing.Graphics G = System.Drawing.Graphics.FromImage(partImg);
System.Drawing.Font f = new Font("Lucida Grande", 6);
System.Drawing.Brush b = new SolidBrush(Color.Gray);
G.Clear(Color.White);
graphics.DrawImage(originalImg, destRect, origRect, GraphicsUnit.Pixel);
G.DrawString("Ftodo.com", f, b, 0, 0);
G.Dispose();
originalImg.Dispose();
if (File.Exists(normalJpgPath))
{
File.SetAttributes(normalJpgPath, FileAttributes.Normal);
File.Delete(normalJpgPath);
}
partImg.Save(normalJpgPath, ImageFormat.Jpeg);
}
}
/**//**/
/**//// <summary>
/// 获取按比例缩放的图片指定部分
/// </summary>
/// <param name="pPath">图片路径</param>
/// <param name="pSavePath">保存路径</param>
/// <param name="pPartStartPointX">目标图片开始绘制处的坐标X值(通常为)</param>
/// <param name="pPartStartPointY">目标图片开始绘制处的坐标Y值(通常为)</param>
/// <param name="pPartWidth">目标图片的宽度</param>
/// <param name="pPartHeight">目标图片的高度</param>
/// <param name="pOrigStartPointX">原始图片开始截取处的坐标X值</param>
/// <param name="pOrigStartPointY">原始图片开始截取处的坐标Y值</param>
/// <param name="imageWidth">缩放后的宽度</param>
/// <param name="imageHeight">缩放后的高度</param>
public void GetPart(string pPath, string pSavedPath, int pPartStartPointX, int pPartStartPointY, int pPartWidth, int pPartHeight, int pOrigStartPointX, int pOrigStartPointY, int imageWidth, int imageHeight)
{
string normalJpgPath = pSavedPath + "\\normal.jpg";
using (Image originalImg = Image.FromFile(pPath))
{
if (originalImg.Width == imageWidth && originalImg.Height == imageHeight)
{
GetPart(pPath, pSavedPath, pPartStartPointX, pPartStartPointY, pPartWidth, pPartHeight, pOrigStartPointX, pOrigStartPointY);
return;
}
Image.GetThumbnailImageAbort callback = new Image.GetThumbnailImageAbort(ThumbnailCallback);
Image zoomImg = originalImg.GetThumbnailImage(imageWidth, imageHeight, callback, IntPtr.Zero);//缩放
Bitmap partImg = new Bitmap(pPartWidth, pPartHeight);
Graphics graphics = Graphics.FromImage(partImg);
Rectangle destRect = new Rectangle(new Point(pPartStartPointX, pPartStartPointY), new Size(pPartWidth, pPartHeight));//目标位置
Rectangle origRect = new Rectangle(new Point(pOrigStartPointX, pOrigStartPointY), new Size(pPartWidth, pPartHeight));//原图位置(默认从原图中截取的图片大小等于目标图片的大小)
/**////文字水印
System.Drawing.Graphics G = System.Drawing.Graphics.FromImage(partImg);
System.Drawing.Font f = new Font("Lucida Grande", 6);
System.Drawing.Brush b = new SolidBrush(Color.Gray);
G.Clear(Color.White);
graphics.DrawImage(zoomImg, destRect, origRect, GraphicsUnit.Pixel);
G.DrawString("Ftodo.com", f, b, 0, 0);
G.Dispose();
originalImg.Dispose();
if (File.Exists(normalJpgPath))
{
File.SetAttributes(normalJpgPath, FileAttributes.Normal);
File.Delete(normalJpgPath);
}
partImg.Save(normalJpgPath, ImageFormat.Jpeg);
}
}
/**//// <summary>
/// 获得图像高宽信息
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public ImageInformation GetImageInfo(string path)
{
using (Image image = Image.FromFile(path))
{
return new ImageInformation { Width = image.Width, Height = image.Height };
}
}
public bool ThumbnailCallback()
{
return false;
}
}
public struct ImageInformation
{
public int Width { get; set; }
public int Height { get; set; }
}
2、前台具体实现
那么我们应该怎样获取这几个重要参数?各位看看头像截取模块的示例图就知道了
为了实现上面的效果,页面的结构如下。
<div id="Currentimages" style="overflow: auto;">
<ul>
<li>
<div id="currentImage" runat="server">
<h2>当前头像</h2>
<hr/>
<asp:Image ID="img_CurrentHeadImage" runat="server"/>
</div>
<div id="div_HeadImageCut">
<h2> 裁切头像照片</h2><hr/>
<div id="content">
<div id="image">
<img id="img" src="image/无标题.jpg" />
</div>
<div id="drop">
<img id="drop_img" src="image/无标题.jpg" />
</div>
</div>
<table>
<tr>
<td id="Min">
<img alt="缩小" src="image/Minc.gif" style="width: 19px; height: 19px"
id="moresmall" class="smallbig" />
</td>
<td>
<div id="bar">
<div class="child">
</div>
</div>
</td>
<td id="Max">
<img alt="放大" src="image/Maxc.gif" style="width: 19px; height: 19px"
id="morebig" class="smallbig" />
</td>
</tr>
</table>
<br />
<asp:Button ID="btn_Image" runat="server" Text="保存头像" OnClick="btn_Image_Click" />
原尺寸:宽<label id="width" class="Hidden">
<%=this.width %></label>px 高:<label id="height" class="Hidden"><%=this.height%>px</label>
</div>
</li>
<li>
图片实际宽度: <asp:TextBox ID="txt_width" runat="server" Text="1" CssClass="Hidden"></asp:TextBox><br />
图片实际高度: <asp:TextBox ID="txt_height" runat="server" Text="1" CssClass="Hidden"></asp:TextBox><br />
距离顶部: <asp:TextBox ID="txt_top" runat="server" Text="1" CssClass="Hidden"></asp:TextBox><br />
距离左边: <asp:TextBox ID="txt_left" runat="server" Text="1" CssClass="Hidden"></asp:TextBox><br />
截取框的宽: <asp:TextBox ID="txt_DropWidth" runat="server" Text="1" CssClass="Hidden"></asp:TextBox><br />
截取框的高: <asp:TextBox ID="txt_DropHeight" runat="server" Text="1" CssClass="Hidden"></asp:TextBox><br />
放大倍数: <asp:TextBox ID="txt_Zoom" runat="server" Text="1" CssClass="Hidden"></asp:TextBox>
</li>
</ul>
</div>
样式如下:
html{}{filter: expression(document.execCommand("BackgroundImageCache", false, true));}
body{}{ width:955px; text-align:left;}
#content
{}{cursor: pointer; position: relative;width: 320px;height: 320px; border: 1px solid #ccc;overflow: hidden;background-color:#bbb}
#drop{}{border: 1px solid #ccc;width: 120px;height: 120px;cursor: pointer;position: absolute;top: 100px; left: 100px;overflow: hidden;}
#drop_img
{}{cursor:pointer;position: absolute;}
#father
{}{border: 1px solid red;width: 50px;height: 10px;position: relative;}
#bar
{}{width: 211px;height: 18px;background-image: url("../image/track.gif");background-repeat: no-repeat;position: relative;}
.child
{}{width: 11px;height: 16px;background-image: url("../image/grip(11 16).gif");background-repeat: no-repeat;left: 0;top: 0;position: absolute;left: 100px;}
.smallbig{}{ cursor:pointer;}
li{}{ list-style:none;}
Content本身为相对定位,content内的元素互相叠加,而且,在截取框里外都有个image和div,拖动外面的图片(其实是div)则截取框内的图片也按比例在div里移动。拖动里面的也是同样的道理。只要把上面代码中的所标注的几个参数取出来用后台方法进行截取出来就OK了,老实说,代码并没什么难点,有的话也在主要还是对图形的计算上比较麻烦,让我想起了以前做平面几何还有应用题时的情况
拖动的代码如下,非常的简单,关于ui的使用方法各位可以看看jquery的网站。
javascript代码
//获取图片大小的方法,在ie下偶尔有问题
var getSizeImg = function(src)
{
var timg = $('<img>').attr('src', src).css({ position: 'absolute', top: '-1000px', left: '-1000px' }).appendTo('body');
var size = [timg.get(0).offsetWidth, timg.get(0).offsetHeight];
try { document.body.removeChild(timg[0]); }
catch (e) { };
return size;
};
//缩放代码
function bigSmall()
{
var size=$(this).attr("id")=="morebig"?0.01:-0.01;
var value=parseFloat($("#txt_Zoom").val());
var temp=value+size;
if(temp<=2)
{
$("#txt_Zoom").val((value+size).toString());
var width=parseInt($("#width").text());
var height=parseInt($("#height").text());
$("#img").css({ width:parseInt(width*temp)+ "px", height:parseInt(height*temp) + "px" });
$("#image").css({ width:parseInt((width*temp))+ "px", height:parseInt((height*temp)) + "px" });
$("#drop_img").css({ width:parseInt((width*temp))+ "px", height:parseInt((height*temp)) + "px" });
$("#txt_width").val($("#img").css("width").replace(/px/,""));
$("#txt_height").val($("#img").css("height").replace(/px/,""));
$(".child").css({left:parseInt($(".child").eq(0).css("left").replace(/px/,""))+size*100+"px"});
}
}
//初始化
$(document).ready(
function()
{
var width = parseInt($("#width").text())//图片的原长宽
var height = parseInt($("#height").text());
//将图片长宽输入textbox中
$("#txt_DropWidth").val($("#drop").css("width").replace("px",""));
$("#txt_DropHeight").val($("#drop").css("height").replace("px", ""));
$("#drop_img").css({left:"-101px",top:"-101px"});//将截取框内的图片移动到适合位置,注意截取框的1px边框
//设置div的拖动功能
$("#image").draggable({ cursor: 'move',
drag: function(e, ui)
{
var self = $(this).data("draggable");
var drop_img = $("#drop_img");
var top = $("#drop_img").css("top").replace(/px/,"");//取出截取框到顶部的距离
var left = $("#drop_img").css("left").replace(/px/,"");//取出截取框到左边的距离
drop_img.css({left:(parseInt(self.position.left)-101)+"px",top:(parseInt(self.position.top)-101)+"px"});//同时移动内部的图片
//drop_img.style.backgroundPosition = (self.position.left - parseInt(left)-1) + 'px ' + (self.position.top - parseInt(top)-1) + 'px';
$("#txt_left").val(99-parseInt($(this).css("left")));
$("#txt_top").val(99-parseInt($(this).css("top")));
}
});
$("#drop_img").draggable(
{ cursor: 'move',
drag: function(e, ui)
{
var self = $(this).data("draggable");
var divimage=$("#image");
//divimage.style.backgroundPosition = parseInt((self.position.left))*300 + 'px ' + parseInt((self.position.top))*300 + 'px';
divimage.css({left:(parseInt(self.position.left)+101)+"px" ,top:(parseInt(self.position.top)+101)+"px"});//同时移动div
$("#txt_left").val(99-parseInt($("#image").css("left")));
$("#txt_top").val(99-parseInt($("#image").css("top")));
}
});
$("#image").css({ opacity: 0.3,backgroundColor:"#fff",width: width + "px", height: height + "px" });
$("#txt_top").val("100");
$("#txt_left").val("100");
$("#txt_width").val(width);
$("#txt_height").val(height);
$(".smallbig").click(bigSmall);
//缩放的代码
$(".child").draggable(
{
cursor:"move",containment:$("#bar"),
drag:function(e,ui)
{
var left=parseInt($(this).css("left"));
var value=1+(left-100)/100;
$("#txt_Zoom").val(value);
$("#img,image").css({ width:parseInt(width*value)+ "px", height:parseInt(height*value) + "px" });
$("#image").css({ width:parseInt(width*value)+ "px", height:parseInt(height*value) + "px"});
$("#drop_img").css({ width:parseInt(width*value)+ "px", height:parseInt(height*value) + "px"});
$("#txt_width").val($("#img").css("width").replace(/px/,""));
$("#txt_height").val($("#img").css("height").replace(/px/,""));
}
});
}
);
而缩放的功能也是运用了jquery ui的拖动功能,按移动的比例计算,并将放大的比例存在textbox中,不过在进行截取时并不需要知道它的值,只要知道放大缩小后的图片的高宽。尤其要注意的是后台的代码只接受int而不能是float,所以在给页面做缩放的时候要把缩放后的图片高宽进行强制转换为int。
缩放功能条:
方便vs2005的朋友使用 .net 2.0版代码下载
总结:
由于刚完成的项目涉及到很多ajax应用,个人觉得web前台的分离尤其重要:Javascript由当年的验证表单功能发展到如今各种匪夷所思的ajax应用,为了实现大规模,可维护,可修改的开发,必须始终贯彻页面行为、结构、表现三者分离的原则,也就是实现javascript、html、css三者的分离。所以将css写入页面内,或者在页面标签里注册事件都是和分离原则相背离的,在项目的前期由于在repeater等模板化控件中用了很多传统的绑定方式,使得整个页面非常的肮脏,到处都是注册的代码,看的人头都晕了,需求一变更,改起页面会让人有砸电脑的冲动(说真的)。
---------分离原则的论述大家可以看看《ppk on javascipt》和《javascript dom高级编程这两本书》,虽然很多人推荐《javascript高级编程》,但是我个人看过后觉得zakas的这本成书时间太早,和当下的最佳实践都有所背离,上面那两本反倒更值得推荐。