【样式效果】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>
相关推荐
未来之窗软件服务1 分钟前
一体化系统(九)智慧社区综合报表——东方仙盟练气期
大数据·前端·仙盟创梦ide·东方仙盟·东方仙盟一体化
陈天伟教授3 小时前
人工智能训练师认证教程(2)Python os入门教程
前端·数据库·python
信看4 小时前
NMEA-GNSS-RTK 定位html小工具
前端·javascript·html
Tony Bai4 小时前
【API 设计之道】04 字段掩码模式:让前端决定后端返回什么
前端
苏打水com4 小时前
第十四篇:Day40-42 前端架构设计入门——从“功能实现”到“架构思维”(对标职场“大型项目架构”需求)
前端·架构
king王一帅4 小时前
流式渲染 Incremark、ant-design-x markdown、streammarkdown-vue 全流程方案对比
前端·javascript·人工智能
苏打水com5 小时前
第十八篇:Day52-54 前端跨端开发进阶——从“多端适配”到“跨端统一”(对标职场“全栈化”需求)
前端
Bigger5 小时前
后端拒写接口?前端硬核自救:纯前端实现静态资源下载全链路解析
前端·浏览器·vite
BD_Marathon5 小时前
【JavaWeb】路径问题_前端绝对路径问题
前端