弹弹弹,弹走鱼尾纹的弹出菜单(vue)

前言

上一篇面试的总结,你们看的还行,由于量很大,错误在所不免,但愿你们发现错误了能够告诉我一声,个人邮箱是236490794@qq.com,一个小前端的但愿。
javascript

言归正传

咱们老样子直接先上效果图再开始今天的分享
这个项目的github能够看一看
css

组件分析

  1. 界面组成
  2. 逻辑分析
  3. 最终实现
界面组成

从上图中,咱们能够看出界面主要分为menu和item2块,其中menu的动画是自传,item的动画是位移,而后这里咱们经过绝对布局的方式将整个控件定位在四个角落html

.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我简单的带过一下vue

<div>
    <div class="menu_container" ref="menuHome" @click="toggleMenu">
      <img :src="menuSrc"><!--menu图-->
    </div>
    <div class="menu_item" v-for="(item,index) in menuItems" :id="item.name" @click="clickMenu(item,index)">
      <img :src="item.src"><!--item图-->
    </div>
  </div>

核心实现
经过分析能够得出,每一个item的偏移量应该为
横向x:基础值 * sin(角度值)
纵向y:基础值 * cos(角度值)
角度值:(数组的长度-1-当前的下标) 每一块所占的角度 弧度表示
弧度表示:2 * Math.PI / 360java

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)
      }
    }
  }

再父组件中引入就能够大功告成啦,先跳一下子吧,燃烧你的卡路里
git

父组件调用

引入组件github

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')},
]
<toggle-menu :menuItems="menuItems"
             @clickMenu="clickMenu"
             ></toggle-menu>

属性及方法一栏

属性名 用处 默认值 是否必须
position 四个方位(LT、LB、RT、RB) LT
menuBg 菜单背景 white
menuSrc 菜单图片 一个菜单图片
itemBg 按钮背景 white
width 按钮宽度 50px
baseDistance 位移距离,若item不少,可适当提升 150px
menuItems 菜单数组
方法名 用处 参数
clickMenu 点击item触发事件 item,index

好了,差很少就分享这么多,


个人github,求戳,求star

相关文章
相关标签/搜索