目錄
- 一、拖拽效果示例
- 二、CSS實現原理
- 三、CSS實現細節
- 四、CSS實現布局
- 五、CSS實現其他功能
- 六、說明和小結
發揮你的想象,CSS也能實現拖拽效果。
一、拖拽效果示例
這是移動端很常見的一個效果,可以按住拖來拖去,比如下面的 起點中文網 [1] 觸屏版:

這類效果用JS可以很容易實現,無非就是多了一些計算,多考慮了一些臨界場景,然后代碼量也多了一些。不過,經過我的一番腦洞,發現CSS也能幾乎實現這一效果,接著往下看。
二、CSS實現原理
在傳統 web 中,頁面滾動是一個很常見交互,操作上就是 利用鼠標滾輪或者直接拖動滾動條 。但是,移動端可不一樣, 直接用手指拖動頁面就可以滾動了。 通常頁面是要么垂直方向滾動,要么水平方向滾動, 如果兩個方向都可以滾動呢? 例如
.dragbox{
width: 300px;
height: 300px;
overflow: auto
}
.dragcon{
width: 500px;
height: 500px;
}
只需要內部元素寬高都大于容器就實現兩個方向的滾動了(記得設置 overflow:auto ),示意如下

一般情況下,鼠標滾輪只能同時滾動一個方向(按住 Shift 可以滾動另一方向),但是移動端可以 直接拖著內容任意滾動 ,如下所示
現在,在內容中間添加一個元素,跟隨內容區域一起滾動

接下來,把后面的文本隱藏起來

是不是有點 拖拽 的味道了?原理就是這么簡單!
三、CSS實現細節
首先確定拖拽目標與拖拽容器的尺寸關系,假設拖拽目標的尺寸是 w * h ,那么很容易得出的尺寸關系為: 內部尺寸是容器的2倍減去拖拽目標的尺寸
.dragbox{
width: 100%;
height: 100%;
}
.dragcon{
width: calc(200% - w);
height: calc(200% - h);
}
用一張動圖描述如下(中間的橙色塊塊表示拖拽目標)

四、CSS實現布局
接下來需要把這個特性加入到頁面當中,這里列舉了兩種布局
1. fixed 定位
現在直接把剛才的布局添加到頁面上,并添加 fixed 定位
<body>
...頁面上的其他元素
<div class=“dragbox”>
<div class=“dragcon”></div>
<div class=“ball”></div> <!--拖拽元素-->
</div>
</body>
關鍵樣式如下
.dragbox{
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: auto
}
層級示意關系如下

這樣一來, dragbox 肯定把頁面原有的部分遮擋了,所以還需要添加 pointer-events: none; 同時在拖拽時添加 pointer-events: all
.dragbox{
/*...*/
pointer-events: none;
}
.ball{
/*...*/
pointer-events: all;
}
.dragbox.active{
/*...*/
pointer-events: all;
}
借助 JS 可以在按下時觸發外層容器滾動
ball.addEventListener('touchstart',(ev)=>{
dragbox.classList.add('active');
})
document.addEventListener('touchend',()=>{
dragbox.classList.remove('active');
})
實際效果如下

完整代碼可訪問https://codepen.io/xboxyan/pen/PobwxBK(PC訪問請打開移動端模式)
也可直接掃描以下二維碼

1. absolute 定位 + 層級
前面一種布局由于 fixed 定位層級的影響,不得不借助 JS 來動態改變容器的狀態,有沒有什么辦法可以實現 既可以拖拽,又不影響原有頁面 呢?下面來看這一種布局,用到了 ab s olute 定位
這里需要對原有頁面包裹一層 div 容器,如下
<body>
<div class=“dragbox”>
<div class=“dragcon”></div>
<div class=“ball”></div>
</div>
<div class=“body”> <!--單獨用一層實現頁面滾動-->
...頁面上的其他元素
</div>
</body>
關鍵樣式如下
.dragbox{
position: absolute;
width: 100%;
height: 100%;
overflow: auto;
}
.body{
position: relative;
height: 100%;
overflow: auto;
}
.ball{
position: relative;
z-index: 10; /*拖拽目標的層級設置高一點*/
}
現在層級關系就變成了這樣

這里原先頁面內容在層級上處于 dragbox 和 拖拽目標 之間,所以在拖拽時也不會影響到原有頁面的滾動, 無需任何 JS 處理

完整代碼可訪問https://codepen.io/xboxyan/pen/bGBNQxL(PC訪問請打開移動端模式)
也可直接掃描以下二維碼

提示:上面兩種布局方式,第一種方式適應性更好,不影響現有項目;第二種體驗更好,但是會使用 div 作為頁面滾動容器,會對頁面結構做一定的改動,可以根據實際情況自行選擇。
五、CSS實現其他功能
1. 吸附功能
很多時候,在拖拽結束時需要讓它自動吸附在邊緣,就如同文章開頭的示意圖一樣。那么,通過吸附,可以聯想到什么屬性呢?
答案就是 CSS Scroll Snap [2]
<body>
...頁面上的其他元素
<div class=“dragbox”>
<div class=“dragcon”>A</div>
<div class=“dragcon”>B</div>
<div class=“ball”></div>
</div>
</body>
下面是關鍵樣式
.dragbox{
...
scroll-snap-type: x mandatory;
}
.dragcon{
scroll-snap-align: start;
}
實際效果如下

完整代碼可訪問https://codepen.io/xboxyan/pen/XWNJyPw(PC訪問請打開移動端模式)
也可直接掃描以下二維碼
1. 設置初始位置
默認情況下,拖拽目標是唯有右下角的,如何位于左下角呢?很簡單,這里拖拽是滾動容器實現的,所以只 需要改變 scrollLeft 或者 scrollTop 即可
dragbox.scrollLeft = 999;
dragbox.scrollTop = 999;
除此之外,也可以采用純 HTML 方式實現, 利用元素的 autofocus 自動聚焦到可視范圍的特性
<div class=“dragcon">
...
<button class="pos" autofocus></button> <!--添加一個自動聚焦的元素-->
</div>
比如你希望初始位置在 左上角 ,那么添加一個 右下角 的自動聚焦元素就可以了(當然還需要設置透明度等隱藏起來)~
1. 設置邊界
現在拖拽目標的邊界是屏幕邊緣,有時候可能需要留一些間距,這種需要在 CSS 就很容易了,可以改變 left/top/right/bottom 、 padding 、 border ... 很多方式
.dragbox{
left: 10px;
top: 10px;
right: 10px;
bottom: 10px;
/*rect: 10px;*/
}
.dragbox{
padding: 10px;
}
六、說明和小結
關于兼容性本來以為是沒什么問題的,實測下來 ios 問題多多,主要是 safari 滾動容器的問題。例如,有些低版本 ios 滾動不順暢,需要添加
-webkit-overflow-scrolling:touc h , 才能實現平滑滾動和自動吸附,但是又會導致層級問題,有些文檔上描述設置該屬性會創建一個原生的滾動容器,層級最高。還有第一種 fixed 布局,如果默認情況下設置 pointer-events: none , 在 touchstart 之后設置成 auto ,這個在 ios 上滾動竟然失效了,但是反轉過來就可以了(demo中已兼容ios)。
優點嘛,繼承 CSS 的靈活性,幾乎零成本,容易復用,同時利用了原生滾動,也不會有卡頓。
不過也有少許局限,如果 拖拽目標的尺寸是不固定的 ,可能需要借助 JS 來獲取了。當然相比較而言,這還是一個性價比極高的實現方式。
現在回頭看看,其實也沒有用到什么非常生僻的屬性(scroll-snap 可能算一個,不過畢竟是輔助功能),主要還是常見的效果,然后通過聯想和發散,根據平日的積累,充分挖掘原生的能力,最終完成所需要的交互,然后就有了本文。
感謝閱讀,希望能對日后的工作有所啟發。
References
[1]
起點中文網: https://m.qidian.com/
[2]
CSS Scroll Snap: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Scroll_Snap
到此這篇關于純 CSS 實現拖拽效果的代碼的文章就介紹到這了,更多相關CSS 實現拖拽效果內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章,希望大家以后多多支持腳本之家!