《Vue》你的弹窗能拖动吗?Vue自定义指令实现可拖动弹窗

return data;

}

在这里,我们允许用户输入的动态参数类型有两种,一种是字符串类型,一种是对象类型,除此之外的所有类型都会被认为是非法类型:

  1. 字符串:那么我们就认为拖动和实际移动的DOM是同一种;

  2. 对象:根据key进行一次遍历,并逐一对key对应的值进行覆盖;

最后会得到一个全新的合法的动态参数,并返回;

合并修饰符


动态参数合并完了,那么紧接着就是合并修饰符,和动态参数一样的规则:用户设定优先,如果没有设定,启动默认修饰符,这一块主要合并的是一些比如是否可以超屏拖动的参数,比如设定:

// 允许超屏拖动

开启超屏拖动后,那么被拖动的DOM边界可以超出屏幕之外,这个功能是什么用处呢?具体举例一下,比如:实际项目中图片预览功能,某张图片被放大后,因为视角不方便,需要将图片中待阅读的部分拖动到屏幕中央,因为图片放大后肯定有部分会被移出屏幕,因此就需要超屏拖动了,如果不开启,那么被拖动的DOM永远只能在屏幕内部拖动;

具体参数合并代码如下:

/**

  • 处理修饰符

  • @param {Object} modifiers 修饰符原始数据

  • @returns {Object} 期望的修饰符对象

*/

export function handleModifiers(modifiers) {

// 容错判断,排除非对象

if (!commom.isObj(modifiers)) {

console.error(“类型错误,修饰符必须是对象类型”);

return false;

}

return {

dirPosition: ABSOLUTE,

dirAll: modifiers.all ? ALL : null,

};

}

挺简单的代码,首先是判断了修饰符参数是否是一个对象,做了一个简单的容错,其实这里不对,如果修饰符如果设定异常,那么返回的应该是默认参数,而不是false,这里有问题

拖动验证


肯定有小伙伴觉得奇怪,为什么会有一个拖动验证,很简单,有时候这个拖动功能也是需要开启的,比如,有时候我们希望这个DOM默认是不可以拖动的,只有当开启了某个开关后才能拖动,用法示例:

// isMove判断当前拖动是否生效,true-可拖动,false-不可拖动

代码上的话,我们只需要对这个value值做一个判断即可,如果vlaue值是false,直接返回,不给走代码就行了

// false-不可拖动

if (commom.isBol(binding.value) && !binding.value) {

return false;

}

初始化坐标


这一步中,主要的作用就是初始化DOM的坐标,一是我们得获取到当前DOM现有的坐标,二是初始化一些下面步骤中需要使用到的变量

// 坐标,这个是初始化下面步骤需要的变量

let x = 0,

y = 0;

let offsetLeft = 0,

offsetRight = 0;

具体初始化的时机就在于我们鼠标按下的那一刻,在那一刻,我们可以获取到当前被拖动DOM的初始偏移量,我们需要将这些偏移量保存下来做计算:

// 触发鼠标左击

dom.click.onmousedown = function(event) {

// 阻止默认事件和冒泡

event.preventDefault();

event.stopPropagation();

// 设置初始坐标

x = event.pageX;

y = event.pageY;

// 设置初始偏移位置

offsetLeft = dom.move.offsetLeft;

offsetRight = dom.move.offsetTop;

onMove = true;

};

计算坐标


在计算坐标这一部分,一共有四个坐标需要计算:

  • x轴上的最小坐标,最大坐标

  • y轴上的最小坐标,最大坐标

当我们拖动DOM的时候,需要实时计算出对应的坐标

最小值计算

/**

  • 计算在X轴/Y轴可拖动的最小值

  • @param {Obejct} modifiers 修饰符

  • @param {String} position X轴/Y轴

  • @param {HTMLElement} el

  • @returns {Number} 可拖动的最小距离值

*/

export function computedMin(modifiers, position, el) {

// 容错判断,排除非对象的修饰符

if (!commom.isObj(modifiers)) {

console.error(“类型错误,修饰符必须是对象类型”);

return false;

}

// 容错判断,排除非DOM

if (!(el instanceof HTMLElement)) {

console.error(“类型错误,绑定对象必须是HTMLElement”);

return false;

}

// 获得x轴最小值

const minXDistance = parseInt(window.getComputedStyle(el).width);

// 获得Y轴最小值

const minYDistance = parseInt(window.getComputedStyle(el).height);

// 超屏拖动

if (modifiers.dirAll === ALL) {

return position === “width” ? -minXDistance : -minYDistance;

}

// 非超屏拖动

else {

return position === “width” ? 0 : 0;

}

}

因为x轴和y轴上都存在最小值的计算,因此就通用了一下

最大值计算

/**

  • 计算在X轴/Y轴可拖动的最大值

  • @param {Object} modifiers 修饰符

  • @param {String} position X轴-width,Y轴-height

  • @param {HTMLElement} el 绑定的元素

  • @returns {Number} 可拖动的最大距离值

*/

export function computedMax(modifiers, position, el) {

// 容错判断,排除非对象

if (!commom.isObj(modifiers)) {

console.error(“类型错误,修饰符必须是对象类型”);

return false;

}

// 容错判断,排除非DOM

if (!(el instanceof HTMLElement)) {

console.error(“类型错误,绑定对象必须是HTMLElement”);

return false;

}

// 无上级元素

if (!el.parentNode) {

return 0;

}

// 获得X轴最大值

const maxXDistance = parseInt(window.getComputedStyle(el).width);

// 获得Y轴最大值

const maxYDistance = parseInt(window.getComputedStyle(el).height);

// 超屏拖动

if (modifiers.dirAll === ALL) {

return position === “width”

? el.parentNode.clientWidth + maxXDistance
el.parentNode.clientHeight + maxYDistance;

}

// 非超屏拖动

else {

return position === “width”

? el.parentNode.clientWidth - maxXDistance
el.parentNode.clientHeight - maxYDistance;

}

}

同理,最大值的计算也存在x轴和y轴上的,所以也通用了一下

这样在这一步中,我们就得到了四个坐标,分别是:x轴的最小值和最大值,y轴上的最小值和最大值

设置坐标


在这一步就是对DOM进行坐标设置了,我们需要实时修改DOM的left和top的值,达到DOM的位置的变化,当然,这部分代码需要写在mousemove中,以及鼠标松开时,移除拖动事件

// 触发鼠标拖动

document.onmousemove = function(e) {

// 判断控制阀确认是否可拖动

if (!onMove) return false;

// 初始化位置坐标

let initMouseX = e.pageX;

let initMouseY = e.pageY;

// 初始化偏移坐标

let offsetX = initMouseX - (x - offsetLeft);

let offsetY = initMouseY - (y - offsetRight);

// 获得X轴最大值

let maxX = computedMax(modifiers, “width”, dom.move);

// 获得X轴最小值

let minX = computedMin(modifiers, “width”, dom.move);

// 获得Y轴最大值

let maxY = computedMax(modifiers, “height”, dom.move);

// 获得Y轴最小值

let minY = computedMin(modifiers, “height”, dom.move);

// 拖动后坐标

offsetX = offsetX > maxX ? maxX : offsetX < minX ? minX : offsetX;

offsetY = offsetY > maxY ? maxY : offsetY < minY ? minY : offsetY;

// 设置坐标

dom.move.style.left = offsetX + “px”;

dom.move.style.top = offsetY + “px”;

};

// 触发鼠标左击释放

document.onmouseup = function() {

// 关闭控制阀

onMove = false;

// 清空状态

document.onmousemove = document.onmouseup = null;

// 非函数时返回,函数时触发函数

if (!commom.isFunction(binding.value)) {

return false;

}

binding.value.call(this);

};

当然,这里还有一个小细节,就是如果我们的value值是一个函数的时候,那么松开鼠标的时候需要触发函数

代码


以下为v-move的主体代码,有兴趣的小伙伴可以试下

/**

  • @Description 本指令应用于设置DOM元素为可拖动状态,具体用法见文档

*/

// 引入工具函数

import commom from “…/…/Utils/common”;

// dom的position值

const ABSOLUTE = “absolute”;

// 是否可以超屏拖动

const ALL = “all”;

/**

  • 处理修饰符

  • @param {Object} modifiers 修饰符原始数据

  • @returns {Object} 期望的修饰符对象

*/

export function handleModifiers(modifiers) {

// 容错判断,排除非对象

if (!commom.isObj(modifiers)) {

console.error(“类型错误,修饰符必须是对象类型”);

return false;

}

return {

dirPosition: ABSOLUTE,

dirAll: modifiers.all ? ALL : null,

};

}

// 默认参数

const DEFAULT_CONFIG = {

move: “”,

click: “”,

};

/**

  • 处理动态参数

  • @param {String | Obejct} params 动态参数原始数据

  • @returns {Obejct} 期望的动态参数对象

*/

export function handleParams(params) {

const data = {};

Object.assign(data, DEFAULT_CONFIG);

// 动态参数类型

switch (commom.getType(params)) {

// 字符串

case “[object String]”:

data.move = params;

data.click = params;

break;

// 对象

case “[object Object]”:

for (let item in data) {

if (!Object.prototype.hasOwnProperty.call(data, item)) continue;

// 容错,判断是否为undefined

data[item] = commom.isUndefined(params[item])

? data[item]
params[item];

}

break;

default:

// 启用默认参数

break;

}

return data;

}

/**

  • 计算在X轴/Y轴可拖动的最大值

  • @param {Object} modifiers 修饰符

  • @param {String} position X轴-width,Y轴-height

  • @param {HTMLElement} el 绑定的元素

  • @returns {Number} 可拖动的最大距离值

*/

export function computedMax(modifiers, position, el) {

// 容错判断,排除非对象

if (!commom.isObj(modifiers)) {

console.error(“类型错误,修饰符必须是对象类型”);

return false;

}

// 容错判断,排除非DOM

if (!(el instanceof HTMLElement)) {

console.error(“类型错误,绑定对象必须是HTMLElement”);

return false;

}

// 无上级元素

if (!el.parentNode) {

return 0;

}

// 获得X轴最大值

const maxXDistance = parseInt(window.getComputedStyle(el).width);

// 获得Y轴最大值

const maxYDistance = parseInt(window.getComputedStyle(el).height);

// 超屏拖动

if (modifiers.dirAll === ALL) {

return position === “width”

? el.parentNode.clientWidth + maxXDistance
el.parentNode.clientHeight + maxYDistance;

}

// 非超屏拖动

else {

return position === “width”

? el.parentNode.clientWidth - maxXDistance
el.parentNode.clientHeight - maxYDistance;

}

}

/**

  • 计算在X轴/Y轴可拖动的最小值

  • @param {Obejct} modifiers 修饰符

  • @param {String} position X轴/Y轴

  • @param {HTMLElement} el

  • @returns {Number} 可拖动的最小距离值

*/

export function computedMin(modifiers, position, el) {

// 容错判断,排除非对象的修饰符

if (!commom.isObj(modifiers)) {

console.error(“类型错误,修饰符必须是对象类型”);

return false;

}

// 容错判断,排除非DOM

if (!(el instanceof HTMLElement)) {

console.error(“类型错误,绑定对象必须是HTMLElement”);

return false;

}

// 获得x轴最小值

const minXDistance = parseInt(window.getComputedStyle(el).width);

// 获得Y轴最小值

const minYDistance = parseInt(window.getComputedStyle(el).height);

// 超屏拖动

if (modifiers.dirAll === ALL) {

return position === “width” ? -minXDistance : -minYDistance;

}

// 非超屏拖动

else {

return position === “width” ? 0 : 0;

}

}

/**

  • 获得DOM

  • @param {String} className dom的类名

  • @param {HTMLElement} el DOM

  • @returns {HTMLElement} 获得的DOM

*/

export function getDOM(className, el) {

// 容错判断,排除非DOM

最后

今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。不过光是看完而不去付出行动,或者直接进入你的收藏夹里吃灰,那么我写这篇文章就没多大意义了。所以看完之后,还是多多行动起来吧!

可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个实现拖拽弹窗头部的 Vue 自定义指令: ```js // draggable.js const draggable = { bind: function(el, binding, vnode) { let isDragging = false; let x = 0; let y = 0; let initialX = 0; let initialY = 0; el.style.cursor = 'move'; function mouseDown(e) { initialX = e.clientX - x; initialY = e.clientY - y; if (e.target === el) { isDragging = true; } } function mouseMove(e) { if (isDragging) { x = e.clientX - initialX; y = e.clientY - initialY; el.style.transform = `translate3d(${x}px, ${y}px, 0)`; } } function mouseUp(e) { isDragging = false; } el.addEventListener('mousedown', mouseDown); el.addEventListener('mousemove', mouseMove); el.addEventListener('mouseup', mouseUp); el._cleanup = function() { el.removeEventListener('mousedown', mouseDown); el.removeEventListener('mousemove', mouseMove); el.removeEventListener('mouseup', mouseUp); }; }, unbind: function(el, binding, vnode) { el._cleanup && el._cleanup(); } }; export default draggable; ``` 这个自定义指令实现了在鼠标按下时将弹窗拖动到指定位置的功能。指令会在绑定时给指定的元素添加 mousedown、mousemove 和 mouseup 事件监听器,当鼠标按下时,记录下当前鼠标位置和弹窗位置的偏移量,并将 isDragging 状态设置为 true;当鼠标移动时,根据当前鼠标位置和偏移量计算出弹窗的新位置,并使用 CSS transform 属性将其移动到指定位置;当鼠标松开时,将 isDragging 状态设置为 false。 使用这个自定义指令非常简单,只需要在弹窗头部元素上添加 v-draggable 指令即可: ```html <template> <div class="modal"> <div class="modal-header" v-draggable> <h3 class="modal-title">{{ title }}</h3> <button class="modal-close" @click="$emit('close')">x</button> </div> <div class="modal-body"> <slot></slot> </div> </div> </template> <script> import draggable from './draggable'; export default { directives: { draggable }, props: { title: String, visible: Boolean } }; </script> <style scoped> .modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: white; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); } .modal-header { display: flex; align-items: center; justify-content: space-between; padding: 10px; background-color: #eee; cursor: move; } .modal-title { margin: 0; } .modal-close { border: none; background-color: transparent; font-size: 20px; cursor: pointer; } </style> ``` 在这个组件中,我们将 v-draggable 指令绑定到了弹窗头部的元素上。指令会自动添加鼠标事件监听器,并在鼠标拖拽时实现弹窗的移动效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值