彈彈彈,彈走魚尾紋的彈出菜單(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
相關文章
相關標籤/搜索