感謝參考原文-http://bjbsair.com/2020-03-27/tech-info/7202.html
前言
在本文中將會用Vue完成九宮格拖拽效果,同時介紹一下網(wǎng)格布局。具體代碼以及demo可以點(diǎn)以下超鏈接進(jìn)入
效果實(shí)例
Demo
簡單了解Grid布局(網(wǎng)格布局)
什么是網(wǎng)格布局
CSS網(wǎng)格布局(又稱“網(wǎng)格”),是一種二維網(wǎng)格布局系統(tǒng)。CSS在處理網(wǎng)頁布局方面一直做的不是很好。一開始我們用的是table(表格)布局,然后用float(浮動),position(定位)和inline-block(行內(nèi)塊)布局,但是這些方法本質(zhì)上是hack,遺漏了很多功能,例如垂直居中。后來出了flexbox(盒子布局),解決了很多布局問題,但是它僅僅是一維布局,而不是復(fù)雜的二維布局,實(shí)際上它們(flexbox與grid)能很好的配合使用。Grid布局是第一個專門為解決布局問題而創(chuàng)建的CSS模塊.
grid
簡單說說網(wǎng)格布局的屬性
- display: grid: 生成塊級網(wǎng)格 inline-grid: 生成行內(nèi)網(wǎng)格 subgrid: 如果網(wǎng)格容器本身是網(wǎng)格項(xiàng)(嵌套網(wǎng)格容器),此屬性用來繼承其父網(wǎng)格容器的列、行大小。
- grid-template-columns 設(shè)置網(wǎng)格列大小
- grid-template-rows 設(shè)置網(wǎng)格行大小
- grid-template-areas 設(shè)置網(wǎng)格區(qū)域
- grid-column-gap 設(shè)置網(wǎng)格列間距
- grid-row-gap 設(shè)置網(wǎng)格行間距
- grid-gap 縮寫形式 grid-gap: <grid-row-gap> <grid-column-gap>
- justify-items 水平方向?qū)R方式(在這里只是簡單說明) start: 左對齊 end: 右對齊 center: 居中對齊 stretch: 填滿(默認(rèn))
- align-items 垂直方向?qū)R方式 start: 頂部對齊 end: 底部對齊 center: 居中對齊 stretch:填滿(默認(rèn))
當(dāng)然,如果看不懂也不要緊,這里有一篇個人十分喜歡的網(wǎng)格布局的文章。里面介紹得十分詳細(xì)??梢怨┐蠹疑钊雽W(xué)習(xí)網(wǎng)格布局內(nèi)容。
傳送門:Grid布局指南
http://www.itdecent.cn/p/d183265a8dad
實(shí)現(xiàn)九宮格布局
/*css*/
.container{
position: relative; /*實(shí)現(xiàn)定位,使得滑塊定位相對于此*/
display: grid; /*定義網(wǎng)格布局*/
width: 300px;
height: 300px;
grid-template-columns: 100px 100px 100px; /*實(shí)現(xiàn)九宮格,行列各三*/
grid-template-rows: 100px 100px 100px;
grid-template-areas: "head1 head2 head3" /*定義個格子的名稱,方便計(jì)算*/
"main1 main2 main3"
"footer1 footer2 footer3";
border: 1px solid #000;
margin: 0 auto;
}
.block{
position: absolute; /*相對于container定位*/
width: 100px;
height: 100px;
display: flex; /*flex布局,使得文字在中央*/
justify-content: center;
justify-items: center;
align-items: center;
align-content: center;
user-select: none; /*用戶不可選定文字*/
background: olivedrab;
border: 1px solid #000
}
//app.vue
<div id="app">
<div class="container">
<transition >
<div class="block animated" :style="{top:this.positionY,left:this.positionX,gridArea:'main2'}" @mousedown="move" ref="block">
{{positionX}}
{{positionY}}
</div>
</transition>
</div>
</div>
實(shí)現(xiàn)拖拽的JS代碼部分
在這里我選取一些核心代碼出來講解。代碼有所省略,因?yàn)榇a著實(shí)有點(diǎn)長,太占篇幅而且沒多大意義,如果需要瀏覽全部代碼可以點(diǎn)擊上面的Demo連接。
<script>
//引入animate.css 沒有手撕css動畫,直接用了animate.css實(shí)現(xiàn)我們的動畫效果
import animate from 'animate.css';
export default {
name: 'app',
data () {
return {
positionX:0, //定義方塊的兩個坐標(biāo)
positionY:0,
}
},
methods:{
move(e){
let oDiv = e.target; //獲取點(diǎn)擊的目標(biāo)元素
let gDiv = e.path[1]; //獲取點(diǎn)擊元素的父級元素
/*獲取點(diǎn)擊時的偏移位置,在這里要注意一下
**由于我們用的是網(wǎng)格布局,每在一個格子中相對位置都是相對格子來算的,不是相對于父級盒子左上角
**也就是說當(dāng)你把方塊移動到九個格子中任意一個時,方塊的位置都是top:0和left:0
*/
//所以這里我們直接取鼠標(biāo)點(diǎn)擊的位置減去點(diǎn)擊盒子的偏移位置,也就是0
let disX = e.clientX - 0;
let disY = e.clientY - 0;
document.onmousemove = (e)=>{
//當(dāng)拖動時,算出的值就剛好是方塊的top和left值
let left = e.clientX - disX;
let top = e.clientY - disY;
switch (oDiv.style.gridArea){
case "head1 / head1 / head1 / head1":this.rangeOfHead1(left,top,oDiv);break; //實(shí)現(xiàn)head1的移動范圍
case "head2 / head2 / head2 / head2":this.rangeOfHead2(left,top,oDiv);break; //實(shí)現(xiàn)head2的移動范圍
case "head3 / head3 / head3 / head3":this.rangeOfHead3(left,top,oDiv);break; //實(shí)現(xiàn)head3的移動范圍
case "main1 / main1 / main1 / main1":this.rangeOfMain1(left,top,oDiv);break; //實(shí)現(xiàn)main1的移動范圍
...
}
};
document.onmouseup = (e)=>{
//當(dāng)鼠標(biāo)抬起時,我們要做的事
//通過點(diǎn)擊位置和父級元素的偏移判斷方塊在哪個區(qū)域
if(e.clientY-gDiv.offsetTop<100&&e.clientX-gDiv.offsetLeft<100){
//將方塊移動到該區(qū)域中
this.changeBlock("head1",oDiv);
}else if(e.clientY-gDiv.offsetTop>100&&e.clientX-gDiv.offsetLeft<100&&e.clientY-gDiv.offsetTop<200){
this.changeBlock("main1",oDiv);
}else if(e.clientY-gDiv.offsetTop>200&&e.clientX-gDiv.offsetLeft<100){
this.changeBlock("footer1",oDiv);
}else if(e.clientY-gDiv.offsetTop<100&&e.clientX-gDiv.offsetLeft>100&&e.clientX-gDiv.offsetLeft<200){
this.changeBlock("head2",oDiv);
}else if(e.clientY-gDiv.offsetTop<100&&e.clientX-gDiv.offsetLeft>200){
this.changeBlock("head3",oDiv);
}else if(e.clientY-gDiv.offsetTop>100&&e.clientX-gDiv.offsetLeft>200&&e.clientY-gDiv.offsetTop<200){
this.changeBlock("main3",oDiv);
}else if(e.clientY-gDiv.offsetTop>200&&e.clientX-gDiv.offsetLeft>200){
this.changeBlock("footer3",oDiv);
}else if(e.clientY-gDiv.offsetTop>200&&e.clientX-gDiv.offsetLeft>100&&e.clientX-gDiv.offsetLeft<200){
this.changeBlock("footer2",oDiv);
}else {
this.changeBlock("main2",oDiv);
}
document.onmousemove=null; //需要把事件監(jiān)聽取消
document.onmousedown = null; //需要把事件監(jiān)聽取消
//當(dāng)然,不能忘記我們的動畫hhh
oDiv.className = "block animated wobble";
let removeClass = setTimeout(()=>{
oDiv.className = "block animated";
},500);
};
},
rangeOfHead1(x,y,oDiv){ //判斷head1格子中的可以移動范圍
if(x>=200){
x=200;
}else if(x<=0){
x=0;
}
if(y>=200){
y=200;
}else if(y<=0){
y=0;
}
oDiv.style.left = x + 'px';
oDiv.style.top = y + 'px';
this.positionX = x;
this.positionY = y;
},
rangeOfHead2(x,y,oDiv){ //判斷head2格子中的可以移動范圍
if(x>=100){
x=100;
}else if(x<=-100){
x=-100;
}
if(y>=200){
y=200;
}else if(y<=0){
y=0;
}
oDiv.style.left = x + 'px';
oDiv.style.top = y + 'px';
this.positionX = x;
this.positionY = y;
},
...
changeBlock(blockName,oDiv){ //將方塊移入到對應(yīng)的區(qū)域中
this.positionX = 0;
this.positionY = 0;
oDiv.style.gridArea=blockName;
}
},
}
</script>
總結(jié)
到這里我們把九宮格拖拽實(shí)現(xiàn)了,同時學(xué)習(xí)了Grid(網(wǎng)格布局)。總的做下來,發(fā)現(xiàn)用網(wǎng)格布局做網(wǎng)格拖拽更加費(fèi)事,但是為了后續(xù)可以方便一些,也只好搗鼓下來了。到這里我們就把基于Vue的九宮格拖拽實(shí)現(xiàn)了,有問題或者發(fā)現(xiàn)錯誤的請指正,謝謝大家
珍惜淡定的心境,苦過后更加清感謝參考原文-http://bjbsair.com/2020-03-27/tech-info/7202/
前言
在本文中將會用Vue完成九宮格拖拽效果,同時介紹一下網(wǎng)格布局。具體代碼以及demo可以點(diǎn)以下超鏈接進(jìn)入
效果實(shí)例
Demo
簡單了解Grid布局(網(wǎng)格布局)
什么是網(wǎng)格布局
CSS網(wǎng)格布局(又稱“網(wǎng)格”),是一種二維網(wǎng)格布局系統(tǒng)。CSS在處理網(wǎng)頁布局方面一直做的不是很好。一開始我們用的是table(表格)布局,然后用float(浮動),position(定位)和inline-block(行內(nèi)塊)布局,但是這些方法本質(zhì)上是hack,遺漏了很多功能,例如垂直居中。后來出了flexbox(盒子布局),解決了很多布局問題,但是它僅僅是一維布局,而不是復(fù)雜的二維布局,實(shí)際上它們(flexbox與grid)能很好的配合使用。Grid布局是第一個專門為解決布局問題而創(chuàng)建的CSS模塊.
grid
簡單說說網(wǎng)格布局的屬性
- display: grid: 生成塊級網(wǎng)格 inline-grid: 生成行內(nèi)網(wǎng)格 subgrid: 如果網(wǎng)格容器本身是網(wǎng)格項(xiàng)(嵌套網(wǎng)格容器),此屬性用來繼承其父網(wǎng)格容器的列、行大小。
- grid-template-columns 設(shè)置網(wǎng)格列大小
- grid-template-rows 設(shè)置網(wǎng)格行大小
- grid-template-areas 設(shè)置網(wǎng)格區(qū)域
- grid-column-gap 設(shè)置網(wǎng)格列間距
- grid-row-gap 設(shè)置網(wǎng)格行間距
- grid-gap 縮寫形式 grid-gap: <grid-row-gap> <grid-column-gap>
- justify-items 水平方向?qū)R方式(在這里只是簡單說明) start: 左對齊 end: 右對齊 center: 居中對齊 stretch: 填滿(默認(rèn))
- align-items 垂直方向?qū)R方式 start: 頂部對齊 end: 底部對齊 center: 居中對齊 stretch:填滿(默認(rèn))
當(dāng)然,如果看不懂也不要緊,這里有一篇個人十分喜歡的網(wǎng)格布局的文章。里面介紹得十分詳細(xì)??梢怨┐蠹疑钊雽W(xué)習(xí)網(wǎng)格布局內(nèi)容。
傳送門:Grid布局指南
http://www.itdecent.cn/p/d183265a8dad
實(shí)現(xiàn)九宮格布局
/*css*/
.container{
position: relative; /*實(shí)現(xiàn)定位,使得滑塊定位相對于此*/
display: grid; /*定義網(wǎng)格布局*/
width: 300px;
height: 300px;
grid-template-columns: 100px 100px 100px; /*實(shí)現(xiàn)九宮格,行列各三*/
grid-template-rows: 100px 100px 100px;
grid-template-areas: "head1 head2 head3" /*定義個格子的名稱,方便計(jì)算*/
"main1 main2 main3"
"footer1 footer2 footer3";
border: 1px solid #000;
margin: 0 auto;
}
.block{
position: absolute; /*相對于container定位*/
width: 100px;
height: 100px;
display: flex; /*flex布局,使得文字在中央*/
justify-content: center;
justify-items: center;
align-items: center;
align-content: center;
user-select: none; /*用戶不可選定文字*/
background: olivedrab;
border: 1px solid #000
}
//app.vue
<div id="app">
<div class="container">
<transition >
<div class="block animated" :style="{top:this.positionY,left:this.positionX,gridArea:'main2'}" @mousedown="move" ref="block">
{{positionX}}
{{positionY}}
</div>
</transition>
</div>
</div>
實(shí)現(xiàn)拖拽的JS代碼部分
在這里我選取一些核心代碼出來講解。代碼有所省略,因?yàn)榇a著實(shí)有點(diǎn)長,太占篇幅而且沒多大意義,如果需要瀏覽全部代碼可以點(diǎn)擊上面的Demo連接。
<script>
//引入animate.css 沒有手撕css動畫,直接用了animate.css實(shí)現(xiàn)我們的動畫效果
import animate from 'animate.css';
export default {
name: 'app',
data () {
return {
positionX:0, //定義方塊的兩個坐標(biāo)
positionY:0,
}
},
methods:{
move(e){
let oDiv = e.target; //獲取點(diǎn)擊的目標(biāo)元素
let gDiv = e.path[1]; //獲取點(diǎn)擊元素的父級元素
/*獲取點(diǎn)擊時的偏移位置,在這里要注意一下
**由于我們用的是網(wǎng)格布局,每在一個格子中相對位置都是相對格子來算的,不是相對于父級盒子左上角
**也就是說當(dāng)你把方塊移動到九個格子中任意一個時,方塊的位置都是top:0和left:0
*/
//所以這里我們直接取鼠標(biāo)點(diǎn)擊的位置減去點(diǎn)擊盒子的偏移位置,也就是0
let disX = e.clientX - 0;
let disY = e.clientY - 0;
document.onmousemove = (e)=>{
//當(dāng)拖動時,算出的值就剛好是方塊的top和left值
let left = e.clientX - disX;
let top = e.clientY - disY;
switch (oDiv.style.gridArea){
case "head1 / head1 / head1 / head1":this.rangeOfHead1(left,top,oDiv);break; //實(shí)現(xiàn)head1的移動范圍
case "head2 / head2 / head2 / head2":this.rangeOfHead2(left,top,oDiv);break; //實(shí)現(xiàn)head2的移動范圍
case "head3 / head3 / head3 / head3":this.rangeOfHead3(left,top,oDiv);break; //實(shí)現(xiàn)head3的移動范圍
case "main1 / main1 / main1 / main1":this.rangeOfMain1(left,top,oDiv);break; //實(shí)現(xiàn)main1的移動范圍
...
}
};
document.onmouseup = (e)=>{
//當(dāng)鼠標(biāo)抬起時,我們要做的事
//通過點(diǎn)擊位置和父級元素的偏移判斷方塊在哪個區(qū)域
if(e.clientY-gDiv.offsetTop<100&&e.clientX-gDiv.offsetLeft<100){
//將方塊移動到該區(qū)域中
this.changeBlock("head1",oDiv);
}else if(e.clientY-gDiv.offsetTop>100&&e.clientX-gDiv.offsetLeft<100&&e.clientY-gDiv.offsetTop<200){
this.changeBlock("main1",oDiv);
}else if(e.clientY-gDiv.offsetTop>200&&e.clientX-gDiv.offsetLeft<100){
this.changeBlock("footer1",oDiv);
}else if(e.clientY-gDiv.offsetTop<100&&e.clientX-gDiv.offsetLeft>100&&e.clientX-gDiv.offsetLeft<200){
this.changeBlock("head2",oDiv);
}else if(e.clientY-gDiv.offsetTop<100&&e.clientX-gDiv.offsetLeft>200){
this.changeBlock("head3",oDiv);
}else if(e.clientY-gDiv.offsetTop>100&&e.clientX-gDiv.offsetLeft>200&&e.clientY-gDiv.offsetTop<200){
this.changeBlock("main3",oDiv);
}else if(e.clientY-gDiv.offsetTop>200&&e.clientX-gDiv.offsetLeft>200){
this.changeBlock("footer3",oDiv);
}else if(e.clientY-gDiv.offsetTop>200&&e.clientX-gDiv.offsetLeft>100&&e.clientX-gDiv.offsetLeft<200){
this.changeBlock("footer2",oDiv);
}else {
this.changeBlock("main2",oDiv);
}
document.onmousemove=null; //需要把事件監(jiān)聽取消
document.onmousedown = null; //需要把事件監(jiān)聽取消
//當(dāng)然,不能忘記我們的動畫hhh
oDiv.className = "block animated wobble";
let removeClass = setTimeout(()=>{
oDiv.className = "block animated";
},500);
};
},
rangeOfHead1(x,y,oDiv){ //判斷head1格子中的可以移動范圍
if(x>=200){
x=200;
}else if(x<=0){
x=0;
}
if(y>=200){
y=200;
}else if(y<=0){
y=0;
}
oDiv.style.left = x + 'px';
oDiv.style.top = y + 'px';
this.positionX = x;
this.positionY = y;
},
rangeOfHead2(x,y,oDiv){ //判斷head2格子中的可以移動范圍
if(x>=100){
x=100;
}else if(x<=-100){
x=-100;
}
if(y>=200){
y=200;
}else if(y<=0){
y=0;
}
oDiv.style.left = x + 'px';
oDiv.style.top = y + 'px';
this.positionX = x;
this.positionY = y;
},
...
changeBlock(blockName,oDiv){ //將方塊移入到對應(yīng)的區(qū)域中
this.positionX = 0;
this.positionY = 0;
oDiv.style.gridArea=blockName;
}
},
}
</script>
總結(jié)
到這里我們把九宮格拖拽實(shí)現(xiàn)了,同時學(xué)習(xí)了Grid(網(wǎng)格布局)??偟淖鱿聛?,發(fā)現(xiàn)用網(wǎng)格布局做網(wǎng)格拖拽更加費(fèi)事,但是為了后續(xù)可以方便一些,也只好搗鼓下來了。到這里我們就把基于Vue的九宮格拖拽實(shí)現(xiàn)了,有問題或者發(fā)現(xiàn)錯誤的請指正,謝謝大家
珍惜淡定的心境,苦過后更加清