本篇文章給大家介紹一下如何使用純CSS實現鼠標點擊拖拽效果,讓交互更加生動,希望對大家有所幫助!
背景
鼠標拖拽元素移動,算是一個稍微有點點復雜的交互。
而在本文,我們就將打破常規,向大家介紹一種超強的僅僅使用純 CSS 就能夠實現的鼠標點擊拖拽效果。
本文,我們還是僅僅通過 CSS,來實現一種絲滑的鼠標點擊拖動元素移動的效果。
鼠標點擊拖拽跟隨效果
OK,什么意思呢?我們先來看一個最最簡單的效果示意圖,實現點擊一個元素,能夠拖動元素進行移動的效果:
好的,到這里,在繼續往下閱讀之前,你可以停一停。這種效果,正常而言,都是必須要借助 JavaScript 才能夠實現的。從表現上來看:
首先拖拽元素過程,可以任意將元素進行移動
然后放置元素,讓元素停留在另外一個地方
思考一下,如果不借助 JavaScript 的話,有辦法將元素小球從 A 點移動到 B 點么?這個效果完全就不像是純 CSS 能夠完成的。
答案必然是可以的!整個過程也非常之巧妙,這里我們核心需要利用強大的 resize
屬性。以及,配合通過構建一種巧妙的布局,去解決可能會遇到的各種難題。
使用 resize,構建可拖拽改變大小的元素
首先,我們利用 resize
屬性來實現一個可改變大小的元素。
什么是 resize
呢?根據 MDN -- resize:該 CSS 屬性允許你控制一個元素的可調整大小性。
其 CSS 語法如下所示:
{ /* Keyword values */ resize: none; resize: both; resize: horizontal; resize: vertical; resize: block; resize: inline; }
簡單解釋一下:
resize: none
:元素不能被用戶縮放
resize: both
:允許用戶在水平和垂直方向上調整元素的大小
resize: horizontal
:允許用戶在水平方向上調整元素的大小
resize: vertical
:允許用戶在垂直方向上調整元素的大小
resize: block
:根據書寫模式(writing-mode)和方向值(direction),元素顯示允許用戶在塊方向上(block)水平或垂直調整元素大小的機制。
resize: inline
:根據書寫模式(writing-mode)和方向值(direction),元素顯示一種機制,允許用戶在內聯方向上(inline)水平方向或垂直方向調整元素的大小。
看一個最簡單的 DEMO:
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A aut qui labore rerum placeat similique hic consequatur tempore doloribus aliquid alias, nobis voluptates. Perferendis, voluptate placeat esse soluta deleniti id!</p>
p { width: 200px; height: 200px; resize: horizontal; overflow: scroll; }
這里,我們設置了一個長寬為 200px
的 <p>
為橫向可拖拽改變寬度。效果如下:
簡單總結一些小技巧:
resize
的生效,需要配合overflow: scroll
,當然,準確的說法是,overflow
不是visible
,或者可以直接作用于替換元素譬如圖像、<video>
及<iframe>
、<textarea>
等我們可以通過
resize
的horizontal
、vertical
、both
來設置橫向拖動、縱向拖動、橫向縱向皆可拖動。可以配合容器的
max-width
、min-width
、max-height
、min-height
限制可拖拽改變的一個范圍
這里,如果你的對 resize
還有所疑惑,或者想了解更多 resize 的有趣用法,可以去找找相關的教程案例。
將 resize 應用到本文實例中
OK,接下來,我們將 resize 實際運用到我們本文的例子中去,首先,我們先簡單實現一個 DIV:
<div class="g-resize"></div>
.g-resize { width: 100px; height: 100px; border: 1px solid deeppink; }
如下,非常普通,沒有什么特別的:
但是,通過給這個元素加上 resize: both
以及 overflow: scroll
,此時,這個元素的大小就通過元素右下角的 ICON 進行拖動改變。
簡單修改下我們的 CSS 代碼:
.g-resize { width: 100px; height: 100px; border: 1px solid deeppink; resize: both; overflow: scroll; }
這樣,我們就得到了一個靈活可以拖動的元素:
是的,我們的整個效果,就需要借助這個特性進行實現。
在此基礎上,我們可以嘗試將一個元素定位到上面這個可拖動放大縮小的元素的右下角,看著能不能實現上述的效果。
簡單加一點代碼:
<div class="g-resize"></div>
.g-resize { position: relative; width: 20px; height: 20px; resize: both; overflow: scroll; } .g-resize::before { content: ""; position: absolute; bottom: 0; right: 0; width: 20px; height: 20px; border-radius: 50%; background: deeppink; }
我們利用元素的偽元素實現了一個小球,放置在容器的右下角看看效果:
如果我們再把整個設置了 resize: both
的邊框隱藏呢?那么效果就會是這樣:
Wow,整個效果已經非常的接近了!只是,認真看的話,能夠看到一些瑕疵,就是還是能夠看到設置了 resize
的元素的這個 ICON:
這個也好解決,在 Chrome 中,我們可以通過另外一個偽元素 ::-webkit-resizer
,設置這個 ICON 的隱藏。
根據 MDN - ::-webkit-resizer,它屬于整體的滾動條偽類樣式家族中的一員。
其中 ::-webkit-resizer
可以控制出現在某些元素底角的可拖動調整大小的滑塊的樣式。
所以,這里我就利用這個偽類:
.g-resize { position: relative; width: 20px; height: 20px; resize: both; overflow: scroll; } .g-resize::before { content: ""; position: absolute; bottom: 0; right: 0; width: 20px; height: 20px; border-radius: 50%; background: deeppink; } .g-resize::-webkit-resizer { background-color: transparent; }
這樣,這里的核心在于利用了 .g-resize::-webkit-resizer
中的 background-color: transparent
,將滑塊的顏色設置為了透明色。我們就得到了與文章一開始,一模一樣的效果:
解決溢出被裁剪問題
當然,這里有個很致命的問題,如果需要移動的內容,遠比設置了 resize
的容器要大,或者其初始位置不在該容器內,超出了的部分因為設置了 overflow: scroll
,將無法看到。
因此上述方案存在比較大的缺陷。
舉個例子,假設我們需要被拖動的元素不再是一個有這樣一個簡單的結構:
<div class="g-content"></div>
.g-content { width: 100px; height: 100px; background: black; pointer-event: none; &::before { content: ""; position: absolute; width: 20px; height: 20px; background: yellow; border-radius: 50%; }
而像是這樣,是一個更為復雜的布局內容展示(當然下面展示的也比較簡單,實際中可以想象成任意復雜結構內容):
如果將這個結構,扔到上面的 g-resize
中:
<div class="g-resize"> <div class="g-content"></div> </div>
那么就會因為設置了 overflow: scroll
的原因,將完全看不到,只剩下一小塊:
為了解決這個問題,我們得修改原本的 DOM 結構,另辟蹊徑。
方法有很多,譬如可以利用 Grid 布局的一些特性。當然,這里我們只需要巧妙的加多一層,就可以完全解決這個問題。
我們來實現這樣一個布局:
<div class="g-container"> <div class="g-resize"></div> <div class="g-content"></div> </div>
解釋一下上述代碼,其中:
g-container
設置為絕對定位加上display: inline-block
,這樣其盒子大小就可以由內部正常流式布局盒子的大小撐開
g-resize
設置為position: relative 并且
設置resize
,負責提供一個可拖動大小元素,在這個元素的變化過程中,就能動態改變父容器的高寬
g-content
實際內容盒子,通過position: absolute
定位到容器的右下角即可
看看完整的 CSS 代碼:
.g-container { position: absolute; display: inline-block; } .g-resize { content: ""; position: relative; width: 20px; height: 20px; border-radius: 50%; resize: both; overflow: scroll; z-index: 1; } .g-content { position: absolute; bottom: -80px; right: -80px; width: 100px; height: 100px; background: black; pointer-event: none; &::before { content: ""; position: absolute; width: 20px; height: 20px; background: yellow; border-radius: 50%; transition: .3s; } } .g-container:hover .g-content::before { transform: scale(1.1); box-shadow: -2px 2px 4px -4px #333, -4px 4px 8px -4px #333; } .g-resize::-webkit-resizer { background-color: transparent; }
下圖中,你看到的所有元素,都只是 g-content
呈現出來的元素,整個效果就是這樣:
是的,可能你會有所疑惑,下面我用簡單不同顏色,標識不同不同的 DOM 結構,方便你去理解。
紅色邊框表示整個
g-container
的大小用藍色矩形表示設置了
g-resize
元素的大小關掉
::-webkit-resizer
的透明設置,展示出 resize 框的可拖拽 ICON
.g-container { border: 3px solid red; } .g-resize { content: ""; background: blue; resize: both; overflow: scroll; } .g-resize::-webkit-resizer { // background-color: transparent; }
看看這個圖,整個原理基本就比較清晰的浮現了出來:
完整的原理代碼,你可以戳這里:CodePen Demo -- Pure CSS Auto Drag Demo
實際應用
OK,用了比較大篇幅對原理進行了描述。下面我們舉一個實際的應用場景。使用上述技巧制作的可拖動便簽貼。靈感來自 -- scottkellum。
代碼也不多,如果你了解了上面的內容,下面的代碼將非常好理解:
<div class="g-container"> <div class="g-resize"></div> <div class="g-content"> Lorem ipsum dolor sit amet consectetur?</div> </div>
完整的 CSS 代碼如下:
body { position: relative; padding: 10px; background: url("背景圖"); background-size: cover; } .g-container { position: absolute; display: inline-block; } .g-resize { content: ""; position: relative; width: 20px; height: 20px; resize: both; overflow: scroll; z-index: 1; } .g-content { position: absolute; bottom: -160px; right: -180px; color: rgba(#000, 0.8); background-image: linear-gradient( 160deg, rgb(255, 222, 30) 50%, rgb(255, 250, 80) ); width: 200px; height: 180px; pointer-event: none; text-align: center; font-family: "marker felt", "comic sans ms", sans-serif; font-size: 24px; line-height: 1.3; padding: 1em; box-sizing: border-box; &:before { content: ""; position: absolute; width: 20px; height: 20px; top: 0; left: 0; border-radius: 50%; background-image: radial-gradient( at 60% 30%, #f99, red 20%, rgb(180, 8, 0) ); background-position: 20% 10%; cursor: pointer; pointer-events: none; transform: scale(0.8); box-shadow: -5px 10px 3px -8.5px #000, -1px 7px 12px -5px #000; transition: all 0.3s ease; transform: scale(0.8); } } .g-container:hover .g-content::before { transform: scale(0.9); box-shadow: -5px 10px 6px -8.5px #000, -1px 7px 16px -4px #000; } .g-resize::-webkit-resizer { background-color: transparent; }
我們通過上述的技巧,實現了一個僅僅使用 CSS 實現的自由拖拽的便簽貼。我們可以自由的將其拖拽到任意地方。看看效果:
當然,我們可以再配合上另外一個有意思是 HTML 屬性 -- contenteditable
。
contenteditable
是一個 HTML TAG 的屬性,表示元素是否可被用戶編輯。如果可以,瀏覽器會修改元素的部件以允許編輯。
簡單修改一下 DOM 結構:
<div class="g-container"> <div class="g-resize"></div> <div class="g-content" contenteditable="true"> Lorem ipsum dolor sit amet consectetur?</div> </div>
此時,元素不僅可以被拖動,甚至可以被重寫,感受一下:
純 CSS 實現的效果,非常的有意思,完整的代碼,你可以戳這里:Pure CSS Auto Drag Demo
最后
基于 resize
這個 CSS 屬性,其實還有很多有意思的用法。
原文地址:https://www.cnblogs.com/coco1s/p/16774696.html
作者:ChokCoco