【样式效果】Vue3实现仿制iOS按钮动态效果

iOS开关效果

定义变量:

vue 复制代码
<style scoped lang="scss">
    .layout {
        // 按钮宽度
        $button-width: 500px;
        //  按钮高度
        $button-height: 250px;
        //  按钮里面圆形直径
        $circle-diameter: 200px;
        //  按钮背景与里面圆形间距
        $button-circle-offset:calc(($button-height - $circle-diameter) / 2);
        //  里面圆形阴影大小
        $circle-shadow-size: 10px;
        //  里面圆形在长按情况下的宽度
        $circle-wider-size: 350px;
        //  浅灰色
        $light-gray: #e0e0e0;
        //  深灰色
        $dark-gray: #616161;
        //  绿色
        $green: #4caf50;
    }
</style>

绘制按钮图形:

vue 复制代码
<template>
  <div class="layout">
    <div class="button">
    </div>
  </div>
</template>

<style scoped lang="scss">
.layout {
  // 上面👆定义的变量
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;

  .button{
    width: $button-width;
    height: $button-height;
    background: $light-gray;
    border-radius:calc($button-width / 2);
  }
}
</style>

里面的圆形使用伪类选择器

vue 复制代码
&:after{
    content: '';
    display: block;
    width: $circle-diameter;
    height: $circle-diameter;
    border-radius: $circle-diameter;
    background-color: #fff;
}

给元素添加上定位让圆形固定到指定位置:

vue 复制代码
  .button{
   // ...其他效果;
    position: relative;
    &:after{
    // ...其他效果;
      position: absolute;
      top:$button-circle-offset;
      transform: translateX($button-circle-offset); // 为了后面方便做动画,所以使用translateX
box-shadow:$button-circle-offset 0 calc($circle-shadow-size * 4) $dark-gray ;
    }
  }

处理点击效果:

vue 复制代码
<template>
<div class="layout">
    <div class="button" @click="clickBtn" :class="{ 'btnClick': isActive }" >
    </div>
    </div>
</template>

<script setup lang="ts">
    import {ref} from "vue";
    const isActive = ref(false)

    const clickBtn = () => {
        isActive.value = !isActive.value
    }
</script>

<style scoped lang="scss">
    .btnClick {
        background: $green;
        &:after{
            transform: translateX( calc( $button-width - $circle-diameter - $button-circle-offset ) );
            box-shadow: calc( $button-circle-offset * -1 ) 0 calc($circle-shadow-size * 4) $dark-gray ;
        }
    }
</style>

加入transition动画:

vue 复制代码
.button{
    transition:.3s all ease-in-out;
    &:after{
        transition:.3s all ease-in-out;
    }
}

接下来处理长按效果:

vue 复制代码
.button{
	&:active:after{
      width: $circle-wider-size;
    }
}
.btnClick {
    &:active:after{
          transform: translateX( calc( $button-width - $circle-wider-size - $button-circle-offset ) );
        }
}

这样按钮效果就实现啦🎉

完整代码:

vue 复制代码
<template>
  <div class="layout">
    <div class="button" @click="clickBtn" :class="{ 'btnClick': isActive }" >
    </div>
  </div>
</template>

<script setup lang="ts">
import {ref} from "vue";
const isActive = ref(false)

const clickBtn = () => {
    isActive.value = !isActive.value
}
</script>

<style scoped lang="scss">
.layout {
  // 按钮宽度
  $button-width: 500px;
  //  按钮高度
  $button-height: 250px;
  //  按钮里面圆形直径
    $circle-diameter: 200px;
  //  按钮背景与里面圆形间距
  $button-circle-offset:calc(($button-height - $circle-diameter) / 2);
  //  里面圆形阴影大小
  $circle-shadow-size: 10px;
  //  里面圆形在长按情况下的宽度
  $circle-wider-size: 350px;
  //  浅灰色
  $light-gray: #e0e0e0;
  //  深灰色
  $dark-gray: rgba(97, 97, 97, 0.34);
  //  绿色
  $green: #71d575;

  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;

  .button{
    width: $button-width;
    height: $button-height;
    background: $light-gray;
    border-radius:calc($button-width / 2);
    position: relative;
    transition:.3s all ease-in-out;
    &:after{
      content: '';
      display: block;
      width: $circle-diameter;
      height: $circle-diameter;
      border-radius: $circle-diameter;
      background-color: #fff;
      position: absolute;
      top:$button-circle-offset;
      transform: translateX($button-circle-offset); // 为了后面方便做动画,所以使用translateX
      box-shadow:$button-circle-offset 0 calc($circle-shadow-size * 4) $dark-gray ;
      transition:.3s all ease-in-out;
    }
    &:active:after{
      width: $circle-wider-size;
    }
  }
  .btnClick {
    background: $green;
    &:after{
      transform: translateX( calc( $button-width - $circle-diameter - $button-circle-offset ) );
      box-shadow: calc( $button-circle-offset * -1 ) 0 calc($circle-shadow-size * 4) $dark-gray ;
    }
    &:active:after{
      transform: translateX( calc( $button-width - $circle-wider-size - $button-circle-offset ) );
    }
  }
}
</style>
相关推荐
weixin199701080162 分钟前
【性能提升300%】仿1688首页的Webpack优化全记录
前端·webpack·node.js
JavinLu6 分钟前
ios 配置了代理且使用 chls.pro/ssl 下载不了证书,无法弹出下载证书的提示问题
网络协议·ios·ssl
冰暮流星12 分钟前
javascript之数组
java·前端·javascript
晚霞的不甘39 分钟前
Flutter for OpenHarmony天气卡片应用:用枚举与动画打造沉浸式多城市天气浏览体验
前端·flutter·云原生·前端框架
weixin79893765432...40 分钟前
Vue 渲染体系“三件套”(template 模板语法、h 函数和 JSX 语法)
vue.js·h函数·template 模板·jsx 语法
xkxnq1 小时前
第五阶段:Vue3核心深度深挖(第74天)(Vue3计算属性进阶)
前端·javascript·vue.js
三小河1 小时前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端
Hilaku1 小时前
不要在简历上写精通 Vue3?来自面试官的真实劝退
前端·javascript·vue.js
三小河1 小时前
前端视角详解 Agent Skill
前端·javascript·后端
Aniugel1 小时前
单点登录(SSO)系统
前端