元素旋转?一个vue指令搞定

45 篇文章 3 订阅

说在前面

🎈鼠标控制元素旋转功能大家应该都不陌生了吧,今天我们一起来看看怎么编写一个vue指令来实现元素旋转功能吧!

效果展示

AI改图-JYeontu组件库 - Google Chrome 2024-05-01 05-34-53-720x387 (1).gif

体验地址

http://jyeontu.xyz/jvuewheel/#/JRotateView

实现思路

1、自定义指令对象

export default {
  inserted(el, binding) {
    // ...
  }
};

这里定义了一个Vue自定义指令,并通过inserted钩子函数,在元素被插入到DOM时执行相关的逻辑。

2、变量声明

let startingMouseAngle = 0;
let startingRotation = 0;

声明了两个变量,startingMouseAngle用于存储鼠标按下时的位置角度,startingRotation用于存储元素初始的旋转角度。

3、事件监听器

(1)防止文本选择
el.addEventListener("selectstart", function (event) {
  event.preventDefault();
});

通过监听selectstart事件来防止用户在旋转区域内选中文本。

(2)鼠标按下事件
el.addEventListener("mousedown", function (event) {
  // ...
});

当用户在元素上按下鼠标时,会触发mousedown事件。

4、鼠标按下时的逻辑

  1. 计算中心点坐标:获取元素的getBoundingClientRect来计算元素的中心点坐标centerXcenterY

  2. 记录初始角度:使用getAngle函数计算出鼠标相对于元素中心点的初始角度startingMouseAngle

  3. 记录初始旋转:调用getCurrentRotation函数获取并记录元素当前的旋转角度startingRotation

  4. 添加鼠标事件监听:向window添加mousemovemouseup事件监听器,分别用于旋转效果和停止旋转。

  5. 设置pointerEvents:设置el.style.pointerEvents = "none",以阻止鼠标事件在旋转区域上的其他交互。

5、旋转和停止旋转的函数

(1)停止旋转
function stopSpin() {
    window.removeEventListener("mousemove", spin);
    window.removeEventListener("mouseup", stopSpin);
    // 恢复旋转区域的鼠标事件
    el.style.pointerEvents = "auto";
}

当用户释放鼠标按钮时,调用stopSpin函数移除之前添加的mousemovemouseup事件监听器,并恢复旋转区域的鼠标事件。

(2)旋转逻辑
function spin(event) {
    const rect = el.getBoundingClientRect();
    const centerX = rect.left + rect.width / 2;
    const centerY = rect.top + rect.height / 2;
    const currentMouseAngle = getAngle(
        centerX,
        centerY,
        event.clientX,
        event.clientY
    );
    const deltaMouseAngle = currentMouseAngle - startingMouseAngle;
    let newRotation = startingRotation + deltaMouseAngle;
    newRotation = normalizeRotation(newRotation);
    el.style.transform = `rotate(${newRotation}deg)`;
}

spin函数计算当前鼠标位置与起始位置的夹角,然后更新元素的旋转角度。

6、旋转角度规范化

function normalizeRotation(rotation) {
    if (rotation >= 0) {
        return rotation % 360;
    } else {
        return (rotation % 360) + 360;
    }
}

normalizeRotation函数确保旋转角度在0到360度之间循环。

7、获取鼠标角度

function getAngle(centerX, centerY, mouseX, mouseY) {
    return (
        Math.atan2(mouseY - centerY, mouseX - centerX) * (180 / Math.PI)
    );
}

getAngle函数使用Math.atan2计算鼠标相对于元素中心点的角度。

8、获取当前旋转角度

function getCurrentRotation() {
    const transformStyle = window
        .getComputedStyle(el)
        .getPropertyValue("transform");
    const matrix = new DOMMatrixReadOnly(transformStyle);
    const angle = Math.acos(matrix.a) * (180 / Math.PI);
    return matrix.b < 0 ? -angle : angle;
}

getCurrentRotation函数尝试从元素的CSS变换属性中解析出当前的旋转角度。这里使用了DOMMatrixReadOnly,但请注意,这个API可能不被所有浏览器支持,且从CSS变换中解析角度可能不是最直接的方法。

完整代码

export default {
    inserted(el, binding) {
        let startingMouseAngle = 0;
        let startingRotation = 0;

        el.addEventListener("selectstart", function (event) {
            event.preventDefault();
        });
        el.addEventListener("mousedown", function (event) {
            const rect = el.getBoundingClientRect();
            const centerX = rect.left + rect.width / 2;
            const centerY = rect.top + rect.height / 2;
            startingMouseAngle = getAngle(
                centerX,
                centerY,
                event.clientX,
                event.clientY
            );
            startingRotation = getCurrentRotation();

            window.addEventListener("mousemove", spin);
            window.addEventListener("mouseup", stopSpin);
            // 阻止元素的拖动事件
            el.style.pointerEvents = "none";
        });

        function stopSpin() {
            window.removeEventListener("mousemove", spin);
            window.removeEventListener("mouseup", stopSpin);
            // 恢复旋转区域的鼠标事件
            el.style.pointerEvents = "auto";
        }

        function spin(event) {
            const rect = el.getBoundingClientRect();
            const centerX = rect.left + rect.width / 2;
            const centerY = rect.top + rect.height / 2;
            const currentMouseAngle = getAngle(
                centerX,
                centerY,
                event.clientX,
                event.clientY
            );
            const deltaMouseAngle = currentMouseAngle - startingMouseAngle;
            let newRotation = startingRotation + deltaMouseAngle;
            newRotation = normalizeRotation(newRotation);
            el.style.transform = `rotate(${newRotation}deg)`;
        }

        function normalizeRotation(rotation) {
            if (rotation >= 0) {
                return rotation % 360;
            } else {
                return (rotation % 360) + 360;
            }
        }

        function getAngle(centerX, centerY, mouseX, mouseY) {
            return (
                Math.atan2(mouseY - centerY, mouseX - centerX) * (180 / Math.PI)
            );
        }

        function getCurrentRotation() {
            const transformStyle = window
                .getComputedStyle(el)
                .getPropertyValue("transform");
            const matrix = new DOMMatrixReadOnly(transformStyle);
            const angle = Math.acos(matrix.a) * (180 / Math.PI);
            return matrix.b < 0 ? -angle : angle;
        }
    },
};

组件库

组件文档

目前该组件也已经收录到我的组件库,组件文档地址如下:
http://jyeontu.xyz/jvuewheel/#/JRotateView

组件内容

组件库中还有许多好玩有趣的组件,如:

  • 悬浮按钮
  • 评论组件
  • 词云
  • 瀑布流照片容器
  • 视频动态封面
  • 3D轮播图
  • web桌宠
  • 贡献度面板
  • 拖拽上传
  • 自动补全输入框
  • 图片滑块验证

等等……

组件库源码

组件库已开源到gitee,有兴趣的也可以到这里看看:https://gitee.com/zheng_yongtao/jyeontu-component-warehouse

觉得有帮助的可以点个star~

有什么问题或错误可以指出,欢迎pr~

有什么想要实现的组件或想法可以联系我~

公众号

关注公众号『前端也能这么有趣』,获取更多有趣内容。

发送『组件库』获取源码

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

  • 33
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是对你提出的前端Vue面试题的回答: 1. Vue.js是一款流行的JavaScript框架,用于构建用户界面。Vue.js的特点是简单易学、灵活性强、性能高、可组合性好等。 2. Vue.js的双向数据绑是通过使用数据劫持和发布/订阅模式实现的。当数据发生变化时,Vue.js会自动更新视图,反之亦然。 3. Vue.js中的生命周期钩子函数有8个,分别是beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy和destroyed。它们分别在不同的时刻被调用,用于执行一些特的操作。 4. Vue组件是Vue.js应用程序中的可重用代码块。通过义一个Vue组件,可以将其作为一个独立的模块来使用。Vue组件通常包括模板、样式和逻辑等内容。 5. Vue.js中的指令是一种用于向DOM元素添加特殊行为的语法。常用的指令有v-if、v-show、v-for、v-bind和v-on等等。 6. Vue.js中的路由是用于管理Vue应用程序中页面之间导航的方式。可以通过Vue Router插件来实现路由功能。 7. Vue.js中的过滤器是一种用于格式化文本的功能。可以通过义一个过滤器来将数据进行格式化并渲染到HTML中。 8. Vue.js中的计算属性是一种用于动态计算Vue组件的属性值的方式,与方法不同的是,计算属性具有缓存和依赖检测的特点。 9. 在Vue.js中,可以通过Vue.directive()方法来义自指令。自指令通常用于扩展Vue.js的功能。 10. Vue.js的单文件组件是一种将模板、样式和逻辑封装在一个文件中的方式,以便于组件的管理和维护。可以通过Vue CLI工具来创建和使用单文件组件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JYeontu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值