在開發Canvas繪畫應用(三):實現對照繪畫中,咱們實現了視圖引導的第一部分,這一篇咱們來完成第二部分,即將圖片直接拖到畫布上進行繪畫。javascript
【拖放的基本概念】:建立一個絕對定位的元素,使其能夠用鼠標或手指移動。css
注意,爲了使元素能被拖放,它必須是絕對定位的。html
而後,咱們須要填充咱們的 touchF
函數來實現拖動功能,添加了 this.dragging
用於判斷是不是拖動狀態,只有當 touchmove
觸發的時候才爲 true
。另外,當拖動的時候,須要改變目標對象的位置,經過 clientX
和 clientY
來進行更改。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;
一樣經過不一樣的方式獲取座標獲得:
☹ 這裏有個坑,就是左右移動的時候,當距離增大時,觸摸點跟圖片的距離會愈來愈增大,即圖片移動的速度更不上觸摸點移動的速度,上下移動時是好的,求大神解答。。。。
▲▲▲ 所以,爲何 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); }