vue3自定义指令(表单验证抖动shake)

301 阅读1分钟

前些日子我在stackoverflow社区看到一篇文章是关于表单验证抖动效果的,比如表单验证没达到要求,就可以要求表单摇摆

动画.gif

原文回答如下
<template>
  <div id="app">
    <button :class="{ 'apply-shake': shake }" @click="shakeAnimation()">
      Shake
    </button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      shake: false,
    };
  },
  methods: {
    shakeAnimation() {
      this.shake = true;
      setTimeout(() => {
        this.shake = false;
      }, 820); // timeout value depending on the duration of the animation
    },
  },
};
</script>
<style>
@keyframes shake {
  10%,
  90% {
    transform: translate3d(-1px, 0, 0);
  }
  20%,
  80% {
    transform: translate3d(2px, 0, 0);
  }
  30%,
  50%,
  70% {
    transform: translate3d(-4px, 0, 0);
  }
  40%,
  60% {
    transform: translate3d(4px, 0, 0);
  }
}
.apply-shake {
  animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}
</style>

现在我们考虑一个问题,如果多次重复应用该代码我们应该做什么?
复制粘贴❌
抽离代码✔️

自定义指令

<template>
  <div class="container">
    <input type="text" v-shake="shake" v-model="inputInner" />
    <button @click="shakeAnimation">点击验证</button>
  </div>
</template>
<script>
import { ref } from 'vue'
export default {
  setup() {
    const shake = ref(false)
    const inputInner = ref(null)
    const shakeAnimation = () => {
      if (!inputInner.value) {
        shake.value = true
        setTimeout(() => {
          shake.value = false
        }, 820) 
      }
    }
    return { shakeAnimation, shake, inputInner }
  }
}
</script>

和组件类似,自定义指令在模板中使用前必须先注册。我们使用 directive 选项进行注册。

app.directive('shake', (el, binding) => { 
   /* ... */
})

那么el, binding两个参数是干嘛的呢,我们打印一下看看

image.png 官方文档中写到

  • el:指令绑定到的元素。这可以用于直接操作 DOM
    • binding:一个对象,包含以下属性。
    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。
  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

emmm...,很明了了,我们只要监听shake这个值的变化,也就是把value拿过来就好了,然后你在他的DOM上操作class名就好了,具体代码如下!

const defineDirective = (app) => {
  app.directive('shake', {
    updated(el, binding) {
      if (binding.value) {
        el.classList.add('apply-shake')
      } else {
        el.classList.remove('apply-shake')
      }
    }
  })
}