<template>
<div id="box">
<label :data-before="before" :data-after="after" :class="{ animate: shouldAnimate }" @animationend="animationEnded">
<input type="text" :placeholder="placeholder" @focus="focus" @blur="blur" @keydown="doSearch" />
</label>
</div>
</template>
<script>
export default {
data() {
return {
keywords: ['1', '2', '3', '4'],
isFocusing: false,
currentIndex: 0,
animationTriggerFlag: false,
timer: null,
}
},
computed: {
before() {
let keyword = this.keywords[this.currentIndex]
return this.isFocusing ? '' : keyword
},
after() {
let keyword =
typeof this.keywords[this.currentIndex + 1] === 'undefined' ? this.keywords[0] : this.keywords[this.currentIndex + 1]
return this.isFocusing ? '' : keyword
},
placeholder() {
let keyword = this.keywords[this.currentIndex]
return !this.isFocusing ? '' : keyword
},
// 是否开启动画
shouldAnimate() {
return !this.isFocusing && this.animationTriggerFlag
},
},
created() {
// 刚进入页面1秒后再执行动画
setTimeout(() => {
this.animationTriggerFlag = true
}, 1000)
},
methods: {
// 动画结束调用事件
animationEnded(e) {
// pseudoElement伪元素
if (e.pseudoElement === '::after') {
this.animationTriggerFlag = false
let newIndex = typeof this.keywords[this.currentIndex + 1] === 'undefined' ? 0 : this.currentIndex + 1
this.currentIndex = newIndex
setTimeout(() => {
this.animationTriggerFlag = true
}, 3000)
}
},
focus() {
// 如果一直点击,动画会一直执行,要先清除动画
if (this.timer) {
clearTimeout(this.timer)
}
this.isFocusing = true
this.animationTriggerFlag = false
},
blur() {
// 鼠标blur后3秒之后再执行动画
this.timer = setTimeout(() => {
this.isFocusing = false
this.animationTriggerFlag = true
}, 3000)
},
doSearch(e) {
if (e.keyCode === 13) {
alert(this.keywords[this.currentIndex])
}
},
},
}
</script>
<style lang="less" scoped>
input[type='text'] {
border: 3px solid #333;
font-size: 3rem;
border-radius: 0.5rem;
padding: 0.5rem 1rem;
margin: 0;
font-family: inherit;
outline: none;
}
label {
position: relative;
display: block;
overflow: hidden;
}
// 动态绑定元素
label::before {
content: attr(data-before);
}
label::after {
content: attr(data-after);
}
label::before,
label::after {
display: block;
position: absolute;
width: 100%;
height: 100%;
border: 3px solid transparent;
font-size: 3rem;
padding: 0.5rem 1rem;
font-family: inherit;
box-sizing: border-box;
color: #999;
}
@keyframes placeholder-before {
0% {
transform: translateY(0%);
opacity: 1;
}
100% {
transform: translateY(-100%);
opacity: 0;
}
}
@keyframes placeholder-after {
0% {
transform: translateY(0%);
opacity: 0;
}
100% {
transform: translateY(-100%);
opacity: 1;
}
}
.animate::before {
animation: 0.3s placeholder-before ease-in-out;
}
.animate::after {
animation: 0.3s placeholder-after ease-in-out;
}
.animate::before,
.animate::after {
animation-fill-mode: forwards;
}
</style>