页面成分坐标和偏移,selectable_jquery_脚本之家

其实就是一个可以按住鼠标进行一个区域内条目选择的功能,相信用过Jquery UI
的都知道这是selectable的功能,然而我们如果用Vue开发的话没有类似的插件,当然你仍然可以把jquery的拿过来直接用,但是我又不想引入jquery
和 jquery UI在我的项目中,于是我就自己尝试着实现类似的功能。

鼠标事件

要实现这个功能分两步。第一步是实现鼠标选择区域的功能,第步部是把这个区域内被选择的item添加一个active的类。

1. 客户区坐标位置(clientX/clientY)

通过event事件对象的clientX/clientY属性,获得事件发生时鼠标指针在视口的水平垂直坐标

先看如何实现按住鼠标画虚线框,思路是先把容器元素的定位改为relative
然后判断当鼠标按下的时候,进行记住这个点击点的位置,然后鼠标移动的时候,实时的监测鼠标的位置,有了这两个位置就可以动态的创建一个div,它的定位为absolute,然后把它添加的容器框里,并且每次清空前一个框就可以了。为什么是用e.layerX
e.layerY呢,

2. 屏幕坐标位置(screenX/screenY)

通过event事件对象的clientX/clientY属性,获得事件发生时鼠标指针相对于整个屏幕的坐标信息

<body onmousedown="showCoords(event)"></body>
<script>
function showCoords(event){
  var input1 = document.getElementsByTagName('input')[0];
  var input2 = document.getElementsByTagName('input')[1];
  var input3 = document.getElementsByTagName('input')[2];
  var input4 = document.getElementsByTagName('input')[3];
  input1.value = event.clientX;
  input2.value = event.clientY;
  input3.value = event.screenX;
  input4.value = event.screenY;
}
</script>

layerX layerY

3. 页面坐标位置 (pageX/pageY)

通过事件对象的pageX/pageY属性可以获得鼠标事件发生时鼠标光标相对于整个文档元素的坐标位置(包含滚动).在页面没有滚动的情况下,通常pageX/pageY的值与clientX/clientY的值相等

如果元素的position样式不是默认的static,我们说这个元素具有定位属性。

4. layerX/layerY

事件对象还有个不那么常见的属性,那就是layerX/layerY,他是对于绝对定位元素来说的,相对于当前点击元素的左上角定位的。当页面上的元素时相对定位(position:relative)的时候,通常pageX/pageY和layerX/layerY的值是相同的,但是当元素绝对定位(position:absolute)了的时候,layerX/layerY就将鼠标光标位置相对于本身的左上角定位了。

图片 1

image.png

在当前触发鼠标事件的元素和它的祖先元素中找到最近的具有定位属性的元素,计算鼠标与其的偏移值,以找到元素的border的左上角的外交点作为相对点。如果找不到具有定位属性的元素,那么就相对于当前页面计算偏移,此时等同于pageY。按照这个思路完成以下代码:

元素

export default  =>{ const listener =  =>{ let reactArea = { startX: 0, startY: 0, endX: 0, endY: 0 } //是否一直按下鼠标 let isMouseDown = false let areaSelect = {} //将元素定位改为relative ele.style.position = 'relative' ele.addEventListener('mousedown', function { reactArea.startX = e.layerX; reactArea.startY = e.layerY; isMouseDown = true }) ele.addEventListener('mousemove', function{ let preArea = ele.getElementsByClassName if{ ele.removeChild } reactArea.endX = e.layerX reactArea.endY = e.layerY let leftValue = 0 let topValue = 0 let widthValue = Math.abs(reactArea.startX - reactArea.endX) let heightValue = Math.abs(reactArea.startY - reactArea.endY) if(reactArea.startX >= reactArea.endX){ leftValue = reactArea.endX }else{ leftValue = reactArea.startX } if(reactArea.startY > reactArea.endY ){ topValue = reactArea.endY }else{ topValue = reactArea.startY } //判断同时有宽高才开始画虚线框 if(reactArea.startX != reactArea.endX && reactArea.startY !=reactArea.endY){ areaSelect = document.createElement areaSelect.classList.add areaSelect.style.position = "absolute"; areaSelect.style.left = leftValue + 'px' areaSelect.style.top = topValue + 'px' areaSelect.style.width = widthValue + 'px' areaSelect.style.height = heightValue + 'px' areaSelect.style.border = "1px dashed grey" ele.append ele.addEventListener('mouseup', function { isMouseDown = false //每次鼠标点击完了areaSelect if(areaSelect && areaSelect.childNodes && ele.contains{ ele.removeChild } areaSelect = null }) } Vue.directive('selectable',{ inserted:listener, updated:listener })}

5. 偏移量(offsetWidth/offsetHeight/offsetLeft/offsetTop)

元素的偏移量(offsetLeft/offsetTop)是相对于它的直接父元素来说的。
(元素的可见大小包括滚动条)

offsetWidth = width + padding + border
offsetHeight = height + padding + border

这个时就可以实现画虚线框的效果

6. 客户区的大小(clientWidth/clientHeight)

元素内容及其内边距所占空间的大小(滚动条占用的空间不计算在内)

clientWidth = width + padding
clientHeight = width + padding

图片 2

image.png

下一步是如何把每个item置为选中状态。思路是遍历这个容器ul 的所有子元素li
,然后判断每个li是否在选中的框内部。然后看每个元素的offsetLeft 和
offsetTop
计算元素相对于父元素的位置,然后通过getBoundingClientRect().height 和
getBoundingClientRect().width
确定子元素的宽高。这些就可以计算出元素的位置和大小了,然后如何判断这个元素是否在选择区域内呢?我的规则是这个元素的四个角位置有任何一个在选择区域内或者选择区域就在这个区域的内部,就算是这个元素被选中了。按照这个思路,继续完成我们的代码:

7. 滚动大小(scrollWidth/scrollHeight/scrollLeft/scrollTop)

scrollHeight/scrollWidth是整个内容区的实际宽高,即包括了隐藏部分
scrollLeft/scrollTop是出现了横/纵向滚动条的情况下,滚动条拉动的距离
有些元素,即使没有执行任何代码也会自动的添加滚动条,如<html>,但是另外一些元素,则需要通过css的overflow属性进行设置才能滚动

带有垂直滚动条的页面总高度
document.documentElement.scrollHeight

图片 3

image.png

export default  =>{ const listener =  =>{ let reactArea = { startX: 0, startY: 0, endX: 0, endY: 0 } //是否一直按下鼠标 let isMouseDown = false let areaSelect = {} //将元素定位改为relative ele.style.position = 'relative' ele.addEventListener('mousedown', function { reactArea.startX = e.layerX; reactArea.startY = e.layerY; isMouseDown = true }) ele.addEventListener('mousemove', function{ let preArea = ele.getElementsByClassName if{ ele.removeChild } reactArea.endX = e.layerX reactArea.endY = e.layerY let leftValue = 0 let topValue = 0 let widthValue = Math.abs(reactArea.startX - reactArea.endX) let heightValue = Math.abs(reactArea.startY - reactArea.endY) if(reactArea.startX >= reactArea.endX){ leftValue = reactArea.endX }else{ leftValue = reactArea.startX } if(reactArea.startY > reactArea.endY ){ topValue = reactArea.endY }else{ topValue = reactArea.startY } //判断同时有宽高才开始画虚线框 if(reactArea.startX != reactArea.endX && reactArea.startY !=reactArea.endY){ areaSelect = document.createElement areaSelect.classList.add areaSelect.style.position = "absolute"; areaSelect.style.left = leftValue + 'px' areaSelect.style.top = topValue + 'px' areaSelect.style.width = widthValue + 'px' areaSelect.style.height = heightValue + 'px' areaSelect.style.border = "1px dashed grey" ele.append } let children = ele.getElementsByTagName for(let i =0 ; i < children.length ; i ++ ){ let childrenHeight = children[i].getBoundingClientRect().height let childrenWidth = children[i].getBoundingClientRect().width //每个li元素的位置 let offsetLeft = children[i].offsetLeft let offsetTop = children[i].offsetTop //每个li元素的宽高 let endPositionH = childrenHeight + offsetTop let endPositionW = childrenWidth + offsetLeft //五个条件满足一个就可以判断被选择 //一是右下角在选择区域内 let require1 = endPositionH > topValue && endPositionW > leftValue && endPositionH < topValue + heightValue && endPositionW < leftValue + widthValue //二是左上角在选择区域内 let require2 = offsetTop > topValue && offsetLeft > leftValue && offsetTop < topValue + heightValue && offsetLeft < leftValue + widthValue //三是右上角在选择区域内 let require3 = offsetTop > topValue && offsetLeft + childrenWidth > leftValue && offsetTop < topValue + heightValue && offsetLeft + childrenWidth< leftValue + widthValue //四是左下角在选择区域内 let require4 = offsetTop + childrenHeight > topValue && offsetLeft > leftValue && offsetTop + childrenHeight < topValue + heightValue && offsetLeft < leftValue + widthValue //五选择区域在元素体内 let require5 = offsetTop < topValue && offsetLeft < leftValue && offsetTop + childrenHeight > topValue + heightValue && offsetLeft + childrenWidth > leftValue + widthValue if(require1 || require2 || require3 || require4 || require5){ children[i].classList.add }else{ children[i].classList.remove ele.addEventListener('mouseup', function { isMouseDown = false if(areaSelect && areaSelect.childNodes && ele.contains{ ele.removeChild } areaSelect = null }) } Vue.directive('selectable',{ inserted:listener, updated:listener })}

window

完成之后再看看如何使用,html 结构:

8. window.scrollX/window.scrollY与window.pageXOffset/window.pageYOffset

window.scrollX/window.scrollY返回的是整个文档document在水平和竖直方向滚动了的距离
window.pageXOffset/window.pageYOffset相当于window.scrollX/window.scrollY的别名一样的

window.pageXOffset == window.scrollX; // 总是返回真

但在跨浏览器的情况下,尽量使用window.pageXOffset/window.pageYOffset比较好
为了保险起见,使用下面这样的代码来判断文档在垂直和水平防线滚动的距离比较好:

var x = (window.pageXOffset !== undefined) ? window.pageXOffset
 : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
var y = (window.pageYOffset !== undefined) ? window.pageYOffset
 : (document.documentElement || document.body.parentNode || document.body).scrollTop;

关于获取滚动条高度的问题
有三种方式:

document.body.scrollTop
document.documentElement.scrollTop    //在chrome有bug,需要进行判断
window.pageYOffset

三种方式都可以,但是pageYOffset在IE下会有兼容性问题
参考:
http://blog.csdn.net/wl110231/article/details/8542722

  • item1
  • item2
  • item3
  • item4
  • item5
  • item6

9. window.innerHeight/window.innerWidth

window.innerHeight/window.innerWidth记录了视口内文档元素的实际高度和宽度,实际上还有window.outerHeight/window.outerWidth

图片 4

image.png

如果页面中有frameset

var intFrameHeight = window.innerHeight; // or
var intFrameHeight = self.innerHeight;
// will return the height of the frame viewport within the frameset
var intFramesetHeight = parent.innerHeight;
// will return the height of the viewport of the closest frameset
var intOuterFramesetHeight = top.innerHeight;
// will return the height of the viewport of the outermost frameset

注意ul的这个v-selectable就是我们自定义的指令,但是使用之前必须 Vue.use

10. getBoundingClientRect()方法

getBoundingClientRect用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。getBoundingClientRect是DOM元素到浏览器可视范围的距离(不包含文档卷起的部分)。该函数返回一个Object对象,该对象有6个属性:top,lef,right,bottom,width,height;这里的top、left和css中的理解很相似,width、height是元素自身的宽高,但是right,bottom和css中的理解有点不一样。right是指元素右边界距窗口最左边的距离,bottom是指元素下边界距窗口最上面的距离。
通过这个方法可以比较方便的获取页面元素的位置:

var X= this.getBoundingClientRect().left+document.documentElement.scrollLeft;
var Y =this.getBoundingClientRect().top+document.documentElement.scrollTop;

文章参考自:
https://www.cnblogs.com/skylar/p/4121508.html

import Vue from 'vue'import Selectable from '@/components/vue-selectable/vue-selectable.js' //这个修改为你的js路径Vue.use; 

再给我们的ul li
加点样式,注意我们的被选择项会被添加一个active的class,通过这个来改变选中项样式

 ul{ margin: 40px 40px 40px 40px; border: 1px solid red; width: 300px; padding-bottom: 20px; } ul li { width: 200px; height: 30px; list-style: none; border: 1px solid black; margin-left: 10px; margin-top: 30px; text-align: center; line-height: 30px; user-select:none; } ul li.active{ background-color: red; }

这样就可以达到开头的效果了。实际上代码运行过程中还是有许多小bug的,本文只是提供了一个简单的思路和代码,更多功能可以自己修改代码进行添加。如果不明白这个自定义指令为什么是这样的写法,可以参考我的另一篇文章自定义懒加载图片插件v-lazyload。

//www.jb51.net/article/112355.htm

总结

以上所述是小编给大家介绍的自定义类似于jQuery UI Selectable
的Vue指令v-selectable,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

发表评论

电子邮件地址不会被公开。 必填项已用*标注