在使用hammer.js開發移動終端的繪圖類App時,我們需要用手指來處理畫布的縮放/旋轉和移動。
一般來說我們會考慮到使用 DOM 的CSS3 屬性transform來操作。比如:
scale(x,y)
translate(x,y)
rotate(d)
但是編碼時,會有不少坑。如:
坑1:分別處理scale/rotate等時,影響其他屬性。
分別處理touch的rotatemove,pinchmove事件時,如果我們只是操作:
$(“#id”).css(“transform”,”rotate(“ + e.rotation + “deg)”);
會發現角度雖然改變了,但是縮放又變回了原值。
同樣,在pinchmove中處理scale時也是如此。
開始時,想使用matrix來處理這個問題,後來涉及到較複雜的計算就放棄了(總之沒達到我要的效果,也不知是計算的問題還是調用次序的問題)。
後來發現,其實可以同時操作,寫法是在每個函數中增加空格。如:
$(“#id”).css(“transform”,”translate(“ + e.deltaX + “px,” + e.deltaY + “px) scale(“ + e.scale + “,” + e.scale + “) rotate(“ + e.rotation + “deg)”);
坑2:需要注意的是,不要忘了在相應的數值後加上deg和px,否則會執行錯誤。
坑3:手指捏放時需要將e.scale的作用減弱,否則會出現縮放很快的情況。
我使用的方法是連續開3次平方根:
var scale = Math.sqrt(Math.sqrt(Math.sqrt(e.scale)));
這樣處理後感覺縮放速度感受更好了。
坑4:scale值是相對於canvas初始值的,這個不算很坑,但需要了解。
坑5:每次旋轉時,需要考慮旋轉開始時的角度,要根據當前的canvas角度做相應處理,否則會產生跳動。
代碼如下,自己體會吧:
var initAngle = 0;
var preAngle = 0;
var tempAngleFlag = 0;
var deltaAngle = 0;
var startRotateAngle = 0;
在手勢處理的switch中,略去無關代碼
…
case "rotatestart":
//當每次rotatestart時,e.rotation不從0開始。
startRotateAngle = e.rotation.toFixed(2);
tempAngleFlag = 0;
break;
case "rotatemove":
//轉動的角度
if (cvsObj.obj != null) {
if(tempAngleFlag == 0){
preAngle = startRotateAngle;
tempAngleFlag ++;
}else{
deltaAngle = e.rotation - preAngle;
cvsObj.current.rotate = initAngle + deltaAngle; //cvsObj.current.rotate 用來記錄當前canvas對象的角度
cvsObj.current.rotate = cvsObj.current.rotate % 360; //防止超出360
}
scale = "scale(" + cvsObj.scale + "," + cvsObj.scale + ")";
rotate = "rotate(" + cvsObj.current.rotate + "deg)";
translate = "translate(" + e.deltaX + "px," + e.deltaY + "px)";
style = translate + " " + scale + " " + rotate;
$("#id").css("transform",style);
}
break;
case "rotateend":
initAngle = cvsObj.current.rotate;
break;
…
閱讀更多 午間畫道 的文章