騰訊 AlloyCrop 1.0 釋出

當耐特發表於2017-08-02

寫在前面

AlloyCrop 這個專案是8個月前釋出的,作為AlloyFinger 的典型案例,釋出之後被BAT等其他公司廣泛使用。但是釋出之後,有兩個問題一直沒有抽出時間去解決:

  • 裁剪影象的解析度太小,是否可配?
  • pinch雙指放大的時候,放大的中心不是雙指中心,是否可以優化?

現在很高興地告訴大家,AlloyCrop 已經完全搞定了上面兩個問題,本文將解釋新版本的變化和AlloyCrop背後的原理。當然AlloyFinger的原理這裡就不再闡述,以前有分享過 超小Web手勢庫AlloyFinger原理

先看全新的API變化。

API

new AlloyCrop({
    image_src: "img src",
    circle: true, // optional parameters , the default value is false
    width: 200, // crop width
    height: 100, // crop height
    output: 2, // output resolution --> 400*200
    ok: function (base64, canvas) { },
    cancel: function () { },
    ok_text: "yes", // optional parameters , the default value is ok
    cancel_text: "no" // optional parameters , the default value is cancel
});複製程式碼
引數 是否必填 意義
image_src 必須 需要裁剪圖片的src地址
circle 不必須,預設是false 代表選取的是否是圓形還是矩形,預設是矩形,注意:圓形選取裁剪出來的也是正方形圖片
width 必須 選區的寬
height 必須 選區的高
output 必須 輸出的倍率。比如如果output為2,選區的寬300,選區的高100,輸出的影象的解析度為 (2×300,2×100)
ok 必須 點選ok按鈕的回撥
cancel 必須 點選cancel按鈕的回撥
ok_text 不必須,預設是ok ok按鈕的文字
cancel_text 不必須,預設是cancel cancel按鈕的文字

與之前版本最主要的變化就是新增了 output 支援自定義倍率解析度的影象輸出。

output原理

crop: function () {
    this.calculateRect();
    this.ctx.drawImage(this.img, this.crop_rect[0], this.crop_rect[1], this.crop_rect[2], this.crop_rect[3], 0, 0, this.canvas.width, this.canvas.height);

},複製程式碼

其中 this.calculateRect() 是計算選取和圖片重疊在一起的矩形區域,drawImage 是把裁剪的區域繪製到 canvas 上。注意 canvas 的寬高是多少?且看:

this.canvas.width = option.width * this.output;
this.canvas.height = option.height * this.output;複製程式碼

所以就達到了自定義倍率解析度的目的。當然這裡圖片的失真又或者超分辨,都取決於 drawImage 插值過程。關於插值,以前特意對比過,使用三次卷積插值完爆了其他幾個,但是三次卷積插值速度也是最慢,所以瀏覽器核心要權衡效率和插值結果去實現 drawImage。

img
img

calculateRect計算裁剪區域

因為我們需要把圖片的某個區域繪製到整個canvas上。所以drawImage的後四個引數為(0, 0, this.canvas.width, this.canvas.height),然後我們需要去計算圖片裁剪的區域。

pv
pv

大概就分上面兩種情況,一種是完全包含,一種部分相交。

因為圖片會被放大或者縮小(scale),所以情況會變得稍微複雜一點點。求出相交的矩形區域後,要對圖片scale進行校正,校正回到1的比例,才能用於drawImage。具體程式碼參見 github.com/AlloyTeam/A…

pinch 縮放優化

使用AlloyCrop是可以放大或者縮小再進行裁剪,怎麼基於 pinch 的兩個手指的中間進行放大呢?所以的祕密都在這個multipointStart裡。

  • multipointStart是AlloyFinger丟擲的多手指開始碰到螢幕的回撥函式,通過evt.touches拿到前兩個手指的座標去計算中心座標
  • 重置 originX 和 originY 到兩手指的中心
  • 再重置 translateX 和 translateY 去抹平 originX和originY變更帶來的位移
 new AlloyFinger(this.croppingBox, {
    multipointStart: function (evt) {
        //reset origin x and y
        var centerX = (evt.touches[0].pageX + evt.touches[1].pageX) / 2;
        var centerY = (evt.touches[0].pageY + evt.touches[1].pageY) / 2;
        var cr = self.img.getBoundingClientRect();
        var img_centerX = cr.left + cr.width / 2;
        var img_centerY = cr.top + cr.height / 2;
        var offX = centerX - img_centerX;
        var offY = centerY - img_centerY;
        var preOriginX = self.img.originX
        var preOriginY = self.img.originY
        self.img.originX = offX / self.img.scaleX;
        self.img.originY = offY / self.img.scaleY;
        //reset translateX and translateY
        self.img.translateX += offX - preOriginX * self.img.scaleX;
        self.img.translateY += offY - preOriginY * self.img.scaleX;
        self.initScale = self.img.scaleX;
    },
    pinch: function (evt) {
        self.img.scaleX = self.img.scaleY = self.initScale * evt.zoom;
    },
    pressMove: function (evt) {
        self.img.translateX += evt.deltaX;
        self.img.translateY += evt.deltaY;
        evt.preventDefault();
    }
});複製程式碼
  • 注意,translateX, translateY, translateZ, scaleX, scaleY, scaleZ, rotateX, rotateY, rotateZ, skewX, skewY, originX, originY, originZ 都是 css3transform 類庫 注入到DOM元素上的屬性。

Preview

Preview
Preview

Demo

Dependencies

License

This content is released under the MIT License.

相關文章