【sgOvalMenu】自定义组件:椭圆形菜单,菜单按钮可以随着椭圆轨迹进行循环运动

 

特性:

  1. 可以设置椭圆轨迹宽度、高度 
  2. 可以设置椭圆轨迹旋转角度,并且自动纠偏菜单文字水平状态
  3. 可以设置运动轨迹坐标移动步长
  4. 可以设置运动轨迹改变频率
  5. 可以设置顺时针还是逆时针旋转

sgOvalMenu源码 

<template><div :class="$options.name" :style="style"><div class="ovalMenuBtn" v-for="(a, i) in menubtns" :key="i" :style="a.style" @click="$emit(`click`, a);"><slot :data="a"></slot></div></div>
</template>
<script>
export default {name: 'sgOvalMenu',data() {return {style: {},coordinates: [],step_: 0,//按钮在椭圆轨道上面移动的步长time_: 0,//按钮坐标变化的时间间隔interval: null,menubtns: [],}},props: ["width",//椭圆的长直径"height",//椭圆的短直径"rotate",//椭圆旋转角度"step",//按钮在椭圆轨道上面移动的步长"time",//按钮坐标变化的时间间隔"clockwise",//顺时针运动(boolean)"data",//椭圆上面的按钮数据],watch: {width: {handler(d) {this.style.width = `${d || 800}px`;}, deep: true, immediate: true,},height: {handler(d) {this.style.height = `${d || 400}px`;}, deep: true, immediate: true,},rotate: {handler(d) {this.style.rotate = `${d || 0}deg`;this.setProperty();}, deep: true, immediate: true,},step: {handler(d) {this.step_ = d || 2;}, deep: true, immediate: true,},time: {handler(d) {this.time_ = d || 200;}, deep: true, immediate: true,},data: {handler(d) {if (d) {this.menubtns = JSON.parse(JSON.stringify(d));this.getCoordinates(d => {this.coordinates = d;this.triggerAnimate();});}}, deep: true, immediate: true,},},destroyed() {clearInterval(this.interval);},created() {},mounted() {this.setProperty();},computed: {},methods: {setProperty() {this.$el && this.$el.style.setProperty("--rotate", `${-1 * parseFloat(this.style.rotate || 0)}deg`); //js往css传递局部参数},triggerAnimate() {clearInterval(this.interval);this.interval = setInterval(() => {this.setStyles();}, this.time_);this.setStyles();},setStyles() {let coordinateStep = this.coordinates.length / this.menubtns.length;let arr = this.coordinates, N = this.step_;if (this.clockwise || this.clockwise === '') {//前面N个元素挪到最后arr.splice(arr.length - 1, 0, ...arr.splice(0, N));} else {//最后N个元素挪到前面arr.splice(0, 0, ...arr.splice(arr.length - N));}this.coordinates = arr;this.menubtns.forEach((v, i) => {let coordinate = this.coordinates[i * coordinateStep];this.$set(v, "style", {left: `${coordinate.x}px`,top: `${coordinate.y}px`,});});},getCoordinates(cb) {let a = parseFloat(this.style.width) / 2;let b = parseFloat(this.style.height) / 2;this.getCPoint(a, b, 1, a, b, cb);},// a 长半径, b 短半径, p 节点的间隔 , cx, cy 圆心, getCPoint(a, b, p = 1, cx = 0, cy = 0, cb) {const data = []for (let index = 0; index < 360; index = index + p) {let x = a * Math.cos(Math.PI * 2 * index / 360)let y = b * Math.sin(Math.PI * 2 * index / 360)data.push({ x: x + cx, y: y + cy })}cb && cb(data);},}
};
</script>    
<style lang="scss" scoped>
$rotate: var(--rotate);.sgOvalMenu {border: 2px dashed #eee;border-color: #66b1ff66 #66b1ffAA #66b1CC #66b1ff;border-radius: 100%;width: 100%;height: 100%;transform-origin: center;transition: .382s linear;.ovalMenuBtn {white-space: nowrap;position: absolute;width: max-content;height: max-content;left: 0;top: 0;transform: translate(-50%, -50%) rotate($rotate);transform-origin: center;// transition: left .618s, top .618s, filter .382s;transition: .382s;pointer-events: auto;color: white;cursor: pointer;&:hover {filter: brightness(1.1);}}
}
</style>

应用

<template><div :class="$options.name"><!-- 椭圆菜单 --><sgOvalMenu :data="ovalMenus" @click="clickOvalMenu" :width="700" :height="200" :rotate="30" clockwise><template v-slot="scope"><div class="btn">{{ scope.data.label }}</div></template></sgOvalMenu></div>
</template><script>
import sgOvalMenu from "./sgOvalMenu";
export default {name: 'sgBody',components: { sgOvalMenu },data() {return {ovalMenus: [{ value: '1', label: '显示文本1', },{ value: '2', label: '显示文本2', },{ value: '3', label: '显示文本3', },{ value: '4', label: '显示文本4', },{ value: '5', label: '显示文本5', },],}},methods: {clickOvalMenu(d) {console.log(`获取点击信息:`, JSON.stringify(d, null, 2));},}
};
</script><style lang="scss" scoped>
.sgBody {display: flex;justify-content: center;align-items: center;background-color: black;.btn {box-sizing: border-box;padding: 10px 20px;border-radius: 88px;box-sizing: border-box;border: 1px solid #409EFF;box-shadow: 0 10px 30px #409EFFAA, 0 10px 30px #409EFF99 inset;color: #409EFF;cursor: pointer;transition: .382s;&:hover {box-shadow: 0 10px 30px #409EFFAA, 0 10px 30px #409EFF99 inset;background-color: #409EFF;color: black;filter: brightness(1.3);}}
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LLMs多任务指令微调Multi-task instruction fine-tuning

多任务微调是单任务微调的扩展&#xff0c;其中训练数据集包括多个任务的示例输入和输出。在这里&#xff0c;数据集包含指导模型执行各种任务的示例&#xff0c;包括摘要、评论评分、代码翻译和实体识别。 您在这个混合数据集上训练模型&#xff0c;以便它可以同时提高模型在…

每日一博 - 闲聊云原生和容器编排

文章目录 概念1. 云原生&#xff08;Cloud Native&#xff09;&#xff1a;2. 容器编排&#xff08;Container Orchestration&#xff09;&#xff1a; 小结 概念 云原生和容器编排是两个不同的概念&#xff0c;但它们之间有着密切的联系。下面将分别介绍这两个概念&#xff0…

react解决死循环方法?

使用useeffect&#xff08;副作用&#xff09;方法结束这个操作 1、导入useeffect、useState 2、把下方代码写入&#xff1a;里面填写的是你要终止某个东西的代码 注意&#xff1a;不可不写&#xff0c;也可以写依赖或不写

JS算法之树(一)

前言 之前我们已经介绍过一种非顺序数据结构&#xff0c;是散列表。 JavaScript散列表及其扩展http://t.csdn.cn/RliQf 还有另外一种非顺序数据结构---树。 树数据结构 树是一种分层数据的抽象模型。公司组织架构图就是常见的树的例子。 相关术语 一个树结构&#xff0…

数据结构(Java实现)-java对象的比较

元素的比较 基本类型的比较 在Java中&#xff0c;基本类型的对象可以直接比较大小。 对象比较的问题 Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较 默认情况下调用的就是equal方法&#xff0c;但是该方法的比较规则是&#xff1a;没有比较引用变量引用对象的…

iPhone 15 Pro与谷歌Pixel 7 Pro:哪款相机手机更好?

考虑到苹果最近将更多高级功能转移到iPhone Pro设备上的趋势,今年秋天iPhone 15 Pro与谷歌Pixel 7 Pro的对决将是一场特别有趣的对决。去年发布的iPhone 14 Pro确实发生了这种情况,有传言称iPhone 15 Pro再次受到了苹果的大部分关注。 预计iPhone 15系列会有一些变化,例如切…

k8s ingress (二)

k8s ingress (二) Ingress介绍 在前面课程中已经提到&#xff0c;Service对集群之外暴露服务的主要方式有两种&#xff1a;NodePort和LoadBalancer&#xff0c;但是这两种方式&#xff0c;都有一定的缺点&#xff1a; NodePort方式的缺点是会占用很多集群机器的端口&#xff0…

Postman —— postman实现参数化

什么时候会用到参数化 比如&#xff1a;一个模块要用多组不同数据进行测试 验证业务的正确性 Login模块&#xff1a;正确的用户名&#xff0c;密码 成功&#xff1b;错误的用户名&#xff0c;正确的密码 失败 postman实现参数化 在实际的接口测试中&#xff0c;部分参数每…

Redis通信协议

文章目录 Redis通信协议RESP协议数据类型 模拟Redis客户端 Redis通信协议 RESP协议 Redis是一个CS架构的软件&#xff0c;通信一般分为两步(不包含pipeline和PubSub)&#xff1a; 客户端(client)向服务端(server)发送一条命令。服务器解析并执行命令&#xff0c;返回响应结果…

【DETR】3、Conditional DETR | 拆分 content 和 spatial 来实现对 DETR 的加速

文章目录 一、Conditional DETR 是怎么被提出来的二、Conditional DETR 的具体实现2.1 框架结构2.2 DETR 的 cross-attention 和 Conditional DETR 的 cross-attention 对比 三、效果 论文&#xff1a;Conditional DETR for Fast Training Convergence 代码&#xff1a;https:…

c++ qt--事件过滤(第七部分)

c qt–事件过滤&#xff08;第七部分&#xff09; 一.为什么要用事件过滤 上一篇博客中我们用到了事件来进行一些更加细致的操作&#xff0c;如监控鼠标的按下与抬起&#xff0c;但是我们发现如果有很多的组件那每个组件都要创建一个类&#xff0c;这样就显得很麻烦&#xff…

springboot源码编译问题

问题一 Could not find artifact org.springframework.boot:spring-boot-starter-parent:pom:2.2.5.RELEASE in nexus-aliyun (http://maven.aliyun.com/nexus/content/groups/public/) 意思是无法在阿里云的镜像仓库中找到资源 解决&#xff1a;将配置的镜像删除即可&#…