vue2 + elementUI + scss 制作主题切换+动画效果

4 篇文章 0 订阅

前言:看市面上的主题切换简单的用一个switch组件实现深色和浅色的替换(这里也是)。 也有通过input封装成switch组件active-text=☀️ inactive-text=🌙(美观,作者下一步也准备美化 😆)。

为了给切换加上一个友好的交互效果,决定在这强对比的主题切换过度中制作一个赏心悦目的动画效果。以下是个人增添的dom效果及代码:

先看静态效果

深色主题切换:

浅色主题切换:

 TopTip.vue 页面代码

<template>
  <div class="theme-fixed-wrap">
    <section>
      <svg-icon :icon-class="desc.icon" />
      <em>正在切换{{ desc.txt }}</em>
    </section>
    <div class="line-wrap">.</div>
  </div>
</template>

<script>
export default {
  name: 'Tip',
  props: {
    desc: { type: Object, default: () => null }
  }
}
</script>


<style scoped lang="scss">
@import './style';
</style>

 stlye.scss 页面代码

.theme-fixed {
  &-wrap {
    position: fixed;
    top: 0;
    left: 50%;
    min-width: 100px;
    padding: 10px;
    transform: translate(-50%, -100px) scale(0.65);
    border-radius: 10px;
    transition: transform 0.3s ease-in-out;
    box-shadow: 0 0 30px rgba($color: var(--color-black-rgb), $alpha: 0.16);
    overflow: hidden;
    z-index: 100000;
    &::after {
      content: "";
      position: absolute;
      top: 60%;
      left: 60%;
      right: 0;
      bottom: 0;
      width: 120%;
      height: 120%;
      display: block;
      transform: translate(-60%, -60%);
      background-color: rgba($color: var(--border-color-extra-light-rgb), $alpha: 0.68);
      filter: blur(10px);
      z-index: 0;
    }

    &.fadeIn {
      transform: translate(-50%, 100px) scale(1);
    }

    & > section {
      display: flex;
      align-items: center;
      padding: 10px;
      background-color: var(--border-color-lighter);
      border-radius: 10px;
      position: relative;
      z-index: 1;
      & > .svg-icon,
      & > em {
        line-height: 30px;

        font: {
          weight: normal;
          style: normal;
        }
      }

      & > .svg-icon {
        text-align: center;
        font-size: 18px;
        width: 20px;
        height: 20px;
      }

      & > em {
        padding-left: 10px;
        text-align: left;
        font-size: 14px;
        color: var(--color-text-primary);
      }
    }

    .line-wrap {
      position: relative;
      width: 100%;
      height: 50px;
      font-size: 0;
      z-index: 1;
      &::before {
        content: "";
        position: absolute;
        padding: 0;
        margin: 0;
        top: 50%;
        left: 0;
        transform: translate(0, -50%) scaleY(0.5);
        border: none;
        width: 100%;
        height: 2px;
        background-color: rgba($color: var(--color-black-rgb), $alpha: 0.56);
      }
      &::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 0;
        display: block;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        transform: translate(0, -50%);
        animation-name: identifier;
        animation-delay: 0.4s;
        animation-duration: 1s;
        animation-timing-function: cubic-bezier(0, 1);
        animation-iteration-count: 1;
        animation-fill-mode: forwards;
      }
    }
  }
}

// 定义动画变量
$radioWidth: 20px;
$dep: calc(0%);
$dest: calc(100% - #{$radioWidth});
$light_color: #ffffff;
$dark_color: #0c3250;
$light_shadow: #ffffff;
$dark_shadow: #000000;
$blur: 20px;

// 设置动画脚本
@keyframes identifier_light {
  from {
    left: $dep;
    background-color: $light_color;
    box-shadow: 0 0 $blur $light_shadow;
  }
  to {
    left: $dest;
    background-color: $dark_color;
    box-shadow: none;
  }
}

@keyframes identifier_dark {
  from {
    left: $dep;
    background-color: $dark_color;
    box-shadow: none;
  }
  to {
    left: $dest;
    background-color: $light_color;
    box-shadow: 0 0 $blur $light_shadow;
  }
}

// 主题适配
:root[theme="dark"] {
  .line-wrap::after {
    animation-name: identifier_dark;
  }
}

:root[theme="light"] {
  .line-wrap::after {
    animation-name: identifier_light;
  }
}

theme.vue 组件使用方法及页面代码

<template>
    <el-switch
      v-model="theme"
      active-text="深色"
      inactive-text="浅色"
      active-value="dark"
      inactive-value="light"
      @change="handleChange"
    />
</template>
// Cookies.get
import { getThemeColors } from '@/utils/utils'
// 自定义封装 El Loading 服务
import { openLoading, closeLoading } from '@/utils/ElLoading'
export default {
  data() {
    return {
      theme: ''
    }    
  },
  mounted() {
    openLoading('主题加载中...')
    let themeColors = getThemeColors() || 'dark'
    this.theme = themeColors
    this.$store.dispatch('theme/setTheme', themeColors).then(() => {
      closeLoading()
    })
  },
  methods: {
    // 主题切换
    async handleChange(val) {
      await create(TopTip, {
        desc: { ...getThemeConf(val) }
      }).remove()

      this.$store.dispatch('theme/setTheme', val)
    }
  }
}

create.js 页面代码

import Vue from 'vue'

// 全局动画延迟基数设定
export const delay = 300

// 创建DOM
export function create(Component, props) {
  const vm = new Vue({
    render(h) {
      // console.log(h(Component, { props }))
      return h(Component, { props })
    }
  }).$mount()
  let el = vm.$el
  document.body.appendChild(el)
  tryCatch(fadeInTimer)
  let fadeInTimer = setTimeout(() => {
    el.classList.add('fadeIn')
  }, delay / 3)

  // 移除DOM
  const comp = vm.$children[0]
  comp.remove = () => {
    return new Promise((resolve) => {
      tryCatch(timer)
      let timer = setTimeout(() => {
        el.classList.remove('fadeIn')
        tryCatch(destroyTimer)
        let destroyTimer = setTimeout(() => {
          document.body.removeChild(el)
          vm.$destroy()
          resolve(true)
        }, delay)
      }, delay * 6)
    })
  }
  return comp
}

export function tryCatch(timer) {
  try {
    clearTimeout(timer)
  } catch (error) {
    console.error('err', error)
  }
}

以上是个人实际项目的核心代码,仅供学习参考。欢迎大家指正,谢谢!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vinca@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值