彈彈彈,彈走魚尾紋的彈出選單(vue)

信心發表於2018-09-11

前言

上一篇面試的總結,大家看的還行,因為量很大,錯誤在所難免,希望大家發現錯誤了可以告訴我一聲,我的郵箱是236490794@qq.com,一個小前端的希望。

彈彈彈,彈走魚尾紋的彈出選單(vue)

言歸正傳

我們老樣子直接先上效果圖再開始今天的分享 這個專案的github可以看一看

彈彈彈,彈走魚尾紋的彈出選單(vue)

元件分析

  1. 介面組成
  2. 邏輯分析
  3. 最終實現
介面組成

從上圖中,我們可以看出介面主要分為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的相關內容則由資料來控制,具體的我們直接在下方的實現裡來講解。

彈彈彈,彈走魚尾紋的彈出選單(vue)

最終實現

這裡我用程式碼加註釋的方式,幫助大家理解,template我簡單的帶過一下

<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 / 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)
      }
    }
  }
複製程式碼

再父元件中引入就可以大功告成啦,先跳一會兒吧,燃燒你的卡路里

彈彈彈,彈走魚尾紋的彈出選單(vue)

父元件呼叫

引入元件

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

彈彈彈,彈走魚尾紋的彈出選單(vue)

好了,差不多就分享這麼多,

彈彈彈,彈走魚尾紋的彈出選單(vue)
我的github,求戳,求star

相關文章