開發Canvas 繪畫應用(四):實現拖拽繪畫

開發Canvas繪畫應用(三):實現對照繪畫中,咱們實現了視圖引導的第一部分,這一篇咱們來完成第二部分,即將圖片直接拖到畫布上進行繪畫。javascript

✁ 拖放如何實現?

【拖放的基本概念】:建立一個絕對定位的元素,使其能夠用鼠標或手指移動。css

注意,爲了使元素能被拖放,它必須是絕對定位的。html

而後,咱們須要填充咱們的 touchF 函數來實現拖動功能,添加了 this.dragging 用於判斷是不是拖動狀態,只有當 touchmove 觸發的時候才爲 true。另外,當拖動的時候,須要改變目標對象的位置,經過 clientXclientY 來進行更改。java

touchF(e) {
    e.preventDefault(); // 阻止瀏覽器默認行爲
    const touches = e.changedTouches;
    const point = touches[0];
    let el = e.target,
        $el = $(e.target);

    switch (e.type) {
        case 'touchstart':

            // 觸摸點起始座標,不帶單位
            this.p_start = {
                x: point.clientX,
                y: point.clientY
            }

            // 圖片起始座標,由於帶單位,因此用parseFloat進行轉換
            this.el_start = {
                x: parseFloat($el.css('left')),
                y: parseFloat($el.css('top'))
            };

            break;
        case 'touchmove':
            this.dragging = true;

            // 觸摸點移動座標差值,不帶單位
            let diffX = point.clientX - this.p_start.x,
                diffY = point.clientY - this.p_start.y;

            if (this.dragging) {

                // 隨觸摸點座標更改目標元素的座標
                $el.css({
                    'left': this.el_start.x + diffX,
                    'top': this.el_start.y + diffY
                });
            }

            break;
        case 'touchend':
            if (!this.dragging) {
                this.setStyle($(e.target)); // 切換視圖顯示狀態
                this.setBasePlate(e.target); // 切換底板顯示狀態
            }

            this.dragging = false;

            break;
        default:
            this.dragging = false;

            break;
    }
}

▶▶▶ 在獲取視圖對象的座標位置時,除了上述用到的 css() 方式,還有下面兩種:jquery

// 採用jquery的offset方法獲取座標,注意裏面的屬性不是x和y,而是left和top
this.el_start = this.$el.offset();

// 又或者採用getBoundingClientRect來獲取座標,也要注意left和top屬性
this.el_start = this.el.getBoungdingClientRect();

可是這裏有一個大坑!!!也是咱們不採用後兩種方式,而採用 css() 方式的緣由,這跟元素在css中如何定位有關,下面來看看這個坑。git

咱們當前視口的大小是 980×874,有兩種css方式能夠將元素居中定位,可是獲取位置時會有區別:github

【方式1】:正確的打開方式canvas

position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);

經過不一樣的方式獲取座標:瀏覽器

console.log('經過css()方式獲取:');
console.log({
    left: parseFloat($el.css('left')),
    top: parseFloat($el.css('top'))
});

console.log('經過offset()方式獲取:');
console.log($el.offset());

console.log('經過getBoungdingClientRect()方式獲取:');
console.log(el.getBoundingClientRect());

座標坑

而在實現中,經過第一種css()方法獲取座標時進行對象移動是正常的,後兩種都會在移動時將圖片往左偏移100像素,爲何呢?由於在css中獲取的就是元素的left值,即經過 left:50% 後偏離的 490px,是不包含transform 變換的,所以纔會多出來100像素。app

【方式2】:求解答

margin: auto;
top: 20px;
left: 0;
right: 0;
z-index: 3;

一樣經過不一樣的方式獲取座標獲得:

座標坑2

這裏有個坑,就是左右移動的時候,當距離增大時,觸摸點跟圖片的距離會愈來愈增大,即圖片移動的速度更不上觸摸點移動的速度,上下移動時是好的,求大神解答。。。。

▲▲▲ 所以,爲何 css() 方式能夠實現咱們的正常不偏移位置的拖動,由於咱們用的是絕對定位!!而在更改元素座標時,採用的是加上座標差的方式,即在原先 left 值的基礎上加上偏移量。好吧,得認可這一塊坑死我了T^T。

AnyWay,附上實現效果:

拖動效果實現

✁ 克隆對象

當前,咱們移動的是視圖自己,可是咱們想要本來的視圖不動,移動它的一個複製圖片,這就須要克隆一個當前對象,在 touchstart 中進行實現:

// 克隆一個對象
this.$clone = $el.clone();
this.$clone.insertBefore($el.siblings()[0]).css({
    'z-index': 4,
    'border': 'none'
});

而後咱們對這個克隆對象進行位置操做即可。

克隆對象

✁ 判斷是否拖入畫布

  • 接着,咱們須要判斷是否將圖片拖入畫布,這須要對畫布的座標進行判斷;
  • 而後,當拖入的時候,在畫布上調用 drawImage() 方法進行繪畫,不然,這個克隆對象將回到原始位置,和目標圖片重合;
  • 最後,不管是否進入,當 touchend 觸發時,都須要銷燬這個克隆對象。

① 整理邏輯,在 touchend 時進行判斷:

case 'touchend':

    // 獲取克隆元素的寬高及座標
    let clone_rect = (this.$clone)[0].getBoundingClientRect();

    if (!this.dragging) {
        this.setStyle($el); // 切換視圖顯示狀態
        this.setBasePlate(el); // 切換底板顯示狀態

        // 若是進入畫布
    } else if (this.intoPainter(clone_rect)) {
        this.setBasePlate(); // 清空底板
        this.drawResult(el); // 在painter上進行繪畫

        // 不然回到初始狀態
    } else {

    }

    this.dragging = false;
    this.$clone.remove(); // 移除clone對象

    break;

② 完善 intoPainter 函數,判斷是否進入畫布

/**
 * 判斷是否進入了 painter 畫布
 * @param  {[object]} srcRect 進入畫布對象的大小及在視口中的座標信息
 * @return {[boolean]}   
 */
intoPainter(srcRect) {
    const rect = this.painter.getBoundingClientRect();

    // 上下左右邊界判斷
    let cL = srcRect.left > rect.left,
        cT = srcRect.top > rect.top,
        cR = srcRect.right < rect.right,
        cB = srcRect.bottom < rect.bottom;

    return cL && cT && cR && cB;
}

③ 在 pinter.js 中完善 drawResult() 函數:

/**
 * 繪製拖入的圖片
 * @param  {[type]} image 視圖對象,原生js <img>
 * @return {[type]}       [description]
 */
drawResult(image) {
    this.clearBg(); // 清除畫布
    this.ctx.drawImage(image, 0, 0, this.config.cvaW, this.config.cvaH);
}

拖入畫上

✈ Github:paintApp

✎ 參考:

javascript小實例,移動端頁面中的拖拽

移動端拖拽的實現效果

HTML5 移動端div塊跟隨手指拖動

相關文章
相關標籤/搜索