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

news2024/12/24 20:45:39

说在前面

🎈鼠标控制元素旋转功能大家应该都不陌生了吧,今天我们一起来看看怎么编写一个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,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1857947.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

选择门店收银系统要考虑哪些方面?美业系统Java源码分享私

开店前的一个重要事件就是选择门店收银软件/系统&#xff0c;尤其是针对美容、医美等美业门店&#xff0c;一个优秀专业的系统十分重要&#xff0c;它必须贴合门店的经营需求&#xff0c;提供更全面、便捷、高效的管理功能&#xff0c;帮助提升门店的服务质量和经营效益。 以下…

品牌策划背后的秘密:我为何对此工作情有独钟?

你是否曾有过一个梦想&#xff0c;一份热爱&#xff0c;让你毫不犹豫地投身于一个行业&#xff1f; 我就是这样一个对品牌策划充满热情的人。 从选择职业到现在&#xff0c;我一直在广告行业里“混迹”&#xff0c;一路走来&#xff0c;也见证了许多对品牌策划一知半解的求职…

CPsyCoun:心理咨询多轮对话自动构建及评估方法

CPsyCoun: A Report-based Multi-turn Dialogue Reconstruction and Evaluation Framework for Chinese Psychological Counseling 在大模型应用于心理咨询领域&#xff0c;目前开源的项目有&#xff1a; https://github.com/SmartFlowAI/EmoLLM &#xff08;集合&#xff0c;…

mp4转换成mp3怎么转?教你几种值得收藏的转换方法!

mp4转换成mp3怎么转&#xff1f;MP4&#xff0c;这一深入人心的数字多媒体容器格式&#xff0c;无疑在当今数字世界中占据了一席之地&#xff0c;那么&#xff0c;它究竟有何过人之处呢&#xff1f;首先&#xff0c;MP4的跨平台兼容性是其一大亮点&#xff0c;不论是在Windows的…

(一)、配置服务器的多个网卡路由,访问多个不同网络段

一、现场网络关系说明 有这么一个需要&#xff0c;服务器有三个网口&#xff0c;网口一需要访问外网&#xff0c;网口二需要访问内网1&#xff0c;网口2需要访问内网2。需要配置路由来满足该网络访问需要。 图1 现场网络关系 二、配置教程 步骤1&#xff1a; a、命令行输入…

A股羊群效应CSSD CSAD数据与Stata代码数据(2000-2023)

数据来源 参考马丽老师&#xff08;2016&#xff09;的做法&#xff0c;股价数据来源于东方财富网&#xff0c;采用上证180指数及构成上证180指数样本股日收盘价数据作为样本。上证180指数自2002年7月1日起正式发布&#xff0c;其样本股是在所有 A 股股票中抽取最具市场代表性…

react-native在IOS上集成百度地图详解

export default class BaiDuMapTest extends Component { render() { return ( ); } } const styles StyleSheet.create({ container: { flex: 1, justifyContent: ‘center’, alignItems: ‘center’, backgroundColor: ‘#F5FCFF’, }, welcome: { fontSize:…

若依-前后端分离项目学习

第一天&#xff08;6.24&#xff09; 具体参考视频 b站 楠哥教你学Java 【【开源项目学习】若依前后端分离版&#xff0c;通俗易懂&#xff0c;快速上手】 https://www.bilibili.com/video/BV1HT4y1d7oA/?share_sourcecopy_web&vd_sourcecd9334b72b49da3614a4257…

公司logo设计大全怎么找?直接帮你设计logo

公司logo设计大全怎么找&#xff1f;在品牌塑造的过程中&#xff0c;Logo无疑是至关重要的一环。一个优秀的Logo不仅能够有效传达公司的核心理念和品牌形象&#xff0c;还能在消费者心中留下深刻的印象。然而&#xff0c;对于许多初创公司或小型企业来说&#xff0c;制作出适合…

【稀疏三维重建】Flash3D:单张图像重建场景的GaussianSplitting

项目主页&#xff1a;https://www.robots.ox.ac.uk/~vgg/research/flash3d/ 来源&#xff1a;牛津、澳大利亚国立 文章目录 摘要1.引言2.相关工作3.方法3.1 背景&#xff1a;从单个图像中重建场景3.2 单目前向的多个高斯 4.实验4.14.2 跨域新视角合成4.3 域内新视图合成 摘要 F…

OpenBayes 教程上新 | 5 秒完成高考作文,Llama 3-Chinese-Chat Demo 上线!

Llama 3 自发布以来备受关注&#xff0c;并被誉为「迄今为止最好的开源大模型」&#xff0c;但 Llama 3 在支持中文方面仍然存在一些问题&#xff0c;比如在使用中文提问时&#xff0c;经常发生英文回复或者中英混合回复的情况&#xff0c;非常不方便。 使用中文向 Llama 3-8B-…

web应用-Nginx学习笔记01-应用的组成结构,配置项的分类和理解

参考来源&#xff1a; 在线文档&#xff1a;Nginx开发从入门到精通&#xff0c;https://docs.pythontab.com/nginx/nginx-book/ 极客专栏&#xff1a;nginx核心100讲&#xff0c;作者:陶辉 书籍&#xff1a;《深入理解Nginx&#xff1a;模块开发与架构解析》第二章 参考来源&a…

ONLYOFFICE 桌面编辑器 8.1

ONLYOFFICE 桌面编辑器 8.1 ONLYOFFICE 简介一、轻松编辑器 PDF 文件二、用幻灯片版式快速修改幻灯片三、无缝切换文档编辑、审阅和查看模式四、**改进从右至左语言的支持 & 新的本地化选项**五、隐藏“连接到云”板块六、在演示文稿中播放视频和音频文件七、版本 8.1&…

AI视频改字系统+五端兼容+卡密兑换+内置素材,系统搭建部署

目录 前言&#xff1a; 一、AI视频改字系统是什么 二、AI视频改字系统的功能 三、总结 前言&#xff1a; AI视频改字是利用套模板的原理&#xff0c;对短视频的模板进行更改&#xff0c;从而生成新的短视频。当然这个AI短视频改字系统都是有素材的&#xff0c;不用自己上传…

公共云遣返的经济性

在过去的几年里&#xff0c;显而易见的是&#xff0c;尽管公有云具有所有优势&#xff0c;但它并不能大规模地节省成本。它在一定程度上提高了生产力&#xff0c;但不会降低您的成本。公有云的好处在于&#xff0c;它提供了一个非常强大的价值主张&#xff0c;即基础设施可以立…

开放式供应链,践行智能费控4.0模式的基石

消费供应链在企业传统费用管理模式中是极度边缘化的存在&#xff0c;财务将其界定在职责之外&#xff0c;行政则因为价格政策不断的摇摆在不同的渠道之间。到了3.0移动报销时代&#xff0c;费控报销SaaS也只是简单的通过跳转或捆绑的方式来完成消费场景的接入&#xff0c;而将重…

植物大战僵尸杂交版2.1版本终于来啦!游戏完全免费

在这个喧嚣的城市里&#xff0c;我找到了一片神奇的绿色世界——植物大战僵尸杂交版。它不仅是一款游戏&#xff0c;更像是一扇打开自然奥秘的窗户&#xff0c;让我重新认识了植物和自然的力量。 植物大战僵尸杂交版最新绿色版下载链接&#xff1a; https://pan.quark.cn/s/d6…

Ncat 网络工具

文章目录 Ncat 网络工具简介基础说明使用示例基础示例两个 ncat 互相连接访问 WEB 网站充当 WEB 服务器 文件传输与 Broker文件传输作为 Broker &#xff0c;广播消息作为 Broker&#xff0c;成为文件传输中介 多人聊天室使用 SSL&#xff0c;提高传输安全性执行命令行访问控制…

Sapphire开发日志 (十) 关于页面

关于页面 任务介绍 关于页面用户对我组工作量的展示。 实现效果 代码解释 首先封装一个子组件用于展示用户头像和名称。 const UserGrid ({src,name,size,link, }: {src: any;name: any;size?: any;link?: any; }) > (<Box sx{{ display: "flex", flex…

模组硬件通用丨模组USB电路设计指南

USB&#xff08;全称&#xff1a;Universal Serial Bus&#xff09;是一种串口总线标准&#xff0c;也是一种输入输出接口的技术规范&#xff0c;广泛应用于个人电脑和移动设备等信息通讯产品&#xff0c;并扩展至摄影器材、数字电视&#xff08;机顶盒&#xff09;、游戏机等相…