前言
在本文中将会用 vue 完成九宫格拖拽效果, 同时介绍一下网格布局. 具体代码以及 demo 可以点以下超链接进入
传送门: Demo 以及完整代码连接 https://jsfiddle.net/bb7bb/csnodtoa/4/
效果实例
Demo
简单了解 Grid 布局(网格布局)
什么是网格布局
CSS 网格布局 (又称 "网格"), 是一种二维网格布局系统. CSS 在处理网页布局方面一直做的不是很好. 一开始我们用的是 table(表格) 布局, 然后用 float(浮动),position(定位)和 inline-block(行内块)布局, 但是这些方法本质上是 hack, 遗漏了很多功能, 例如垂直居中. 后来出了 flexbox(盒子布局), 解决了很多布局问题, 但是它仅仅是一维布局, 而不是复杂的二维布局, 实际上它们 (flexbox 与 grid) 能很好的配合使用. Grid 布局是第一个专门为解决布局问题而创建的 CSS 模块.
grid
简单说说网格布局的属性
display:
grid: 生成块级网格
inline-grid: 生成行内网格
subgrid: 如果网格容器本身是网格项(嵌套网格容器), 此属性用来继承其父网格容器的列, 行大小.
grid-template-columns
设置网格列大小
grid-template-rows
设置网格行大小
grid-template-areas
设置网格区域
grid-column-gap
设置网格列间距
grid-row-gap
设置网格行间距
grid-gap
缩写形式 grid-gap: <grid-row-gap> <grid-column-gap>
justify-items
水平方向对齐方式(在这里只是简单说明)
start: 左对齐
end: 右对齐
center: 居中对齐
stretch: 填满(默认)
align-items
垂直方向对齐方式
start: 顶部对齐
end: 底部对齐
center: 居中对齐
stretch: 填满(默认)
当然, 如果看不懂也不要紧, 这里有一篇个人十分喜欢的网格布局的文章. 里面介绍得十分详细. 可以供大家深入学习网格布局内容.
传送门: Grid 布局指南
Vue 实现九宫格拖拽
Demo 地址: Demo 以及完整代码 https://jsfiddle.net/bb7bb/csnodtoa/4/
实现九宫格布局
- /*css*/
- .container{
- position: relative; /* 实现定位, 使得滑块定位相对于此 */
- display: grid; /* 定义网格布局 */
- width: 300px;
- height: 300px;
- grid-template-columns: 100px 100px 100px; /* 实现九宫格, 行列各三 */
- grid-template-rows: 100px 100px 100px;
- grid-template-areas: "head1 head2 head3" /* 定义个格子的名称, 方便计算 */
- "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>
实现拖拽的 JS 代码部分
在这里我选取一些核心代码出来讲解. 代码有所省略, 因为代码着实有点长, 太占篇幅而且没多大意义, 如果需要浏览全部代码可以点击上面的 Demo 连接.
- <script>
- // 引入 animate.css 没有手撕 css 动画, 直接用了 animate.css 实现我们的动画效果
- import animate from 'animate.css';
- export default {
- name: 'app',
- data () {
- return {
- positionX:0, // 定义方块的两个坐标
- positionY:0,
- }
- },
- methods:{
- move(e){
- let oDiv = e.target; // 获取点击的目标元素
- let gDiv = e.path[1]; // 获取点击元素的父级元素
- /* 获取点击时的偏移位置, 在这里要注意一下
- ** 由于我们用的是网格布局, 每在一个格子中相对位置都是相对格子来算的, 不是相对于父级盒子左上角
- ** 也就是说当你把方块移动到九个格子中任意一个时, 方块的位置都是 top:0 和 left:0
- */
- // 所以这里我们直接取鼠标点击的位置减去点击盒子的偏移位置, 也就是 0
- let disX = e.clientX - 0;
- let disY = e.clientY - 0;
- document.onmousemove = (e)=>{
- // 当拖动时, 算出的值就刚好是方块的 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; // 实现 head1 的移动范围
- case "head2 / head2 / head2 / head2":this.rangeOfHead2(left,top,oDiv);break; // 实现 head2 的移动范围
- case "head3 / head3 / head3 / head3":this.rangeOfHead3(left,top,oDiv);break; // 实现 head3 的移动范围
- case "main1 / main1 / main1 / main1":this.rangeOfMain1(left,top,oDiv);break; // 实现 main1 的移动范围
- ...
- }
- };
- document.onmouseup = (e)=>{
- // 当鼠标抬起时, 我们要做的事
- // 通过点击位置和父级元素的偏移判断方块在哪个区域
- if(e.clientY-gDiv.offsetTop<100&&e.clientX-gDiv.offsetLeft<100){
- // 将方块移动到该区域中
- 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; // 需要把事件监听取消
- document.onmousedown = null; // 需要把事件监听取消
- // 当然, 不能忘记我们的动画 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){ // 将方块移入到对应的区域中
- this.positionX = 0;
- this.positionY = 0;
- oDiv.style.gridArea=blockName;
- }
- },
- }
- </script>
总结
到这里我们把九宫格拖拽实现了, 同时学习了 Grid(网格布局). 总的做下来, 发现用网格布局做网格拖拽更加费事, 但是为了后续可以方便一些, 也只好捣鼓下来了. 到这里我们就把基于 Vue 的九宫格拖拽实现了, 有问题或者发现错误的请指正, 谢谢大家
珍惜淡定的心境, 苦过后更加清
来源: http://www.jianshu.com/p/ccef46e15f3f