正文:
为了使层次分明及便于阅读, 整个解决方案如下:
其中BitmapCutter.Core是图片的服务器端处理程序, 类图为:
简单说明下, 更多说明可查看源码注释 :
Cutter为裁剪对象, 用于存储客户端通过AJAX提交的数据.
Helper为图片处理类, 包括图片翻转(RotateImage()), 图片裁剪(GenerateBitmap()).
Callback为服务器端图片处理类, 通过使用Cutter封装客户端AJAX提交的数据, 然后调用Helper中的方法来完成图片处理.
BitmapScissors是一个HttpHandler, 通过客户端返回的 'action' 来调用Callback中的方法:
注: 此处用了反射来根据'action'值动态调用Callback中的方法, 如不习惯或认为不妥的请自行更改...
客户端AJAX提交的Url为"scissors.axd", 实际为BitmapScissors类(如修改了服务器端处理程序, 请在Web.config中根据实际进行httpHandler节点配置), 本例为:
Ok, 下面开始介绍重点, $.fn.bitmapcutter插件的实现:
先来上张效果图,标记下bitmapcutter中的Dom对象, 额, 喜欢刚大木的漫迷比较多, 就来张洛克昂的吧:
说明:
holder为原图承载容器, 当原图过大时可只显示部分, 通过平移来查看未显示区域的图象, holder为div, 设置position样式为relative, 这样原图img(同为img对象)的position样式为absolute时, 即可通过控制其left, top 值来进行图片的平移显示, 注意holder的overflow必须设置成hidden, 这样img将会被包裹在holder中,超出holder边界的img将会被隐藏.
cutter为截取框, 可进行拖拽以选择自己敢兴趣的截取区域, 拖拽区域在holder容器内, 同时会在thumbimg(原理与holder+img相同)内生成缩略图.
opts - 功能区域, 提供所有可用的功能按钮, 包括: 放大, 缩小, 左移, 右移, 上移, 下移, 还原, 左旋转, 右旋转.
info - 图片相素, 用于显示当前待截取图片的宽高比.
原理:
熟悉css的话应该很容易看出来, 利用相对定位的灵活性和js对css的控制.
插件说明:
由于插件的代码比较多, 这里就拣重点的讲了:
1. 全局变量:
由于在整个裁剪过程中会频繁的使用一些数据和jQuery对象, 所以定义了一个全局变量用来存储.
$originalSize: 原图的相素, 在首次加载原图时获取, 不可改变, 主要是为放大, 缩小和还原提供一对基础数据(长,宽).
$zoomValue: 当前缩放比, 在执行放大或缩小时同步.
$thumbimg: 裁剪区域缩略图的jQuery对象, 在该dom被创建时赋值.
$img: 原图jQuery对象, 在该dom被创建时获取.
$cutter: 裁剪区域, 同样在dom被创建时获取.
2. 插件:
为了方便图象的控制, 同样编写了几个附加插件, 如果同学们在使用中有重名的请设法避免 ;-)
$.fn.f: 用于获取jQuery对象的样式值, 例如width, height, left, top等.
$.fn.loadBitmap: 用于预载入图片以获取正确的相素.
$.fn.scaleBitmap: 用于缩放图片..缩放比率来自全局变量$zoomValue.
$.fn.dragndrop: 我的上一个插件$.fn.Drags的定制版, 主要是为了实现某一dom对象在特定元素内的拖拽.
3. API:
$.fn.bitmapCutter的API包括:
src(String): 待裁剪图片的路径(相对于程序主目录), 默认为空, 必须,
renderTo(String(Selector)|jQuery Object): bitmapCutter插件容器, jQuery对象或选择器, 默认为$(document.body),
holderSize(Object): holder对象的大小, 包括width和height两个值, 默认为{ width: 300, height: 400 },
cutterSize(Object): cutter对象的大小(生成头像大小), 包括width和height两个值, 默认为{ width: 70, height: 70 },
zoomStep(Float): 每次缩放的比率更改幅度, 默认为0.2,
zoomIn(Float): 放大时与原图的最大比, 默认为2.0,
zoomOut(Float): 缩小时于原图的最大比, 默认为0.1,
rotateAngle(Int): 图片翻转角度, 可取值为 90, 180, -90, -180, 默认为 90,
moveStep(Int): 原图平移时的平移象素, 默认为100,
onGenerated(Function): 成功生成头像时触发事件数, bitmapCutter向此方法传递一个参数 'src', 表示新裁剪生成的头像路径. 此属性的默认值为 function(src) { },
lang(Object): 功能区域各按钮的Tooltip, lang的API为:
zoomout(String): 放大, 默认值 'Zoom out',
zoomin(String): 缩小, 默认值'Zoom in',
original(String): 原始大小, 默认值'Original size',
clockwise(String): 顺时针旋转, 默认值'Clockwise rotation({0} degrees)', 需包含 {0} 以便格式化时使用rotateAngle值填充,
counterclockwise(String): 逆时针旋转, 默认值'Counterclockwise rotation({0} degrees)', 需包含 {0} 以便格式化时使用rotateAngle值填充,
generate(String): 生成头像, 默认值'Generate!',
process(String): 生成头像时的提示语, 默认值为'Please wait, transaction is processing......',
left(String): 左平移, 默认值'Left',
right(String): 右平移, 默认值'Right',
up(String): 上移, 默认值'Up',
down(String): 下移, 默认值'Down'
API原型为:
4. 开发技巧:
为了便于代码管理及功能优化, 将功能进行了归纳, 大致可分为:
缩放(zoom): 细分为放大和缩小,
翻转(rotate): 细分为顺时针和逆时针,
移动(move): 细分为上|右|下|左平移.
所以直接编写三个 '基类' (具体实现看源码):
izoom(zv): 提供图片放大或缩小功能, zv为当前缩放比,
irotate(angle): 提供图片旋转功能, angle为当前旋转角度,
imove(direction): 提供原图平移功能, direction为移动方向, 包括'left','up','right','down'.
那么现在需要的就是细分各个功能:
这里我使用了一个对象scissors来封装这些操作, 使用call来 '继承', 当然你也可以使用 '冒充' 或者 '原型链' 这些手段来实现. 这样的话在绑定这些方法的时候使用eval和json就可以很轻松的完成:
看不明白?那么贴上Html树呢?
5. 自问自答
Q: 为什么要在服务器端来实现旋转效果?
A: 为了实现大众化和浏览器兼容, 所以我放弃了滤镜和Canvas.
Q: 포토샵이나 기타 이미지 처리 소프트웨어는 모두 크롭 시 반전 효과를 구현하는데 왜 구현하지 않나요?
A: 프로그램에서 반전, 커터를 구현한다면 효율성도 고려해야 한다고 생각합니다. img도 Thumbimg와 동기화하려면 개체에 내장해야 하므로 이미지 회전 시 메모리 축적이 매우 무섭습니다
Q: Flash나 Silverlight와 비교하면 어떤 장점이 있나요?
A: 음, 사실 원래는 Flash를 사용하려고 했는데 인기 문제를 고려하면(.Net 개발자가 몇 명이나 될까요?) ?), 오픈 API와 오픈 소스 코드를 갖춘 jQuery HttpHandler는 모든 사람에게 .fla를 변경하라고 요청하는 것보다 훨씬 쉽고 즐겁다고 생각합니다.
A: 우리 모두 알고 있듯이 브라우저 캐시는 양날의 검입니다. img의 src에 추가하세요. 스탬프(임의의 숫자? 시간? 무엇이든..), 그러나 이는 브라우저 메모리 축적이라는 불가피한 결과를 가져올 것입니다(이 위협은 지금까지 최소화되었습니다.-)) 음..!@ #$%
gif 형식의 이미지는 특별한 처리가 이루어지지 않으므로 첫 번째 프레임만 가져오고 아바타 형식은 Png가 됩니다. 필요한 경우 소스 코드를 직접 수정하세요.
jquery.bitmapcutter.js의 tail 메서드. C# 문자열 형식을 시뮬레이션하는 것입니다. 필수입니다.
키보드 작동 지원, 화살표 키로 패닝 제어, 확대 및 축소 제어, 마음에 들지 않으면 소스 코드에서 삭제할 수 있습니다.
아바타를 캡처하기 위해 C.C의 사진을 사용하고, 캡처 프레임의 길이와 너비를 120:120으로 설정하고, 컨테이너는 컨테이너 ID를 가진 div이고, 각 회전 각도는 90이며, 프롬프트는 시계 방향 회전 기능 버튼은 '시계 방향으로 회전'{0}도'입니다.
효과: