vue3实现右下角扇形导航菜单动画效果

 效果如下:


实现的大概思路:一级导航:先把图片定位到右下角,并绑定事件,控制蓝色大图片的切换,

        再给二级子菜单动态添加css动画。

二级导航:同理,给子菜单绑定点击事件,控制表单的显示与隐藏,二级菜单没有添加动画,

        后续可以自己添加。


实现代码如下:

div部分(img图片自己换一下)

<!-- 扇形菜单图标 -->
    <div class="button">
        <img :style="buttonClose" @click="menuClose" src="../../assets/button-close.png" alt="">
        <img :style="buttonQuery" @click="menuQuery" src="../../assets/button-query.png" alt="">
        <img :style="buttonStyle" @click="menu" src="../../assets/button.png" alt="">
    </div>
    <!-- 子菜单图标 -->
    <div class="item-menu">
        <img :class="inAnimation" :style="menuStyle" src="../../assets/menu-white.png" alt="">
        <img :class="inAnimationTwo" @click="itemMenu" :style="menuStyleTwo" src="../../assets/menu-blue.png" alt="">
        <img :class="inAnimationThree" :style="menuStyleThree" src="../../assets/menu-white.png" alt="">
        <img :class="inAnimationFour" :style="menuStyleFour" src="../../assets/menu-white.png" alt="">
    </div>
    <!-- table查询条件 -->
    <div class="el-query" v-show="show">
        <div class="query-title">查询条件</div>
        <div class="el-left">
            <div>
                <el-date-picker
                    v-model="value"
                    type="daterange"
                    style="width: 92%;"
                    size="large"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                />
            </div>
            <el-select v-model="value1" placeholder="护理项目" style="width: 100%;" size="large">
                <el-option
                v-for="item in options1"
                :key="item.value"
                :label="item.label"
                :value="item.value"
                />
            </el-select>
            <el-select v-model="value2" placeholder="护理团队" style="width: 100%;" size="large">
                <el-option
                v-for="item in options2"
                :key="item.value"
                :label="item.label"
                :value="item.value"
                />
            </el-select>
            <el-select v-model="value3" placeholder="护理人员" style="width: 100%;" size="large">
                <el-option
                v-for="item in options3"
                :key="item.value"
                :label="item.label"
                :value="item.value"
                />
            </el-select>
            <div>
                <el-button @click="empty">清空</el-button>
                <el-button @click="query" type="primary">查询</el-button>
            </div>
        </div>
    </div>

js部分(查询组件的data未粘贴)

        

import { ref,onMounted } from 'vue';

// 按钮弹出框
let inAnimation = ref();
let inAnimationTwo = ref();
let inAnimationThree = ref();
let inAnimationFour = ref();
let menuStyle = ref({
    display: "none"
})
let menuStyleTwo = ref({
    display: "none"
})
let menuStyleThree = ref({
    display: "none"
})
let menuStyleFour = ref({
    display: "none"
})
let buttonStyle = ref({
    width: "100px",
    position: "absolute",
    "z-index": "500",
    right: "0.5vw",
    bottom: "3vh",
})
let buttonClose = ref({
    "display":"none"
})
let buttonQuery = ref({
    "display":"none"
})

// 扇形打开
let booooo = ref(true);
let menu = ()=>{
    if(booooo.value){
        menuStyle.value = {
            display:"block"
        }
        menuStyleTwo.value = {
            display:"block"
        }
        menuStyleThree.value = {
            display:"block"
        }
        menuStyleFour.value = {
            display:"block"
        }
        buttonStyle.value = {
            display:"none"
        }
        buttonQuery.value = {
            display:"block",
            width: "100px",
            position: "absolute",
            "z-index": "500",
            right: "0.5vw",
            bottom: "3vh",
        }
        inAnimation.value="inAnimation"
        inAnimationTwo.value="inAnimationTwo"
        inAnimationThree.value="inAnimationThree"
        inAnimationFour.value="inAnimationFour"
        booooo.value = false
    }
}

// 扇形换查询图标
let menuQuery = ()=>{
    setTimeout(() => {
        menuStyle.value = {
            display:"none"
        }
        menuStyleTwo.value = {
            display:"none"
        }
        menuStyleThree.value = {
            display:"none"
        }
        menuStyleFour.value = {
            display:"none"
        }
        booooo.value = true
    }, 500);
    
    buttonStyle.value = {
        display:"block",
        width: "100px",
        position: "absolute",
        "z-index": "500",
        right: "0.5vw",
        bottom: "3vh",
    }
    buttonQuery.value = {
        display:"none"
    }
    inAnimation.value="inAnimationClose"
    inAnimationTwo.value="inAnimationTwoClose"
    inAnimationThree.value="inAnimationThreeClose"
    inAnimationFour.value="inAnimationFourClose"
}

// 子菜单打开
let show = ref(false);
let itemMenu = ()=>{
    if(show.value==false){
        show.value = true;
        buttonQuery.value = {
            display:"none"
        }
        buttonClose.value = {
            display:"block",
            width: "100px",
            position: "absolute",
            "z-index": "500",
            right: "0.5vw",
            bottom: "3vh",
        }
    }else{
        menuClose();
    }
}

// 扇形关闭子菜单
let menuClose = ()=>{
    show.value = false;
    buttonClose.value = {
        display:"none"
    }
    buttonQuery.value = {
        display:"block",
        width: "100px",
        position: "absolute",
        "z-index": "500",
        right: "0.5vw",
        bottom: "3vh",
    }
}

css部分

.item-menu>img{
    display: none;
    z-index: 500;
    width: 70px;
    position: absolute;
    right: 0.5vw;
    bottom: 3vh;
}
.inAnimation{
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBot;
}
@keyframes AuxSlideFromBot {
    from {
        transform: translateX(0px);
    }
    to {
        transform: translateX(-100px);
    }
}
.inAnimationClose{
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBotClose;
}
@keyframes AuxSlideFromBotClose {
    from {
        transform: translateX(-100px);
    }
    to {
        transform: translateX(0px);
    }
}
.inAnimationTwo{
    z-index: 600!important;
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBotTwo;
}
@keyframes AuxSlideFromBotTwo {
    from {
        transform: translateX(0px);
    }
    to {
        transform: translate(-85px,-50px);
    }
}
.inAnimationTwoClose{
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBotTwoClose;
}
@keyframes AuxSlideFromBotTwoClose {
    from {
        transform: translate(-85px,-50px);
    }
    to {
        transform: translateX(0px);
    }
}
.inAnimationThree{
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBotThree;
}
@keyframes AuxSlideFromBotThree {
    from {
        transform: translateX(0px);
    }
    to {
        transform: translate(-45px,-80px);
    }
}
.inAnimationThreeClose{
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBotThreeClose;
}
@keyframes AuxSlideFromBotThreeClose {
    from {
        transform: translate(-45px,-80px);
    }
    to {
        transform: translateX(0px);
    }
}
.inAnimationFour{
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBotFour;
}
@keyframes AuxSlideFromBotFour {
    from {
        transform: translateY(0px);
    }
    to {
        transform: translateY(-100px);
    }
}
.inAnimationFourClose{
    animation-duration: 1000ms;
    animation-iteration-count: 1;
    animation-fill-mode:forwards;
    animation-name: AuxSlideFromBotFourClose;
}
@keyframes AuxSlideFromBotFourClose {
    from {
        transform: translateY(-100px);
    }
    to {
        transform: translateY(0px);
    }
}
.el-query{
    width: 240px;
    padding: 2vh 1vw;
    position: absolute;
    right: 175px;
    z-index: 600;
    bottom: 4vh;
    border-radius: 8px;
    height: 26vh;
    display: flex;
    flex-direction: column;
    background: #FFFFFF;
}
.query-title{
    font-style: normal;
    font-weight: 800;
    font-size: 16px;
    color: #1358B2;
}
.el-left{
    margin-top: 1vh;
    height: 23vh;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: space-between;
}

代码写的有点臃肿,有很多地方可以封装,后期大家可以按需求封装更改。

头次发博客,希望能帮到各位。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮你编写一个使用 Vue 3 的菜单栏滑动组件。以下是一个简单的示例: ```vue <template> <div class="menu-container"> <div class="menu" ref="menu" @mousemove="handleMouseMove"> <!-- 菜单项 --> <div class="menu-item" v-for="(item, index) in menuItems" :key="index"> {{ item }} </div> </div> </div> </template> <script> import { ref } from 'vue'; export default { data() { return { menuItems: ['菜单项1', '菜单项2', '菜单项3', '菜单项4', '菜单项5'], // 菜单项数组 menuWidth: 0, // 菜单宽度 mouseX: 0, // 鼠标X轴位置 }; }, mounted() { this.menuWidth = this.$refs.menu.offsetWidth; }, methods: { handleMouseMove(event) { this.mouseX = event.clientX; }, }, computed: { menuPosition() { const containerWidth = this.$el.offsetWidth; const maxTranslateX = containerWidth - this.menuWidth; const translateX = (this.mouseX / containerWidth) * maxTranslateX; return `translateX(-${translateX}px)`; }, }, }; </script> <style> .menu-container { width: 100%; overflow: hidden; } .menu { display: flex; transition: transform 0.3s ease; white-space: nowrap; } .menu-item { padding: 10px; } </style> ``` 在上述示例中,我们使用了 Vue 3 的 Composition API,通过 `ref` 创建了响应式的 `menuWidth` 和 `mouseX` 变量。`menuWidth` 用于保存菜单的宽度,`mouseX` 用于保存鼠标的 X 轴位置。 通过监听菜单容器的 `mousemove` 事件,我们可以实时更新 `mouseX` 的值。然后,通过计算属性 `menuPosition`,我们根据鼠标的位置计算出需要应用到菜单项容器的 `transform` 样式,实现菜单项的滑动效果。 最后,我们在模板中使用 `v-for` 循环渲染菜单项,并将计算出的 `menuPosition` 应用到菜单项容器上。 这只是一个简单的示例,你可以根据自己的需求进行修改和扩展。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值