基于vue实现的 input 小组件

最近看到一个挺有趣的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 ...

正确的应对方式是:

  1. 定义一个局部变量,并用 prop 的值初始化它:
data(){
        return{
            isInputActive:false,
            inptVal:this.myVal,
            inptPlaceholder:this.placeholderText,
            inptLabel:this.placeholderText
        }
},
props: {
        myVal:{
            type:String,
            default:''
        },
        placeholderText:{
            type:String,
            default:''
        }
}
  1. 定义一个计算属性,处理 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;
            }
        }
posted @ 2017-10-24 10:44  MiyaMiya  阅读(619)  评论(0编辑  收藏  举报