手寫原生代碼專題 | 圖片拖拽效果(一)

一、系列介紹
前端的小伙伴們,我想大多數(shù)都是顏值控吧,看到一個漂亮或新奇的效果,都想搞明白是怎么實現(xiàn)的吧。但是前端發(fā)展的實在太快,各種框架和組件五花八門,由于項目業(yè)務(wù)時間的問題,我們都習(xí)慣了使用各種框架和組件去實現(xiàn),以至于離開這些東西,我們有可能連個最基礎(chǔ)的動效都不清楚怎么實現(xiàn),這就是我想寫這個系列文章的原因,除了這個原因,也是方便小編自己總結(jié)和梳理,日后用到時方便查閱。總之練習(xí)這些基礎(chǔ)的項目并不low,放低姿態(tài),從最基礎(chǔ)的原生代碼開始復(fù)習(xí)實踐,幫助我們梳理基礎(chǔ)知識,日積月累,一定會有不少收獲。
本系列文章小編將和大家一起從最基礎(chǔ)的原生代碼實踐,做一些小的項目,從最基礎(chǔ)的實踐中復(fù)習(xí)和掌握前端的一些基礎(chǔ)知識,只有熟練了才能理解前端的本質(zhì),學(xué)習(xí)前端新的知識和框架時就能更快的上手。本專題文章會用到 ES6、css3的特性來實踐目前比主流的交互特效。
二、 圖片拖拽效果介紹
本篇文章,如下視頻所示,界面有5個方格拖放區(qū)域,我們可以在這些區(qū)域里拖拽圖片,當鼠標拖動圖片時,圖片周圍有灰色的粗邊框效果提示用戶當前元素可拖動,在可放置圖片的目標方格會出現(xiàn)白色的虛線邊框并且背景色更改為黑色,提示用戶可以在此位置放置被拖動的圖片,結(jié)束拖動時(松開鼠標時),圖片將會放置在目標方格內(nèi)。
三、拖拽相關(guān)知識復(fù)習(xí)
在練習(xí)前,我們先復(fù)習(xí)下和拖拽相關(guān)的幾個API事件,在某個元素被拖動時,會按照順序觸發(fā)以下事件:
-
dragstart(按住鼠標不放,剛開始拖動元素時,就會觸發(fā) dragstart 事件) -
drag(dragstart 事件觸發(fā)后,只要元素還在被拖動時,就會持續(xù)觸發(fā) drag 事件,類似 mouseover,隨著鼠標移動而不斷觸發(fā)) -
dragend(當拖動元素的動作停止時即松開鼠標時,放到目標位置或非目標位置,都會觸發(fā)此事件)
以上三個事件,都是針對被拖動元素的,并不會改變被拖動元素的外觀,如果你想改變外觀需要自己定義。
除了這些事件,當你把元素拖動到一個有效的放置目標上時,會依次觸發(fā)以下事件:
-
dragenter(只要被拖動元素進入目標位置上,就會立即觸發(fā)) -
dragover(dragenter事件觸發(fā)后,會立即觸發(fā)此事件,如果被拖動元素,還在目標元素內(nèi)持續(xù)拖動,會持續(xù)觸發(fā)此事件) -
dragleave 或 drop(當被拖動的元素,放置在目標之外,dragover事件就會立即停止,觸發(fā)dragleave事件;如果被拖動元素被放到了目標上,則會觸發(fā)drop事件)
四、開始編寫代碼
復(fù)習(xí)完基礎(chǔ)知識后,我們來開始動手實踐吧,我們依次創(chuàng)建3個文件 index.html,style.css,script.js,然后在 index.html 文件里引入樣式和腳本文件,接下來我們開始編寫代碼吧!
1、編寫HTML代碼
html代碼文件比較簡單,我們依次創(chuàng)建5個div方格,并將被拖動的圖片元素容器初始化放置在第一個方格內(nèi),并在元素上添加可拖動屬性 draggable 值為 true,表示此元素可被拖動(可調(diào)用拖拽API),示例代碼如下:
<divclass="empty">
<divclass="fill"draggable="true"></div>
</div>
<divclass="empty"></div>
<divclass="empty"></div>
<divclass="empty"></div>
<divclass="empty"></div>
2、編寫CSS樣式
接下來,我們來編寫相關(guān)的CSS樣式,代碼很簡單,這里只是簡單說明下:
-
首先我們先定義全局樣式,讓五個方格水平垂直居中,這里我們使用flex彈性盒子布局; -
接下來我們定義五個方格樣式:寬高150px,背景元素為白色,邊框為黑色; -
被拖動的圖片樣式:寬高145px,圖片路徑我們調(diào)用了unsplash.com 提供的圖片服務(wù),可以按照圖片大小隨機圖片,在我們需要圖片測試數(shù)據(jù)時,這個服務(wù)非常有用; -
為了讓用戶比較直觀的感受哪個元素正在被拖動,我們定義元素被拖動的外觀樣式,給圖片定義5px寬的灰色邊框。 -
在拖動至目標位置元素時,為了讓用戶更直觀的感受到哪些位置是可以放置的目標元素,我們需要給其定義 hovered 樣式,進入目標位置元素時,樣式發(fā)生改變,背景為黑灰色,并有白色的邊框虛線。 -
為了適應(yīng)小屏幕,將五個方格由水平排列更改為垂直居中排列。
以上就是我們編寫樣式代碼的思路,相關(guān)的CSS代碼如下:
*{
box-sizing: border-box;
}
body{
background-color: steelblue;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
margin: 0;
}
.empty{
height: 150px;
width: 150px;
margin: 10px;
border: solid 3px black;
background-color: white;
}
.fill{
background-image: url("https://source.unsplash.com/random/150x150");
height: 145px;
width: 145px;
cursor: pointer;
}
.hold{
border: solid 5px #ccc;
}
.hovered{
background-color: #333;
border-color: white;
border-style: dashed;
}
@media(max-width: 800px) {
body{
flex-direction: column;
}
}
3、編寫JS腳本
最后我們來編寫腳本代碼,在編寫前,我們需要提前規(guī)劃思考下,具體思路如下:
-
首先定義兩個DOM對象變量fill和empties,一個代表被拖動的圖片對象,一個是可放置元素的目標對象(數(shù)組對象)。 -
在被拖動的圖片元素上,綁定 dragstart 和 dragend事件。 -
在可被放置圖片的目標元素進行循環(huán)迭代,依次綁定 dragenter、dragover、dragleave、drop 事件。 -
接下來我們分別來定義相關(guān)事件函數(shù), dragstart :當圖片目標剛被拖動時,我們?yōu)樵靥砑踊疑拇诌吙驅(qū)傩?.hold,并將當前此元素的容器背景div隱藏,這里使用樣式 invisible。 -
鼠標放下時,拖拽動作結(jié)束,觸發(fā)dragend事件,我們定義 dragEnd() 函數(shù),將圖片元素的容器樣式更改為fill。 -
接下來,我們來定義拖動至目標位置元素觸發(fā)的相關(guān)事件函數(shù),進入目標元素時,觸發(fā) dragEnter:阻止默認的瀏覽器行為,為其添加進入目標位置的元素樣式 .hovered;在目標位置元素移動拖動元素時的 dragOver 函數(shù):阻止瀏覽器的默認行為;當元素離開目標位置時 dragLeave,我們需要將當前元素的樣式更改為原始的樣式 empty;最后定義 dragDrop 函數(shù),用戶在目標位置放置拖動的圖片元素,松開鼠標時觸發(fā),我們先將當前位置的樣式更改為empty,并在其中添加拖動的圖片元素容器。
思路就聊到這里,下面給出腳本相關(guān)的代碼,示例代碼如下:
constfill=document.querySelector(".fill");
constempties=document.querySelectorAll(".empty");
fill.addEventListener('dragstart',dragStart);
fill.addEventListener('dragend',dragEnd);
for(constemptyofempties){
empty.addEventListener('dragenter',dragEnter);
empty.addEventListener('dragover',dragOver);
empty.addEventListener('dragleave',dragLeave);
empty.addEventListener('drop',dragDrop);
}
functiondragStart(){
this.className += " hold";
setTimeout(()=>this.className='invisible',0)
}
functiondragEnd(){
this.className='fill'
}
functiondragOver(e){
e.preventDefault();
}
functiondragEnter(e){
e.preventDefault();
this.className += " hovered"
}
functiondragLeave(){
this.className='empty'
}
functiondragDrop(){
this.className="empty"
this.append(fill)
}
結(jié)束語
好了,今天的項目就到這里結(jié)束了,想必大家都熟悉了拖拽相關(guān)的事件和如何應(yīng)用,有了這些基礎(chǔ)后,我們就有了寫出更復(fù)雜拖拽應(yīng)用的基礎(chǔ)。最后大家可以點擊閱讀原文體驗交互效果(在PC端體驗),如果想獲取本案例源碼,請關(guān)注「前端達人」公眾號,回復(fù) “a01” 獲取本項目源碼。
