【样式效果】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 小时前
Vue 3 + Elementui + TypeScript 实现左侧菜单定位右侧内容
vue.js·elementui·typescript
whysqwhw1 小时前
js之Promise
前端
恋猫de小郭5 小时前
Flutter 3.35 发布,快来看看有什么更新吧
android·前端·flutter
chinahcp20086 小时前
CSS保持元素宽高比,固定元素宽高比
前端·css·html·css3·html5
暖木生晖6 小时前
flex-wrap子元素是否换行
javascript·css·css3·flex
gnip7 小时前
浏览器跨标签页通信方案详解
前端·javascript
gnip7 小时前
运行时模块批量导入
前端·javascript
hyy27952276848 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
逆风优雅8 小时前
vue实现模拟 ai 对话功能
前端·javascript·html
若梦plus8 小时前
http基于websocket协议通信分析
前端·网络协议