最近看到一个挺有趣的input交互效果,尝试着用vue做一个,效果如下:
1.没有数值时,显示placeholder;
2.触发焦点时,label动画上移,placeholder清空;
1.有值时,label依旧存在;
vue代码如下:
<template>
<div class="page">
<div class="inputBox" :class="{inputBoxActive:isNameInputActive}">
<input class="inputSelf form-control" v-model="nameVal" :placeholder="placeholderName" @input="onInputName" @focus="onFocusName" @blur="onBlurName"></input>
<label class="inputLabel">your name</label>
</div>
<div class="inputBox" :class="{inputBoxActive:isPhoneInputActive}">
<input class="inputSelf form-control" v-model="phoneVal" :placeholder="placeholderPhone" @input="onInputPhone" @focus="onFocusPhone" @blur="onBlurPhone"></input>
<label class="inputLabel">your phone</label>
</div>
<div class="inputBox" :class="{inputBoxActive:isEmailInputActive}">
<input class="inputSelf form-control" v-model="emailVal" :placeholder="placeholderEmail" @input="onInputEmail" @focus="onFocusEmail" @blur="onBlurEmail"></input>
<label class="inputLabel">your email</label>
</div>
</div>
</template>
<script>
export default{
data(){
return{
isNameInputActive:false,
nameVal:'',
placeholderName:'your name',
isPhoneInputActive:false,
phoneVal:'',
placeholderPhone:'your phone',
isEmailInputActive:false,
emailVal:'',
placeholderEmail:'your email'
}
},
methods:{
onFocusName:function(){
this.isNameInputActive = true;
this.placeholderName = '';
},
onInputName:function(){
this.isNameInputActive = true;
this.placeholderName = '';
},
onBlurName:function(){
if(!this.nameVal){
this.isNameInputActive = false;
this.placeholderName = 'your name';
}else{
this.isNameInputActive = true;
}
},
onFocusPhone:function(){
this.isPhoneInputActive = true;
this.placeholderPhone = '';
},
onInputPhone:function(){
this.isPhoneInputActive = true;
this.placeholderPhone = '';
},
onBlurPhone:function(){
if(!this.phoneVal){
this.isPhoneInputActive = false;
this.placeholderPhone = 'your phone';
}else{
this.isPhoneInputActive = true;
}
},
onFocusEmail:function(){
this.isEmailInputActive = true;
this.placeholderEmail = '';
},
onInputEmail:function(){
this.isEmailInputActive = true;
this.placeholderEmail = '';
},
onBlurEmail:function(){
if(!this.emailVal){
this.isEmailInputActive = false;
this.placeholderEmail = 'your email';
}else{
this.isEmailInputActive = true;
}
}
}
}
</script>
<style scoped>
.form-control {
display: block;
width: 100%;
height: 36px;
padding: 6px 12px;
font-size: 16px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 2px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-webkit-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s
}
.form-control:focus {
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)
}
.form-control::-moz-placeholder {
color: #666;
opacity: 1
}
.form-control:-ms-input-placeholder {
color: #666
}
.form-control::-webkit-input-placeholder {
color: #666
}
.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control {
cursor: not-allowed;
background-color: #eee;
opacity: 1
}
.inputBox{
position:relative;
width:80%;
margin:0 auto;
margin-top:24px;
margin-bottom:8px;
}
.inputSelf{
z-index: 2;
position: relative;
background: 0;
-webkit-transition: all .4s ease;
transition: all .4s ease
}
.inputLabel {
position: absolute;
display: none;
width: 100%;
left: 0;
padding: 12px 16px;
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
font-weight: 500;
color: #333;
-webkit-transition: all .4s ease;
transition: all .4s ease;
}
label.inputLabel {
display:block;
opacity: 0;
bottom: 0;
padding: 12px 16px;
}
.inputBoxActive label.inputLabel{
display:block;
opacity: 1;
bottom: 100%;
padding: 0 16px;
}
</style>
虽然效果有了,但是同样的交互效果,应该独立成一个组件。
所以,接下来开始组件化。
第一步:创建myInput.vue。
<template>
<div class="inputBox" :class="{inputBoxActive:isInputActive}">
<input class="inputSelf form-control" v-model="inptVal" :placeholder="inptPlaceholder" @input="onInput" @focus="onFocus" @blur="onBlur" v-on:input="updateValue($event.target.value)"></input>
<label class="inputLabel">{{inptLabel}}</label>
</div>
</template>
<script>
export default{
data(){
return{
isInputActive:false,
inptVal:this.myVal,
inptPlaceholder:this.placeholderText,
inptLabel:this.placeholderText
}
},
props: {
myVal:{
type:String,
default:''
},
placeholderText:{
type:String,
default:''
}
},
methods: {
onFocus:function(){
this.isInputActive = true;
this.inptPlaceholder = '';
},
onInput:function(){
this.isInputActive = true;
this.inptPlaceholder = '';
},
onBlur:function(){
if(!this.inptVal){
this.isInputActive = false;
this.inptPlaceholder = this.placeholderText;
}else{
this.isInputActive = true;
}
},
updateValue: function (value) {
this.$emit('input', value)
}
}
}
</script>
<style scoped>
.form-control {
display: block;
width: 100%;
height: 36px;
padding: 6px 12px;
font-size: 16px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 2px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-webkit-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s
}
.form-control:focus {
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)
}
.form-control::-moz-placeholder {
color: #666;
opacity: 1
}
.form-control:-ms-input-placeholder {
color: #666
}
.form-control::-webkit-input-placeholder {
color: #666
}
.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control {
cursor: not-allowed;
background-color: #eee;
opacity: 1
}
.inputBox{
position:relative;
width:80%;
margin:0 auto;
margin-top:24px;
margin-bottom:8px;
}
.inputSelf{
z-index: 2;
position: relative;
background: 0;
-webkit-transition: all .4s ease;
transition: all .4s ease
}
.inputLabel {
position: absolute;
display: none;
width: 100%;
left: 0;
padding: 12px 16px;
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
font-weight: 500;
color: #333;
-webkit-transition: all .4s ease;
transition: all .4s ease;
}
label.inputLabel {
display:block;
opacity: 0;
bottom: 0;
padding: 12px 16px;
}
.inputBoxActive label.inputLabel{
display:block;
opacity: 1;
bottom: 100%;
padding: 0 16px;
}
</style>
需要注意在构建组件时,从父传入的props值,在子组件里不能直接修改,否则会报类似下边的错误:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritt ...
正确的应对方式是:
- 定义一个局部变量,并用 prop 的值初始化它:
data(){
return{
isInputActive:false,
inptVal:this.myVal,
inptPlaceholder:this.placeholderText,
inptLabel:this.placeholderText
}
},
props: {
myVal:{
type:String,
default:''
},
placeholderText:{
type:String,
default:''
}
}
- 定义一个计算属性,处理 prop 的值并返回:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
第二步:在页面里引入myInput组件。
<script>
import myInput from '../../components/myInput/myInput';
export default{
components: {
myInput
},
data(){
return{
nameVal:'',
placeholderName:'your name',
phoneVal:'',
placeholderPhone:'your phone',
emailVal:'',
placeholderEmail:'your email'
}
},
methods:{
saveForm:function(){
console.log(this.nameVal);
console.log(this.phoneVal);
console.log(this.emailVal);
},
inptParent1:function(params){
const trimmedText = params.trim();
this.nameVal = trimmedText;
},
inptParent2:function(params){
const trimmedText = params.trim();
this.phoneVal = trimmedText;
},
inptParent3:function(params){
const trimmedText = params.trim();
this.emailVal = trimmedText;
}
}
}
</script>
第三步:在html中引入myInput元素。
<template>
<div class="page">
<myInput
:myVal="nameVal"
:placeholderText="placeholderName"
v-on:input="inptParent1"
/>
<myInput
:myVal="phoneVal"
:placeholderText="placeholderPhone"
v-on:input="inptParent2"
/>
<myInput
:myVal="emailVal"
:placeholderText="placeholderEmail"
v-on:input="inptParent3"
/>
<div class="btnBox">
<Button type="ghost" @click="saveForm" shape="circle">提交</Button>
</div>
</div>
</template>
<style scoped>
.btnBox{
padding-top:24px;
text-align: center;
}
</style>
第四步:myInput组件的数值变化回传到父组件里。
在myInput组件的input上绑定v-on:input="updateValue($event.target.value)"触发方法:
<template>
<div class="inputBox" :class="{inputBoxActive:isInputActive}">
<input class="inputSelf form-control" v-model="inptVal" :placeholder="inptPlaceholder" @input="onInput" @focus="onFocus" @blur="onBlur" v-on:input="updateValue($event.target.value)"></input>
<label class="inputLabel">{{inptLabel}}</label>
</div>
</template>
在myInput组件注册updateValue方法:
methods: {
updateValue: function (value) {
this.$emit('input', value)
}
}
在父组件引用myInput组件,绑定v-on:input="inptParent1"方法:
<myInput
:myVal="nameVal"
:placeholderText="placeholderName"
v-on:input="inptParent1"
/>
在父组件注册inptParent1方法:
methods:{
inptParent1:function(params){
const trimmedText = params.trim();
this.nameVal = trimmedText;
}
}