注意点:bind 和 inserted
共同点: dom插入都会调用,bind在inserted之前
不同点:
bind 时父节点为 null
inserted 时父节点存在。
bind是在dom树绘制前调用,inserted在dom树绘制后调用
bind: function (el) {
console.log('bind',el.parentNode) // null
},
inserted: function (el) {
console.log('inserted',el.parentNode) // ...
}
注意:自定义指令的钩子里面没有vue实例,this指向undefined;
自定义指令的作用是用于对DOM元素进行底层操作
钩子函数
bind 类似于created 只调用一次,指令第一次绑定到元素时调用,此时被绑定元素在页面上还不存在;
inserted 类似于mounted ,被绑定元素插入到dom的时候执行,此时该元素在页面上已经存在了;
update 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新;
componentUpdated 被绑定元素所在模板完成一次更新周期时调用;
unbind 只调用一次,指令与元素解绑时调用;
钩子函数内的参数
el: 被绑定的元素,可以用来直接操作 DOM el.innerHTML
、 el.addEventListener
binding: 一个对象, 指的是指令本身 v-abc:xxx.a.b.c="abcde"
[arg] 动态指令参数
v-myDirectiveValue:[argument]="value"
未指明钩子函数时,默认bind和update中
const vm = new Vue({
el: "#app",
data: {
},
//自定义局部指令的时候,如果直接跟一个函数,没有写(bind inserted update)的时候,就相当于把该函数写到了bind和update中
directives:{
fontSize:function(el,binding){
el.style.fontSize = binding.value+'px'
}
}
})
directives:{
fontSize:function(el,binding){
el.style.fontSize = binding.value+'px'
}
}
使用
<template>
<div>
<div v-fontSize='40'>牛奶</div>
</div>
</template>
<script>
export default {
name: 'homePage',
components:{
test
},
data() {
return {
test:''
}
},
created(){
},
computed:{
},
methods:{
},
mounted() {
console.log(this.$el);
// this.$el.style.backgroundColor = "green"
},
directives:{
fontSize:function(el,binding){
el.style.fontSize = binding.value+'px'
}
}
}
</script>
<style lang="scss" scoped>
</style>
<style scoped>
</style>
// 拖拽
Vue.directive('drag', { // 全局弹窗拖拽指令
bind: function (el, binding, vnode) {
console.log("parent",el.parentNode) // null
let oDiv = el;
let self = vnode.context;
oDiv.onmousedown = (e) => {
let disX = e.clientX - oDiv.offsetLeft;
let disY = e.clientY - oDiv.offsetTop;
document.onmousemove = (e) => {
let left = e.clientX - disX;
let top = e.clientY - disY;
// 限制拖拽在可视范围内
if (left <= 0) {
left = 5; //设置成5是为了不离边缘太近
} else if (left > document.documentElement.clientWidth - el.clientWidth) {
//document.documentElement.clientWidth 屏幕的可视宽度
left = document.documentElement.clientWidth - el.clientWidth - 5
}
if (top <= 0) {
top = 5;
} else if (top > document.documentElement.clientHeight - el.clientHeight) {
top = document.documentElement.clientHeight - el.clientHeight - 5
}
if (!left && !top) {
oDiv.style.left = self.currentLeft;
oDiv.style.top = self.currentTop;
} else {
oDiv.style.left = left + "px";
oDiv.style.top = top + "px";
}
};
document.onmouseup = (e) => {
document.onmousemove = null;
document.onmouseup = null;
};
};
},
// inserted : function (el, binding, vnode) {
// console.log("parent",el.parentNode)
// let oDiv = el;
// let self = vnode.context;
// oDiv.onmousedown = (e) => {
// let disX = e.clientX - oDiv.offsetLeft;
// let disY = e.clientY - oDiv.offsetTop;
// console.log("1123",disX,disY);
// document.onmousemove = (e) => {
// let left = e.clientX - disX;
// let top = e.clientY - disY;
// if (!left && !top) {
// oDiv.style.left = self.currentLeft;
// oDiv.style.top = self.currentTop;
// } else {
// oDiv.style.left = left + "px";
// oDiv.style.top = top + "px";
// }
// };
// document.onmouseup = (e) => {
// document.onmousemove = null;
// document.onmouseup = null;
// };
// };
// },
})
全局指令写法如下
Vue.directive('drag', {
inserted(el) {
let switchPosition = {
x: 10,
y: 10,
startX: 0,
startY: 0,
endX: 0,
endY: 0
}
el.addEventListener('touchstart', function (e) {
console.log(e)
switchPosition .startX = e.touches[0].pageX
switchPosition .startY = e.touches[0].pageY
})
el.addEventListener('touchend', function (e) {
switchPosition .x = switchPosition .endX
switchPosition .y = switchPosition .endY
switchPosition .startX = 0
switchPosition .startY = 0
})
el.addEventListener('touchmove', function (e) {
if (e.touches.length > 0) {
let offsetX = e.touches[0].pageX - switchPosition .startX
let offsetY = e.touches[0].pageY - switchPosition .startY
let x = switchPosition .x - offsetX
let y = switchPosition .y - offsetY
if (x + el.offsetWidth > document.documentElement.offsetWidth) {
x = document.documentElement.offsetWidth - el.offsetWidth
}
if (y + el.offsetHeight > document.documentElement.offsetHeight) {
y = document.documentElement.offsetHeight - el.offsetHeight
}
if (x < 0) {
x = 0
}
if (y < 0) {
y = 0
}
el.style.right = x + 'px'
el.style.bottom = y + 'px'
switchPosition .endX = x
switchPosition .endY = y
e.preventDefault()
}
})
}
})
使用
<template>
<div>
<h1 v-drag class="drag"></h1>
</div>
</template>
<script>
export default {
name: 'homePage',
components:{
},
data() {
return {
}
},
created(){
},
computed:{
},
methods:{
},
mounted() {
console.log(this.$el);
// this.$el.style.backgroundColor = "green"
},
}
</script>
<style lang="scss" scoped>
</style>
<style scoped>
.drag{
width: 200px;
height: 200px;
background: #ccc;
text-align: center;
line-height: 200px;
color: #fff;
cursor:move;
position: fixed;
z-index: 99;
right: 10px;
bottom: 85px;
width: 40px;
height: 40px;
}
</style>
Vue.directive('touch', {
// 滑动指令
bind: function (el, binding, vnode) {
// 传入参数滑动模式 press swipeRight swipeLeft swipeTop swipeDowm
var touchType = binding.arg;
var timeOutEvent = 0;
var direction = '';
// 滑动处理
var startX, startY;
// 返回角度
function GetSlideAngle(dx, dy) {
return Math.atan2(dy, dx) * 180 / Math.PI;
}
// 根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
function GetSlideDirection(startX, startY, endX, endY) {
var dy = startY - endY;
var dx = endX - startX;
var result = 0;
// 判断滑动距离, 如果滑动距离太短
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
return result;
}
// 判断滑动角度
var angle = GetSlideAngle(dx, dy);
if (angle >= -45 && angle < 45) {
result = 'swipeRight';
} else if (angle >= 45 && angle < 135) {
result = 'swipeUp';
} else if (angle >= -135 && angle < -45) {
result = 'swipeDown';
}
else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 'swipeLeft';
}
return result;
}
// 监听开始
el.addEventListener('touchstart', function (ev) {
startX = ev.touches[0].pageX;
startY = ev.touches[0].pageY;
//判断长按操作
timeOutEvent = setTimeout(() =>{
timeOutEvent = 0 ;
if(touchType === 'press'){
binding.value()
}
} , 500);
}, false);
// 监听移动
el.addEventListener('touchmove' , function (ev) {
clearTimeout(timeOutEvent)
timeOutEvent = 0;
});
// 监听结束
el.addEventListener('touchend', function (ev) {
var endX, endY;
endX = ev.changedTouches[0].pageX;
endY = ev.changedTouches[0].pageY;
direction = GetSlideDirection(startX, startY, endX, endY);
clearTimeout(timeOutEvent)
switch (direction) {
case 0:
break;
case 'swipeUp':
if(touchType === 'swipeUp'){
binding.value()
}
break;
case 'swipeDown':
if(touchType === 'swipeDown'){
binding.value()
}
break;
case 'swipeLeft':
if(touchType === 'swipeLeft'){
binding.value()
}
break;
case 'swipeRight':
if(touchType === 'swipeRight'){
binding.value()
}
break;
default:
}
}, false);
}
})
使用指令
<div
v-touch:swipeLeft="leftSlide"
v-touch:swipeRight="rightSlide"
v-touch:swipeUp="upSlide"
v-touch:swipeDown="downSlide"
v-touch:press="press"
class="slide"
></div>
leftSlide() {
console.log("左滑");
},
rightSlide() {
console.log("右滑");
},
upSlide() {
console.log("上滑");
},
downSlide() {
console.log("下滑");
},
press() {
console.log("长按");
},