前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >弹弹弹,弹走鱼尾纹的弹出菜单(vue)

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

作者头像
IMWeb前端团队
发布2019-12-03 17:56:48
4750
发布2019-12-03 17:56:48
举报
文章被收录于专栏:IMWeb前端团队

本文作者:IMWeb 气势的信心 原文出处:IMWeb社区 未经同意,禁止转载

前言

上一篇面试的总结,大家看的还行,因为量很大,错误在所难免,希望大家发现错误了可以告诉我一声,我的邮箱是236490794@qq.com,一个小前端的希望。

言归正传

我们老样子直接先上效果图再开始今天的分享 这个项目的github可以看一看

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

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

代码语言:javascript
复制
.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我简单的带过一下

代码语言:javascript
复制
<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

代码语言:javascript
复制
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)
      }
    }
  }

再父组件中引入就可以大功告成啦,先跳一会儿吧,燃烧你的卡路里

父组件调用

引入组件

代码语言:javascript
复制
import toggleMenu from './toggleMenu'

在 components声明

代码语言:javascript
复制
components: {
     toggleMenu
},

template中使用

代码语言:javascript
复制
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

好了,差不多就分享这么多,

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 言归正传
      • 组件分析
        • 界面组成
        • 逻辑分析
        • 最终实现
        • 父组件调用
      • 属性及方法一栏
      • 好了,差不多就分享这么多,
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档