【样式效果】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>
相关推荐
椒盐螺丝钉4 分钟前
TypeScript类型兼容性
运维·前端·typescript
_JinHao8 分钟前
Cesium Viewer对象详解——Cesium基础笔记(快速入门)
前端·javascript·笔记·3d·webgl
正义的大古12 分钟前
OpenLayers地图交互 -- 章节十三:拖拽旋转交互详解
javascript·vue.js·openlayers
00后程序员张15 分钟前
iOS 26 兼容测试实战,机型兼容、SwiftUI 兼容性改动
android·ios·小程序·uni-app·swiftui·cocoa·iphone
r0ad39 分钟前
从痛点到解决方案:为什么我开发了Chrome元素截图插件
前端·chrome
OEC小胖胖1 小时前
连接世界:网络请求 `wx.request`
前端·微信小程序·小程序·微信开放平台
jingling5551 小时前
解决微信小程序真机调试中访问本地接口 localhost:8080 报错
前端·微信小程序·小程序
en-route1 小时前
使用 Flask 构建 Web 应用:静态页面与动态 API 访问
前端·python·flask
IT_陈寒1 小时前
Vite 5年迭代揭秘:3个核心优化让你的项目构建速度提升200%
前端·人工智能·后端
2501_915106321 小时前
iOS 可分发是已经上架了吗?深入解析应用分发状态、ipa 文件上传、TestFlight 测试与 App Store 审核流程
android·ios·小程序·https·uni-app·iphone·webview