摘要:前言上一篇面试的总结,大家看的还行,因为量很大,错误在所难免,希望大家发现错误了可以告诉我一声,我的邮箱是,一个小前端的希望。
前言
上一篇面试的总结,大家看的还行,因为量很大,错误在所难免,希望大家发现错误了可以告诉我一声,我的邮箱是236490794@qq.com,一个小前端的希望。
我们老样子直接先上效果图再开始今天的分享
这个项目的github可以看一看
界面组成
逻辑分析
最终实现
从上图中,我们可以看出界面主要分为menu和item2块,其中menu的动画是自传,item的动画是位移,然后这里我们通过绝对布局的方式将整个控件定位在四个角落
.menu_container { position: absolute; z-index: 100; border-radius: 50%; transition-duration: 400ms; text-align: center; border: #efefef 3px solid; box-shadow: aliceblue 1px 1px 1px; } .menu_item { position: absolute; border-radius: 50%; z-index: 99; border: #efefef 3px solid; text-align: center; box-shadow: aliceblue 1px 1px 1px; }
这里我将这个控件几个属性独立出来,方便下次开发,其中包含,menu的背景,整个控件在屏幕的哪个角落,menu的宽高,item距离menu位移的距离,menu的背景色,及item的背景色,item的相关内容则由数据来控制,具体的我们直接在下方的实现里来讲解。
这里我用代码加注释的方式,帮助大家理解,template我简单的带过一下
核心实现
通过分析可以得出,每个item的偏移量应该为
横向x:基础值 * sin(角度值)
纵向y:基础值 * cos(角度值)
角度值:(数组的长度-1-当前的下标) 每一块所占的角度 弧度表示
弧度表示:2 * Math.PI / 360
export default { ... props: {//开放的属性,方便自定义 menuSrc: { default: require("../assets/menu.png") }, position: { default: "LT"//可选择LT、LB、RT、RB4个角落 }, width: { default: 50, }, baseDistance: { default: 150, }, menuBg: { default: "white" }, itemBg: { default: "white" }, menuItems: { type: Array, } }, data() { return { openFlag: false,//展开合并标志 operators: ["+", "+"],//用于记录展开时动画XY方向 } }, mounted() { //根据props初始化各内容的各种style this.$refs.menuHome.style.width = this.width + "px"; this.$refs.menuHome.style.height = this.width + "px"; this.$refs.menuHome.style.lineHeight = this.width + "px"; this.$refs.menuHome.style.background = this.menuBg; this.menuItems.forEach((item) => { let el = document.getElementById(item.name); el.style.width = `${this.width * 0.8}px`; el.style.height = `${this.width * 0.8}px`; el.style.lineHeight = `${this.width * 0.8}px`; el.style.background = this.itemBg; }); //根据position,选择不同的定位 switch (this.position) { case "LT": this.$refs.menuHome.style.left = "20px"; this.$refs.menuHome.style.top = "20px"; this.menuItems.forEach((item) => { let el = document.getElementById(item.name); el.style.left = "26px"; el.style.top = "26px"; }); this.operators = ["+", "+"]; break; ... } }, methods: { toggleMenu() { if (!this.openFlag) {//合并时,点击展开操作 this.menuItems.forEach((item, index) => { this.toggleMenuTransition(item.name, index, false) }); //menu本身转一周 this.$refs.menuHome.style.transform = "rotate(360deg)"; } else { this.menuItems.forEach((item, index) => { this.toggleMenuTransition(item.name, index, true) }); //menu恢复 this.$refs.menuHome.style.transform = "rotate(0)"; } this.openFlag = !this.openFlag; }, toggleMenuTransition(name, index, revert) { let oneArea = 90 / (this.menuItems.length - 1);//每一块所占的角度 let axisX = Math.sin((this.menuItems.length - 1 - index) * oneArea * 2 * Math.PI / 360);//横坐标所偏移的比例 let axisY = Math.cos((this.menuItems.length - 1 - index) * oneArea * 2 * Math.PI / 360);//纵坐标所便宜的比例 let el = document.getElementById(name);//若所传的name一直,会报错。 let that = this; if (!revert) { setTimeout(function () { el.style.transitionDuration = "200ms"; el.style.transform = `translate(${that.operators[0]}${that.baseDistance * axisX}px,${that.operators[1]}${that.baseDistance * axisY }px)`;//进行动画 }, index * 100)//通过定时器的方式,达到一个一个弹出来的效果 } else { //item恢复 el.style.transitionDuration = "200ms"; el.style.transform = `translate(0,0)`; } }, clickMenu(item, index) { //暴露方法给父组件,进行点击事件的操作 this.$emit("clickMenu", item, index) } } }
再父组件中引入就可以大功告成啦,先跳一会儿吧,燃烧你的卡路里
引入组件
import toggleMenu from "./toggleMenu"
在 components声明
components: { toggleMenu },
template中使用
menuItems: [//name和src必填,且name唯一否则会报错 {name: "menu1", src: require("../assets/emoji.png")}, {name: "menu2", src: require("../assets/cart.png")}, {name: "menu3", src: require("../assets/folder.png")}, {name: "menu4", src: require("../assets/home.png")}, {name: "menu5", src: require("../assets/my.png")}, ]属性及方法一栏
属性名 | 用处 | 默认值 | 是否必须 |
---|---|---|---|
position | 四个方位(LT、LB、RT、RB) | LT | 否 |
menuBg | 菜单背景 | white | 否 |
menuSrc | 菜单图片 | 一个菜单图片 | 否 |
itemBg | 按钮背景 | white | 否 |
width | 按钮宽度 | 50px | 否 |
baseDistance | 位移距离,若item很多,可适当提高 | 150px | 否 |
menuItems | 菜单数组 | 无 | 是 |
方法名 | 用处 | 参数 |
---|---|---|
clickMenu | 点击item触发事件 | item,index |
我的github,求戳,求star
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/54646.html
摘要:前言上一篇面试的总结,大家看的还行,因为量很大,错误在所难免,希望大家发现错误了可以告诉我一声,我的邮箱是,一个小前端的希望。 前言 上一篇面试的总结,大家看的还行,因为量很大,错误在所难免,希望大家发现错误了可以告诉我一声,我的邮箱是236490794@qq.com,一个小前端的希望。showImg(https://segmentfault.com/img/remote/146000...
摘要:用了,这类的框架之后,发现组件化的开发在大部分时间很方便,但是有些时候却用起来很变扭。更新今天发现一个大失误,原来我一直没把包发布到。。。赶紧发布了一下,如果有任务问题,都可以在的里面提出来 用了vue,react这类的框架之后,发现组件化的开发在大部分时间很方便,但是有些时候却用起来很变扭。 比如我需要自定义一个alert组件,组件写起来很方便,但是使用的时候却是这样的: ...
摘要:哥决定把小弟的成果纳入,不只是看上去很美系列,以示鼓励其实还挺有压力的,后浪推前浪,新人赶旧人。连续单击同一站点连续单击同一站点注意不是双击,可以将经过此站点的所有线路突出显示出来。 前言 最近特别忙,承蒙大伙关照,3D机房的项目一个接着一个,领了一帮小弟,搞搞传帮带,乌飞兔走,转眼已经菊黄蟹肥……有个小弟很不错,勤奋好学,很快就把API都摸透了,国庆几天自己折腾着做了个HTML5的魔...
阅读 1340·2019-08-30 15:44
阅读 2082·2019-08-30 11:04
阅读 467·2019-08-29 15:17
阅读 2504·2019-08-26 12:12
阅读 3108·2019-08-23 18:09
阅读 896·2019-08-23 15:37
阅读 1504·2019-08-23 14:43
阅读 2891·2019-08-23 13:13