图片(旋转/缩放/翻转)变换效果(ccs3/滤镜/canvas)

以前要实现图片的旋转或翻转,只能用ie的滤镜来实现,虽然canvas也实现,但ie不支持而且不是html标准。
css3出来后,终于可以用标准的transform来实现变换,而canvas也已成为html5标准的一部分。
css3和html5使web变得越来越强大,各种新奇的技术正等待我们发掘。
本程序分别通过滤镜(ie)、ccs3和canvas来实现图片的旋转、缩放和翻转变换效果,可以用作图片查看器。
有如下特色:
1,用滤镜、ccs3和canvas实现相同的变换效果;
2,可任意角度旋转;
3,可任意角度翻转;
4,可扩展滚轮缩放;
5,可扩展拖动旋转。
兼容:ie6/7/8, firefox 3.6.8, opera 10.6, safari 5.0.1, chrome 5.0


效果预览



程序说明

【基本原理】

变换主要是利用css3的变换样式transform的matrix方法来实现。
ie不支持css3,但有Matrix滤镜也能实现类似的效果。
程序还用canvas,通过画图实现相同的效果。


【模式设置】

程序包含三种模式:css3、filter和canvas,程序初始化时,会根据自定义模式进行模式设置。
各个模式相关的属性和方法都存放在ImageTrans.modes中。
每个模式对象都包含support属性,表示当前浏览器是否支持该模式,还有几个方法:
init:初始化执行程序
load:加载图片执行程序
show:变换显示程序
dispose:销毁程序

使用这样的格式来自定义模式:"css3|filter|canvas",判断过程主要在_initMode程序中:

ExpandedBlockStart.gif 代码
var  modes  =  ImageTrans.modes;
this ._support  =  $$A.some(  this .options.mode.toLowerCase().split( " | " ),  function (mode){
    mode 
=  modes[ mode ];
    
if  ( mode  &&  mode.support ) {
        ...
        
return   true ;
    }
}, 
this  );

程序会按顺序逐个判断,当浏览器支持该模式时就会用该模式的方法设置程序函数。
再用_support记录浏览器是否支持指定的模式。

如果浏览器支持,才执行_initContainer容器初始化程序和_init模式初始化程序。


【加载图片】

完成初始化设置后,再执行load方法加载图片。
在load方法中,主要对图片进行设置:

img.onload  ||  ( img.onload  =   this ._LOAD );
img.onerror 
||  ( img.onerror  =   function (){ oThis.onError( " err image " ); } );

最后设置src加载图片。

图片加载成功后,会执行_LOAD程序,在里面会执行_load加载程序和reset方法。
在reset中,会重置变换参数,并执行_show模式画图程序:

this ._y  =   this ._x  =   1 this ._radian  =   0 ;
this ._show();

这时,图片会以初始状态显示。


【图片变换】

图片加载完成后,就可以对图片进行变换。

各种变换方法保存在ImageTrans.transforms中,包括旋转、垂直翻转、水平翻转、缩放等。
变换结果是通过_y垂直变换参数、_x水平变换参数和_radian旋转变换参数计算得到的。
变换方法就是用来修改这些参数的方法。

一般的翻转是通过把_y或_x取反来实现的,是以图片本身为坐标的翻转。
但用户的习惯是按浏览器坐标来翻转的,当图片先旋转变换坐标后再翻转,就不符合用户习惯了。
为了能按视觉习惯坐标来翻转,程序使用的方法是先旋转再翻转来实现。
例如vertical垂直翻转是图片旋转180度,再进行一般的水平翻转:

this ._radian  =  Math.PI  -   this ._radian;  this ._x  *=   - 1 ;

而horizontal水平翻转也类似:

this ._radian  =  Math.PI  -   this ._radian;  this ._y  *=   - 1 ;

实际也是利用了一些坐标变换原理。

rotate旋转是以弧度为单位的,直接修改_radian参数。
rotatebydegress是以角度为单位的旋转,其实是用degress * Math.PI/180公式把角度转换成弧度。
left向左转90度是在原有弧度减Math.PI/2(即90度),而right向右转90度是加Math.PI/2。

scale缩放就是根据缩放比率分别对_y和_x进行修改。
程序定义了一个getZoom修正缩放比率函数:

function  getZoom(scale, zoom) {
    
return     scale  >   0   &&  scale  >- zoom  ?  zoom :
            scale 
<   0   &&  scale  <  zoom  ?- zoom :  0 ;
}

只有当比率不会导致结果是反值的情况才会返回正确的比率。
这里主要是保证缩到最小时不会因为比率太大得到反值,导致图片翻转并放大。
当垂直和水平比率都不是0,才会对参数进行修改:

var  hZoom  =  getZoom(  this ._x, zoom ), vZoom  =  getZoom(  this ._y, zoom );
if  ( hZoom  &&  vZoom ) {
    
this ._x  +=  hZoom;  this ._y  +=  vZoom;
}

这样可以保证水平和垂直缩放同时进行,保持图片比例。
要注意zoomin放大时要保证缩放比率为正数,zoomout缩小就要负数。

这些方法会在模式设置中扩展到程序中:

$$A.forEach( ImageTrans.transforms,  function (transform, name){
    
this [ name ]  =   function (){
        transform.apply( 
this , [].slice.call(arguments) );
        
this ._show();
    }
}, 
this  );

在执行完变换方法后,就会执行_show来显示变换。


【transform】

变换换流程介绍完,就进入主题,介绍三个变换方法了。
先看看css3的transform,它包含以下变换方法:
matrix:矩阵变形,使用它就可以做到后面所有的变换,但使用相对麻烦;
translate/translateX/translateY:坐标变换,就是移动坐标的意思;
scale/scaleX/scaleY:缩放变换,放大缩小,取负值可以做翻转;
rotate:旋转变换,根据角度旋转;
skew/skewX/skewY:拉扯变换,就是上下或左右向不同的方向扯开的效果。
更详细的介绍请看w3c的CSS 2D Transforms或者MDC的-moz-transform

要进行变换,首先要看看浏览器是否支持:

ExpandedBlockStart.gif 代码
var  style  =  document.createElement( " div " ).style;
return  $$A.some(
    [ 
" transform " " MozTransform " " webkitTransform " " OTransform " " msTransform "  ],
    
function (css){  if  ( css  in  style ) {
        css3Transform 
=  css;  return   true ;
    }});

由于程序运行时文档可能还没载入,所以要手动创建一个元素来测试。
虽然w3c标准使用的是transform,但现阶段各浏览器还是需要各自的前缀。
当元素样式包含其中一个变换样式,说明该浏览器支持,再用css3Transform记录样式名字。

在css3模式的init程序中,会执行initImg方法对图片进行设置:

$$D.setStyle( img, {
    position: 
" absolute " ,
    border: 
0 , padding:  0 , margin:  0 , width:  " auto " , height:  " auto " ,
    visibility: 
" hidden "
});
container.appendChild( img );

主要是设置绝对定位、隐藏和重置样式,最后插入容器。
ps:图片对象虽然是用new Image()创建的,但也可以当作img元素来操作。

在load加载程序中,再对图片样式进行设置:

$$D.setStyle( img, {
    top: ( 
this ._clientHeight  -  img.height )  /   2   +   " px " ,
    left: ( 
this ._clientWidth  -  img.width )  /   2   +   " px " ,
    visibility: 
" visible "
});

设置left和top使图片在容器里居中,并显示图片。

关键的show程序,用来进行图片变换,这里用的是matrix变换。
matrix有6个参数,前4个是矩阵变换参数,后两个是坐标变换参数。
要根据弧度旋转,可以这样设置矩阵:
cos(a) -sin(a)
sin(a)  cos(a)
而缩放变换就这样设置:
sx 0
0  sy
旋转同时缩放,矩阵相乘得到:
cos(a)*sx -sin(a)*sy
sin(a)*sx  cos(a)*sy
关于Matrix矩阵变换可以看w3c的Transform Matrix

getMatrix函数就是通过以上矩阵计算matrix参数:

var  Cos  =  Math.cos(radian), Sin  =  Math.sin(radian);
return  {
    M11: Cos 
*  x, M12: - Sin  *  y,
    M21: Sin 
*  x, M22: Cos  *  y
};


在show程序里面,用getMatrix得到参数后,就可以设置样式:

this ._img.style[ css3Transform ]  =   " matrix( "
    
+  matrix.M11  +   " , "   +  matrix.M21  +   " , "
    
+  matrix.M12  +   " , "   +  matrix.M22  +   " , 0, 0) " ;


在实际使用中,使用matrix可能要设置一堆可读性很差的数字,所以还是推荐用其他变换方法。


【Matrix滤镜】

ie的Matrix滤镜跟css3的matrix差不多,也能实现类似的变换。
Matrix滤镜有以下属性:
Dx/Dy:坐标变换参数;
enabled:是否可用;
FilterType:定义新内容的像素的方法;
M11/M12/M21/M22:矩阵变换参数;
SizingMethod:容器是否改变尺寸去适应目标图像。
详细参考msdn的Matrix Filter中文Matrix说明

判断浏览器是否支持,也是创建一个div,看它有没有"filters"属性:

function (){  return   " filters "   in  document.createElement( " div " ); }()


在init程序中也用initImg方法对图片进行设置,此外还需要设置Matrix滤镜:

this ._img.style.filter  =   " progid:DXImageTransform.Microsoft.Matrix(SizingMethod='auto expand') " ;

这里会设置SizingMethod属性,它有两个值,分别是:clip to original(容器不改变尺寸)和auto expand(容器改变尺寸以适应目标图像)。
如果使用默认值clip to original,容器的大小不会变,那么变换时超出容器的部分就会隐藏不见,所以这里要设为'auto expand'才能完整显示图片。

在load程序中显示图片,为了防止ie重复加载gif的bug还要重置onload为null。
ps:该bug的研究可以参考这里的显示预览部分

而show程序也跟css3模式类似,通过getMatrix方法获取变换参数,并设置到滤镜中:

$$.extend(
    img.filters.item(
" DXImageTransform.Microsoft.Matrix " ),
    getMatrix( 
this ._radian,  this ._x,  this ._y )
);


滤镜使用了'auto expand'后,变换时图片元素尺寸会随着内容变化,所以要重新设置居中:

img.style.top  =  (  this ._clientHeight  -  img.offsetHeight )  /   2   +   " px " ;
img.style.left 
=  (  this ._clientWidth  -  img.offsetWidth )  /   2   +   " px " ;


ie还有其他变换滤镜,例如FlipH(水平翻转)、FlipV(垂直翻转)、Wave(扭曲)等。


【canvas】

通过滤镜和css3已经能在主流的浏览器上兼容实现相同的变换,最后再用canvas来实现。
canvas是一个用来绘制图形的元素,它最强大的地方在于结合js来绘制图形甚至制作动画和游戏。
ps:这里推荐几个入门文章:Opera的HTML 5 canvas深入了解canvas标签系列

要知道浏览器是否支持canvas,可以创建一个canvas元素,看它是否有"getContext"方法来判断:

function (){  return   " getContext "   in  document.createElement( ' canvas ' ); }()

getContext方法用来获取渲染空间(也叫上下文),之后会在这个空间绘图,相当于获取画布。

在init程序中,创建并设置canvas及其上下文context:

ExpandedBlockStart.gif 代码
var  canvas  =   this ._canvas  =  document.createElement( ' canvas ' ),
    context 
=   this ._context  =  canvas.getContext( ' 2d ' );

$$D.setStyle( canvas, { position: 
" absolute " , left:  0 , top:  0  } );
canvas.width 
=   this ._clientWidth; canvas.height  =   this ._clientHeight;
this ._container.appendChild(canvas);

程序中只需要2D canvas,所以用getContext('2d')就行了。
ps:关于3D canvas在Opera的HTML 5 canvas有相关的介绍。
还需要设置尺寸,这里要注意要用width和height来设置,我试过用样式来设置,结果画图不正常。

在show程序中进行变换和画图:

ExpandedBlockStart.gif 代码
context.save();
context.clearRect( 
0 0 , clientWidth, clientHeight );
context.translate( clientWidth 
/   2  , clientHeight  /   2  );
context.rotate( 
this ._radian );
context.scale( 
this ._x,  this ._y );
context.drawImage( img, 
- img.width  /   2 - img.height  /   2  );
context.restore();

这里用到了canvas几种方法:
save:保存状态;
clearRect:清除画布;
translate:水平/垂直移动坐标;
rotate:旋转坐标;
scale:缩放坐标;
drawImage:插入图像;
restore:恢复状态。

canvas没有直接清除整个画布的方法,只能用clearRect来间接实现。
它的四个参数用来定义一个矩形,只要定义一个画布大小的矩形就能清除整个画布了。

在canvas里面没有left/top,位置的变换只能用translate来设置,程序用它把坐标移到画布中心。
canvas没有Matrix那样的东西,只能用rotate和scale来变换,用法跟css3的类似。
canvas也没有css3的skew(拉扯)变换。

最后用drawImage画图,可以是img元素、Image对象或Canvas元素,另外两个参数是画图位置,程序用它来居中图像。

每次画图都可能会改变坐标,下次想还原坐标来画图要逐个恢复会很麻烦,这时应该用save和restore来保存和还原状态。
save和restore能保存和还原包括translate、rotate、scale变换后的画布状态,常常配合clearRect来做动画。

除了以上方法,canvas还有很多属性和方法来绘图,详细可以看HTML DOM CanvasRenderingContext2D 对象


【拖动旋转/滚轮缩放】

拖动旋转效果是在容器按下鼠标并拖动的过程中,图像会跟着鼠标转动。
原理是以容器中点为中心,计算按下鼠标位置的弧度R1和拖动到达位置的弧度R2,两者的差就是根据鼠标要旋转的弧度,再加上原来的弧度R0,即R2-R1+R0,就能得到当前要设置的弧度了。
关键的地方就在于如何获取这个弧度,如果要手动计算这个值会很复杂,好在js提供了Math.atan2(y,x)方法,可以返回由 X 轴到 (y,x) 点的弧度,这样直接用坐标就可以得到弧度。
例如中心坐标是(x,y),某一点的坐标是(x1,y1),可以这样得到那个点相对中心坐标的弧度:Math.atan2(y1-y,x1-x)。

首先在鼠标的mousedown中记录容器的中心坐标:

var  rect  =  $$D.clientRect(  this ._container );
this ._mrX  =  rect.left  +   this ._clientWidth  /   2 ;
this ._mrY  =  rect.top  +   this ._clientHeight  /   2 ;

再根据clientX/clientY当前位置坐标计算弧度:

this ._mrRadian  =  Math.atan2( e.clientY  -   this ._mrY, e.clientX  -   this ._mrX )  -   this ._radian;

这里把原来的弧度也顺便计算了。
在拖动的过程中,根据移动坐标获取弧度,并调用rotate来旋转:

this .rotate( Math.atan2( e.clientY  -   this ._mrY, e.clientX  -   this ._mrX )  -   this ._mrRadian );

ps:拖动效果请看这里关于拖动的研究

滚轮缩放效果就是在容器上滚动鼠标会自动实现缩放效果。
这个比较简单,主要是一些兼容问题,相关研究可以参考这里的鼠标滚动缩放


使用技巧

【模式选择】

模式设置中说明了自定义模式可以用这样的格式:"css3|filter|canvas"。
程序会自动按优先顺序选择支持的模式。
为了尽量保证浏览器支持选择的模式,可以把三个模式都用上。
一般filter是必须的,因为目前ie只支持这个,而css3和canvas可以自行选择。
我在自己的电脑做了效率测试(就是用鼠标狂转图片),看cpu的占用情况:
ff css3 40%
ff canvas 35%
opera css3 30%
opera canvas 35%
chrome css3 25%
chrome canvas 55%
safari css3 20%
safari canvas 55%
看来在ff和opera是canvas好一点,在WebKit就是css3快得多。
但canvas只能画静态图片,对于gif那样的动态图片只能显示一帧,所以还是选css3比较好。

还有css3的变换能用在所有元素中(ie8的滤镜也可以),适用在html的变换,而canvas就更适合做复杂的图形和动画。


【选择图片】

程序实例化之后,还需要调用load方法加载指定的图片。
参数可以是图片的路径,data url(支持的话),ie6还可以用本地路径。
如果是gif图片,就要注意不要用canvas,原因上面也说了,canvas只能显示一帧图片。


使用说明

实例化时,必须有容器对象或id作为参数:

var  trans  =   new  ImageTrans( container );

然后调用load方法加载图片:

trans.load(img);


可选参数用来设置系统的默认属性,包括:
属性:    默认值//说明
mode:  "css3|filter|canvas",
zoom:  .1,//缩放比率
onPreLoad: function(){},//图片加载前执行
onLoad:  function(){},//图片加载后执行
onError: function(err){}//出错时执行
其中zoom、onPreLoad、onLoad、onError属性可以在程序初始化后动态设置。

提供以下变换方法:
vertical:垂直翻转
horizontal:水平翻转
rotate: 旋转
left:向左转90度
right:向右转90度
rotatebydegress:根据角度旋转
scale:缩放
zoomin:放大
zoomout:缩小

还提供了以下方法:
load:加载图片;
reset:重置图像为默认状态;
dispose:销毁程序。

加入拖动旋转扩展程序或滚轮缩放扩展程序后,会自动启用,可以自定义mouseRotate或mouseZoom属性为false来取消。


程序源码 

ExpandedBlockStart.gif 代码
var  ImageTrans  =   function (container, options){
    
this ._initialize( container, options );
    
this ._initMode();
    
if  (  this ._support ) {
        
this ._initContainer();
        
this ._init();
    } 
else  { // 模式不支持
         this .onError( " not support " );
    }
};
ImageTrans.prototype 
=  {
  
// 初始化程序
  _initialize:  function (container, options) {
    
var  container  =   this ._container  =  $$(container);
    
this ._clientWidth  =  container.clientWidth; // 变换区域宽度
     this ._clientHeight  =  container.clientHeight; // 变换区域高度
     this ._img  =   new  Image(); // 图片对象
     this ._style  =  {}; // 备份样式
     this ._x  =   this ._y  =   1 ; // 水平/垂直变换参数
     this ._radian  =   0 ; // 旋转变换参数
     this ._support  =   false ; // 是否支持变换
     this ._init  =   this ._load  =   this ._show  =   this ._dispose  =  $$.emptyFunction;
    
    
var  opt  =   this ._setOptions(options);
    
    
this ._zoom  =  opt.zoom;
    
    
this .onPreLoad  =  opt.onPreLoad;
    
this .onLoad  =  opt.onLoad;
    
this .onError  =  opt.onError;
    
    
this ._LOAD  =  $$F.bind(  function (){
        
this .onLoad();  this ._load();  this .reset();
        
this ._img.style.visibility  =   " visible " ;
    }, 
this  );
    
    $$CE.fireEvent( 
this " init "  );
  },
  
// 设置默认属性
  _setOptions:  function (options) {
    
this .options  =  { // 默认值
        mode:         " css3|filter|canvas " ,
        zoom:        .
1 , // 缩放比率
        onPreLoad:     function (){}, // 图片加载前执行
        onLoad:         function (){}, // 图片加载后执行
        onError:     function (err){} // 出错时执行
    };
    
return  $$.extend( this .options, options  ||  {});
  },
  
// 模式设置
  _initMode:  function () {
    
var  modes  =  ImageTrans.modes;
    
this ._support  =  $$A.some(  this .options.mode.toLowerCase().split( " | " ),  function (mode){
        mode 
=  modes[ mode ];
        
if  ( mode  &&  mode.support ) {
            mode.init 
&&  ( this ._init  =  mode.init); // 初始化执行程序
            mode.load  &&  ( this ._load  =  mode.load); // 加载图片执行程序
            mode.show  &&  ( this ._show  =  mode.show); // 变换显示程序
            mode.dispose  &&  ( this ._dispose  =  mode.dispose); // 销毁程序
             // 扩展变换方法
            $$A.forEach( ImageTrans.transforms,  function (transform, name){
                
this [ name ]  =   function (){
                    transform.apply( 
this , [].slice.call(arguments) );
                    
this ._show();
                }
            }, 
this  );
            
return   true ;
        }
    }, 
this  );
  },
  
// 初始化容器对象
  _initContainer:  function () {
    
var  container  =   this ._container, style  =  container.style, position  =  $$D.getStyle( container,  " position "  );
    
this ._style  =  {  " position " : style.position,  " overflow " : style.overflow }; // 备份样式
     if  ( position  !=   " relative "   &&  position  !=   " absolute "  ) { style.position  =   " relative " ; }
    style.overflow 
=   " hidden " ;
    $$CE.fireEvent( 
this " initContainer "  );
  },
  
// 加载图片
  load:  function (src) {
    
if  (  this ._support ) {
        
var  img  =   this ._img, oThis  =   this ;
        img.onload 
||  ( img.onload  =   this ._LOAD );
        img.onerror 
||  ( img.onerror  =   function (){ oThis.onError( " err image " ); } );
        img.style.visibility 
=   " hidden " ;
        
this .onPreLoad();
        img.src 
=  src;
    }
  },
  
// 重置
  reset:  function () {
    
if  (  this ._support ) {
        
this ._x  =   this ._y  =   1 this ._radian  =   0 ;
        
this ._show();
    }
  },
  
// 销毁程序
  dispose:  function () {
    
if  (  this ._support ) {
        
this ._dispose();
        $$CE.fireEvent( 
this " dispose "  );
        $$D.setStyle( 
this ._container,  this ._style ); // 恢复样式
         this ._container  =   this ._img  =   this ._img.onload  =   this ._img.onerror  =   this ._LOAD  =   null ;
    }
  }
};
// 变换模式
ImageTrans.modes  =   function (){
    
var  css3Transform; // ccs3变换样式
     // 初始化图片对象函数
     function  initImg(img, container) {
        $$D.setStyle( img, {
            position: 
" absolute " ,
            border: 
0 , padding:  0 , margin:  0 , width:  " auto " , height:  " auto " , // 重置样式
            visibility:  " hidden " // 加载前隐藏
        });
        container.appendChild( img );
    }
    
// 获取变换参数函数
     function  getMatrix(radian, x, y) {
        
var  Cos  =  Math.cos(radian), Sin  =  Math.sin(radian);
        
return  {
            M11: Cos 
*  x, M12: - Sin  *  y,
            M21: Sin 
*  x, M22: Cos  *  y
        };
    }
    
return  {
        css3: {
// css3设置
            support:  function (){
                
var  style  =  document.createElement( " div " ).style;
                
return  $$A.some(
                    [ 
" transform " " MozTransform " " webkitTransform " " OTransform "  ],
                    
function (css){  if  ( css  in  style ) {
                        css3Transform 
=  css;  return   true ;
                    }});
            }(),
            init: 
function () { initImg(  this ._img,  this ._container ); },
            load: 
function (){
                
var  img  =   this ._img;
                $$D.setStyle( img, {
// 居中
                    top: (  this ._clientHeight  -  img.height )  /   2   +   " px " ,
                    left: ( 
this ._clientWidth  -  img.width )  /   2   +   " px " ,
                    visibility: 
" visible "
                });
            },
            show: 
function () {
                
var  matrix  =  getMatrix(  this ._radian,  this ._y,  this ._x );
                
// 设置变形样式
                 this ._img.style[ css3Transform ]  =   " matrix( "
                    
+  matrix.M11  +   " , "   +  matrix.M21  +   " , "
                    
+  matrix.M12  +   " , "   +  matrix.M22  +   " , 0, 0) " ;
            },
            dispose: 
function (){  this ._container.removeChild( this ._img); }
        },
        filter: {
// 滤镜设置
            support:  function (){  return   " filters "   in  document.createElement( " div " ); }(),
            init: 
function () {
                initImg( 
this ._img,  this ._container );
                
// 设置滤镜
                 this ._img.style.filter  =   " progid:DXImageTransform.Microsoft.Matrix(SizingMethod='auto expand') " ;
            },
            load: 
function (){
                
this ._img.onload  =   null ; // 防止ie重复加载gif的bug
                 this ._img.style.visibility  =   " visible " ;
            },
            show: 
function () {
                
var  img  =   this ._img;
                
// 设置滤镜
                $$.extend(
                    img.filters.item(
" DXImageTransform.Microsoft.Matrix " ),
                    getMatrix( 
this ._radian,  this ._y,  this ._x )
                );
                
// 保持居中
                img.style.top  =  (  this ._clientHeight  -  img.offsetHeight )  /   2   +   " px " ;
                img.style.left 
=  (  this ._clientWidth  -  img.offsetWidth )  /   2   +   " px " ;
            },
            dispose: 
function (){  this ._container.removeChild( this ._img); }
        },
        canvas: {
// canvas设置
            support:  function (){  return   " getContext "   in  document.createElement( ' canvas ' ); }(),
            init: 
function () {
                
var  canvas  =   this ._canvas  =  document.createElement( ' canvas ' ),
                    context 
=   this ._context  =  canvas.getContext( ' 2d ' );
                
// 样式设置
                $$D.setStyle( canvas, { position:  " absolute " , left:  0 , top:  0  } );
                canvas.width 
=   this ._clientWidth; canvas.height  =   this ._clientHeight;
                
this ._container.appendChild(canvas);
            },
            show: 
function (){
                
var  img  =   this ._img, context  =   this ._context,
                    clientWidth 
=   this ._clientWidth, clientHeight  =   this ._clientHeight;
                
// canvas变换
                context.save();
                context.clearRect( 
0 0 , clientWidth, clientHeight ); // 清空内容
                context.translate( clientWidth  /   2  , clientHeight  /   2  ); // 中心坐标
                context.rotate(  this ._radian ); // 旋转
                context.scale(  this ._y,  this ._x ); // 缩放
                context.drawImage( img,  - img.width  /   2 - img.height  /   2  ); // 居中画图
                context.restore();
            },
            dispose: 
function (){
                
this ._container.removeChild(  this ._canvas );
                
this ._canvas  =   this ._context  =   null ;
            }
        }
    };
}();
// 变换方法
ImageTrans.transforms  =  {
  
// 垂直翻转
  vertical:  function () {
    
this ._radian  =  Math.PI  -   this ._radian;  this ._y  *=   - 1 ;
  },
  
// 水平翻转
  horizontal:  function () {
    
this ._radian  =  Math.PI  -   this ._radian;  this ._x  *=   - 1 ;
  },
  
// 根据弧度旋转
  rotate:  function (radian) {  this ._radian  =  radian; },
  
// 向左转90度
  left:  function () {  this ._radian  -=  Math.PI / 2; },
   // 向右转90度
  right:  function () {  this ._radian  +=  Math.PI / 2; },
   // 根据角度旋转
  rotatebydegress:  function (degress) {  this ._radian  =  degress  *  Math.PI / 180; },
   // 缩放
  scale:  function  () {
    
function  getZoom(scale, zoom) {
        
return     scale  >   0   &&  scale  >- zoom  ?  zoom :
                scale 
<   0   &&  scale  <  zoom  ?- zoom :  0 ;
    }
    
return   function (zoom) {  if ( zoom ){
        
var  hZoom  =  getZoom(  this ._y, zoom ), vZoom  =  getZoom(  this ._x, zoom );
        
if  ( hZoom  &&  vZoom ) {
            
this ._y  +=  hZoom;  this ._x  +=  vZoom;
        }
    }}
  }(),
  
// 放大
  zoomin:  function () {  this .scale( Math.abs( this ._zoom) ); },
  
// 缩小
  zoomout:  function () {  this .scale(  - Math.abs( this ._zoom) ); }
};


拖动旋转扩展

ExpandedBlockStart.gif 代码
ImageTrans.prototype._initialize  =  ( function (){
    
var  init  =  ImageTrans.prototype._initialize,
        methods 
=  {
            
" init " function (){
                
this ._mrX  =   this ._mrY  =   this ._mrRadian  =   0 ;
                
this ._mrSTART  =  $$F.bind( start,  this  );
                
this ._mrMOVE  =  $$F.bind( move,  this  );
                
this ._mrSTOP  =  $$F.bind( stop,  this  );
            },
            
" initContainer " function (){
                $$E.addEvent( 
this ._container,  " mousedown " this ._mrSTART );
            },
            
" dispose " function (){
                $$E.removeEvent( 
this ._container,  " mousedown " this ._mrSTART );
                
this ._mrSTOP();
                
this ._mrSTART  =   this ._mrMOVE  =   this ._mrSTOP  =   null ;
            }
        };
    
// 开始函数
     function  start(e){
        
var  rect  =  $$D.clientRect(  this ._container );
        
this ._mrX  =  rect.left  +   this ._clientWidth  /   2 ;
        
this ._mrY  =  rect.top  +   this ._clientHeight  /   2 ;
        
this ._mrRadian  =  Math.atan2( e.clientY  -   this ._mrY, e.clientX  -   this ._mrX )  -   this ._radian;
        $$E.addEvent( document, 
" mousemove " this ._mrMOVE );
        $$E.addEvent( document, 
" mouseup " this ._mrSTOP );
        
if  ( $$B.ie ) {
            
var  container  =   this ._container;
            $$E.addEvent( container, 
" losecapture " this ._mrSTOP );
            container.setCapture();
        } 
else  {
            $$E.addEvent( window, 
" blur " this ._mrSTOP );
            e.preventDefault();
        }
    };
    
// 拖动函数
     function  move(e){
        
this .rotate( Math.atan2( e.clientY  -   this ._mrY, e.clientX  -   this ._mrX )  -   this ._mrRadian );
        window.getSelection 
?  window.getSelection().removeAllRanges() : document.selection.empty();
    };
    
// 停止函数
     function  stop(){
        $$E.removeEvent( document, 
" mousemove " this ._mrMOVE );
        $$E.removeEvent( document, 
" mouseup " this ._mrSTOP );
        
if  ( $$B.ie ) {
            
var  container  =   this ._container;
            $$E.removeEvent( container, 
" losecapture " this ._mrSTOP );
            container.releaseCapture();
        } 
else  {
            $$E.removeEvent( window, 
" blur " this ._mrSTOP );
        };
    };
    
return   function (){
        
var  options  =  arguments[ 1 ];
        
if  (  ! options  ||  options.mouseRotate  !==   false  ) {
            
// 扩展钩子
            $$A.forEach( methods,  function ( method, name ){
                $$CE.addEvent( 
this , name, method );
            }, 
this  );
        }
        init.apply( 
this , arguments );
    }
})();


滚轮缩放扩展 

ExpandedBlockStart.gif 代码
ImageTrans.prototype._initialize  =  ( function (){
    
var  init  =  ImageTrans.prototype._initialize,
        mousewheel 
=  $$B.firefox  ?   " DOMMouseScroll "  :  " mousewheel " ,
        methods 
=  {
            
" init " function (){
                
this ._mzZoom  =  $$F.bind( zoom,  this  );
            },
            
" initContainer " function (){
                $$E.addEvent( 
this ._container, mousewheel,  this ._mzZoom );
            },
            
" dispose " function (){
                $$E.removeEvent( 
this ._container, mousewheel,  this ._mzZoom );
                
this ._mzZoom  =   null ;
            }
        };
    
// 缩放函数
     function  zoom(e){
        
this .scale((
            e.wheelDelta 
?  e.wheelDelta  /  ( - 120 ) : (e.detail  ||   0 /   3
        ) 
*  Math.abs( this ._zoom) );
        e.preventDefault();
    };
    
return   function (){
        
var  options  =  arguments[ 1 ];
        
if  (  ! options  ||  options.mouseZoom  !==   false  ) {
            
// 扩展钩子
            $$A.forEach( methods,  function ( method, name ){
                $$CE.addEvent( 
this , name, method );
            }, 
this  );
        }
        init.apply( 
this , arguments );
    }
})();

 

完整实例下载

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值