前端实现一个绕圆心转动的功能

2024-03-06 23:12

本文主要是介绍前端实现一个绕圆心转动的功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言: 今天遇到了一个有意思的需求,如何实现一个元素绕某一个点来进行圆周运动,用到了一些初高中的数学知识,实现起来还是挺有趣的,特来分享🎁。

一. 效果展示

  1. 我们先展示效果,如下图所示,放大镜会绕着我们设定好的一个圆心来进行圆周运动。相信这个需求还是比较常见的,接下来我会一步一步讲解我的实现过程。
    saomiao.gif

  2. tisp: 注意,放大镜是一张 png 的图片,并不是我们手动画出来的

二. 回忆画圆的步骤

  1. 不要着急想代码如何实现,我们先思考如何手动画一个圆?在这里我们很容易想到一个初中学习数学用到的工具----圆规,如下图所示。
    IMB_DQj3Fs.gif

  2. 从上图可以得知,手动画一个圆其实很简单,大致分为三个步骤。

    1. 确定圆心
    2. 确定半径
    3. 旋转圆规
  3. 前两个我们可以很轻松的根据我们的需求设定好,圆心就是确定页面上的一个点坐标而已,半径也是我们手动写死的一个变量值而已,关键点其实就在于第三步我们如何实现。

  4. 在实现第三步之前,我会提前预热一下弧度的知识,因为等会我们需要用到 sincos 的相关知识,而在 js 中的相关三角函数的参数必须为弧度,所以我们需要通过角度来计算得出弧度
    image.png

  5. 我们大家都知道,一个圆有360度,且度数弧度之间有一个转换公式,如下。
    image.png

  6. 得知了转换关系,我们就可以定义一个变量 angle 来表示我们这个 div 做圆周运动时绕圆心转过的角度,则弧度(radian)radian =(angle*π)/180

三. 回忆 sin 和 cos

  1. 我们先在草稿纸上演练一遍我们的逻辑是否可行。让我们先准备一个矩形来代表我们的页面,然后确定一个点来作为圆心
    image.png

  2. 圆心的位置坐标其实很简单,不要想复杂了,就是相对于页面的位置而已,我们记住这个 (100,100) 的值,等会它会作为我们实现圆周运动的圆心位置。
    image.png

  3. 接下来确定半径,这里我就随便取一个 50 作为半径值。

  4. 假设这是我们的 div 已经绕圆心转了一些弧度以后的情景。
    image.png

  5. tips: 在这里有一个十分重要的概念,div 滑过的路径其实是由无数个不同坐标位置的点构成的。
    image.png

  6. 接下来就是本文的关键部分,请大家喝口水认真听讲。我们取一个中间时刻,假设 div 此时做圆周运动到了点 B ,那么我们的问题就转化为了如何求点 B 的坐标信息。(不要忘了,坐标信息其实就是相对于页面上的 left 和 top 而已。)
    image.png

  7. 一步一步来,我们先求 X 坐标的值,换算下来其实就是要求 a 的长度。
    image.png

  8. 此时我们准备好拿出已有的数据,来套公式即可。度数我们是有的,因为我们已经用变量 angle 来假设我们绕过的度数,则弧度radian =(angle*π)/180 ,然后根据三角函数的正弦定理可得 sin(rad)=a/radius

  9. 此时半径已知,sin(rad) 已知,则 a=sin(rad) X 50,然后加上圆心的 x 坐标值 100,即可得出此时 B 点相对于页面的 left 值。

  10. Y 坐标同理,只不过公式换为 cos 即可,换算过程不再重复,但是需要注意的点是,我们计算 Y 坐标 的目的其实在求的是 B 点的 top 值,又因为我们前端的坐标Y轴其实和数学的坐标Y轴的正负极是相反的,所以我们其实要算的值是这一段的距离。如下图:
    image.png

  11. 即 B 点的 top 值为圆心的 Y 坐标值100 - 距离b,至此我们所有需要的数据都已经获得,接下来就是用代码验证我们思路的可行性。

四. 实现圆周运动函数

  1. 经过上面的解释,我们接下来就可以动手写代码了。

  2. 我们先简单设计一个画面,内容很简单,一个普通的蓝底页面中间有一个居中的红色 div,要想脱离文档流自行移动,那么这个 div 肯定是 absolute 绝对定位。
    image.png

  3. 然后提前定义好我们需要用到的变量,为接下来的工作做准备。
    image.png

  4. 因为我们是需要不断通过改变 divX 偏移值和 Y 偏移值来实现圆周运动的,(先不考虑性能和优化)所以很容易想到 setInterval,所以现在你的代码应该是这个样子。

    // 1. 确定圆心的位置
    const centerPointer = { x: 100, y: 100 };
    // 2. 确定圆周运动的半径
    const radius = 50;
    // 3. div 运动时的角度
    let angle = 0;function run() {setInterval(() => {}, 16);
    }
    
  5. 首先我们需要让我们定义好的 angle 自增。

    function run() {setInterval(() => {angle += 1;}, 16);
    }
    
  6. 接下来计算 ab 的值,根据我们上面的出的公式 radian =(angle*π)/180sin(rad)*radius=a,可以写出下面的代码。

    function run() {setInterval(() => {angle += 1;const radian = (angle * Math.PI) / 180;const a = Math.sin(radian) * radius;const b = Math.cos(radian) * radius;}, 16);
    }
    
  7. 根据我们在上面的结论,divtop 值为 半径 + a left 值为 半径 - b,你现在的 run函数 代码应该是这样的。

    
    function run() {setInterval(() => {if (!box.value) return;angle += 1;const radian = (angle * Math.PI) / 180;const a = Math.sin(radian) * radius;const b = Math.cos(radian) * radius;const x = centerPointer.x + a;const y = centerPointer.y - b;box.value.style.left = x + "px";box.value.style.top = y + "px";}, 16);
    }
    
  8. 然后给我们的 div 打上 ref 那到这个 dom 元素。
    image.png

  9. onMounted 里执行我们的 run 函数,接下来就是见证奇迹的时刻。
    yuanzhou.gif

五. 源码

<script setup lang="ts">
import { ref, onMounted,  } from "vue";const box = ref<HTMLDivElement>();
// 1. 确定圆心的位置
const centerPointer = { x: 100, y: 100 };
// 2. 确定圆周运动的半径
const radius = 50;
// 3. div 运动时的角度
let angle = 0;function run() {setInterval(() => {if (!box.value) return;angle += 1;const radian = (angle * Math.PI) / 180;const a = Math.sin(radian) * radius;const b = Math.cos(radian) * radius;const x = centerPointer.x + a;const y = centerPointer.y - b;box.value.style.left = x + "px";box.value.style.top = y + "px";}, 16);
}onMounted(() => {run();
});
</script>
<template><div class="w-full h-full centered bg-blue relative flex flex-col centered"><div ref="box" class="absolute w-50px h-50px bg-red"></div></div>
</template>

六. 思考题

现在我们的 div顺时针运动,我们该如何让它逆时针运动呢?🤔

这篇关于前端实现一个绕圆心转动的功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/781673

相关文章

HTML5的input标签的`type`属性值详解和代码示例

《HTML5的input标签的`type`属性值详解和代码示例》HTML5的`input`标签提供了多种`type`属性值,用于创建不同类型的输入控件,满足用户输入的多样化需求,从文本输入、密码输入、... 目录一、引言二、文本类输入类型2.1 text2.2 password2.3 textarea(严格

MyBatis-Plus逻辑删除实现过程

《MyBatis-Plus逻辑删除实现过程》本文介绍了MyBatis-Plus如何实现逻辑删除功能,包括自动填充字段、配置与实现步骤、常见应用场景,并展示了如何使用remove方法进行逻辑删除,逻辑删... 目录1. 逻辑删除的必要性编程1.1 逻辑删除的定义1.2 逻辑删php除的优点1.3 适用场景2.

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

Python轻松实现Word到Markdown的转换

《Python轻松实现Word到Markdown的转换》在文档管理、内容发布等场景中,将Word转换为Markdown格式是常见需求,本文将介绍如何使用FreeSpire.DocforPython实现... 目录一、工具简介二、核心转换实现1. 基础单文件转换2. 批量转换Word文件三、工具特性分析优点局

Springboot3统一返回类设计全过程(从问题到实现)

《Springboot3统一返回类设计全过程(从问题到实现)》文章介绍了如何在SpringBoot3中设计一个统一返回类,以实现前后端接口返回格式的一致性,该类包含状态码、描述信息、业务数据和时间戳,... 目录Spring Boot 3 统一返回类设计:从问题到实现一、核心需求:统一返回类要解决什么问题?

Java使用Spire.Doc for Java实现Word自动化插入图片

《Java使用Spire.DocforJava实现Word自动化插入图片》在日常工作中,Word文档是不可或缺的工具,而图片作为信息传达的重要载体,其在文档中的插入与布局显得尤为关键,下面我们就来... 目录1. Spire.Doc for Java库介绍与安装2. 使用特定的环绕方式插入图片3. 在指定位

Java使用Spire.Barcode for Java实现条形码生成与识别

《Java使用Spire.BarcodeforJava实现条形码生成与识别》在现代商业和技术领域,条形码无处不在,本教程将引导您深入了解如何在您的Java项目中利用Spire.Barcodefor... 目录1. Spire.Barcode for Java 简介与环境配置2. 使用 Spire.Barco