提交 23d75de6 编写于 作者: Q qiang

fix: 修复 input、textarea 组件快速输入(删除)光标闪烁的问题

上级 5f5a576b
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
class="uni-input-wrapper" class="uni-input-wrapper"
> >
<div <div
v-show="!(composing || inputValue.length)" v-show="!(composing || valueSync.length)"
ref="placeholder" ref="placeholder"
:style="placeholderStyle" :style="placeholderStyle"
:class="placeholderClass" :class="placeholderClass"
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
/> />
<input <input
ref="input" ref="input"
v-model="inputValue" v-model="valueSync"
:disabled="disabled" :disabled="disabled"
:type="inputType" :type="inputType"
:maxlength="maxlength" :maxlength="maxlength"
...@@ -36,27 +36,18 @@ ...@@ -36,27 +36,18 @@
</template> </template>
<script> <script>
import { import {
emitter, baseInput
keyboard
} from 'uni-mixins' } from 'uni-mixins'
const INPUT_TYPES = ['text', 'number', 'idcard', 'digit', 'password'] const INPUT_TYPES = ['text', 'number', 'idcard', 'digit', 'password']
const NUMBER_TYPES = ['number', 'digit'] const NUMBER_TYPES = ['number', 'digit']
export default { export default {
name: 'Input', name: 'Input',
mixins: [emitter, keyboard], mixins: [baseInput],
model: {
prop: 'value',
event: 'update:value'
},
props: { props: {
name: { name: {
type: String, type: String,
default: '' default: ''
}, },
value: {
type: [String, Number],
default: ''
},
type: { type: {
type: String, type: String,
default: 'text' default: 'text'
...@@ -96,7 +87,6 @@ export default { ...@@ -96,7 +87,6 @@ export default {
}, },
data () { data () {
return { return {
inputValue: this._getValueString(this.value),
composing: false, composing: false,
wrapperHeight: 0, wrapperHeight: 0,
cachedValue: '' cachedValue: ''
...@@ -131,15 +121,9 @@ export default { ...@@ -131,15 +121,9 @@ export default {
focus (value) { focus (value) {
value && this._focusInput() value && this._focusInput()
}, },
value (value) {
this.inputValue = this._getValueString(value)
},
inputValue (value) {
this.$emit('update:value', value)
},
maxlength (value) { maxlength (value) {
const realValue = this.inputValue.slice(0, parseInt(value, 10)) const realValue = this.valueSync.slice(0, parseInt(value, 10))
realValue !== this.inputValue && (this.inputValue = realValue) realValue !== this.valueSync && (this.valueSync = realValue)
} }
}, },
created () { created () {
...@@ -196,11 +180,11 @@ export default { ...@@ -196,11 +180,11 @@ export default {
if (~NUMBER_TYPES.indexOf(this.type)) { if (~NUMBER_TYPES.indexOf(this.type)) {
if (this.$refs.input.validity && !this.$refs.input.validity.valid) { if (this.$refs.input.validity && !this.$refs.input.validity.valid) {
$event.target.value = this.cachedValue $event.target.value = this.cachedValue
this.inputValue = $event.target.value this.valueSync = $event.target.value
// 输入非法字符不触发 input 事件 // 输入非法字符不触发 input 事件
return return
} else { } else {
this.cachedValue = this.inputValue this.cachedValue = this.valueSync
} }
} }
...@@ -209,14 +193,13 @@ export default { ...@@ -209,14 +193,13 @@ export default {
const maxlength = parseInt(this.maxlength, 10) const maxlength = parseInt(this.maxlength, 10)
if (maxlength > 0 && $event.target.value.length > maxlength) { if (maxlength > 0 && $event.target.value.length > maxlength) {
$event.target.value = $event.target.value.slice(0, maxlength) $event.target.value = $event.target.value.slice(0, maxlength)
this.inputValue = $event.target.value this.valueSync = $event.target.value
// 字符长度超出范围不触发 input 事件 // 字符长度超出范围不触发 input 事件
return return
} }
} }
this.$triggerInput($event, {
this.$trigger('input', $event, { value: this.valueSync
value: this.inputValue
}) })
}, },
_onFocus ($event) { _onFocus ($event) {
...@@ -247,16 +230,13 @@ export default { ...@@ -247,16 +230,13 @@ export default {
} }
}, },
_resetFormData () { _resetFormData () {
this.inputValue = '' this.valueSync = ''
}, },
_getFormData () { _getFormData () {
return this.name ? { return this.name ? {
value: this.inputValue, value: this.valueSync,
key: this.name key: this.name
} : {} } : {}
},
_getValueString (value) {
return value === null ? '' : String(value)
} }
} }
} }
......
...@@ -49,26 +49,17 @@ ...@@ -49,26 +49,17 @@
</template> </template>
<script> <script>
import { import {
emitter, baseInput
keyboard
} from 'uni-mixins' } from 'uni-mixins'
const DARK_TEST_STRING = '(prefers-color-scheme: dark)' const DARK_TEST_STRING = '(prefers-color-scheme: dark)'
export default { export default {
name: 'Textarea', name: 'Textarea',
mixins: [emitter, keyboard], mixins: [baseInput],
model: {
prop: 'value',
event: 'update:value'
},
props: { props: {
name: { name: {
type: String, type: String,
default: '' default: ''
}, },
value: {
type: [String, Number],
default: ''
},
maxlength: { maxlength: {
type: [Number, String], type: [Number, String],
default: 140 default: 140
...@@ -116,7 +107,6 @@ export default { ...@@ -116,7 +107,6 @@ export default {
}, },
data () { data () {
return { return {
valueSync: this._getValueString(this.value),
valueComposition: '', valueComposition: '',
composition: false, composition: false,
focusSync: this.focus, focusSync: this.focus,
...@@ -148,19 +138,6 @@ export default { ...@@ -148,19 +138,6 @@ export default {
} }
}, },
watch: { watch: {
value (val) {
this.valueSync = this._getValueString(val)
},
valueSync (val) {
if (val !== this._oldValue) {
this._oldValue = val
this.$trigger('input', {}, {
value: val,
cursor: this.$refs.textarea.selectionEnd
})
this.$emit('update:value', val)
}
},
focus (val) { focus (val) {
if (val) { if (val) {
this.focusChangeSource = 'focus' this.focusChangeSource = 'focus'
...@@ -210,7 +187,6 @@ export default { ...@@ -210,7 +187,6 @@ export default {
}) })
}, },
mounted () { mounted () {
this._oldValue = this.$refs.textarea.value = this.valueSync
this._resize({ this._resize({
height: this.$refs.sensor.$el.offsetHeight height: this.$refs.sensor.$el.offsetHeight
}) })
...@@ -283,7 +259,12 @@ export default { ...@@ -283,7 +259,12 @@ export default {
_input ($event) { _input ($event) {
if (this.composition) { if (this.composition) {
this.valueComposition = $event.target.value this.valueComposition = $event.target.value
return
} }
this.$triggerInput($event, {
value: this.valueSync,
cursor: this.$refs.textarea.selectionEnd
})
}, },
_getFormData () { _getFormData () {
return { return {
...@@ -293,9 +274,6 @@ export default { ...@@ -293,9 +274,6 @@ export default {
}, },
_resetFormData () { _resetFormData () {
this.valueSync = '' this.valueSync = ''
},
_getValueString (value) {
return value === null ? '' : String(value)
} }
} }
} }
......
import {
debounce,
throttle
} from 'uni-shared'
import emitter from './emitter'
import keyboard from './keyboard'
export default {
name: 'BaseInput',
mixins: [emitter, keyboard],
model: {
prop: 'value',
event: 'update:value'
},
props: {
value: {
type: [String, Number],
default: ''
}
},
data () {
return {
valueSync: this._getValueString(this.value)
}
},
watch: {
valueSync (value) {
this.$emit('update:value', value)
}
},
created () {
const valueChange = this.__valueChange = debounce((val, oldVal) => {
this.valueSync = this._getValueString(val)
}, 100)
this.$watch('value', valueChange)
this.__triggerInput = throttle(($event, detail) => {
this.$trigger('input', $event, detail)
}, 100)
this.$triggerInput = ($event, detail) => {
this.__valueChange.cancel()
this.__triggerInput($event, detail)
}
},
beforeDestroy () {
this.__valueChange.cancel()
this.__triggerInput.cancel()
},
methods: {
_getValueString (value) {
return value === null ? '' : String(value)
}
}
}
...@@ -23,7 +23,12 @@ export { ...@@ -23,7 +23,12 @@ export {
} }
from './keyboard' from './keyboard'
export {
default as baseInput
}
from './base-input'
export { export {
default as interact default as interact
} }
from './interact' from './interact'
...@@ -86,11 +86,37 @@ export function guid () { ...@@ -86,11 +86,37 @@ export function guid () {
export function debounce (fn, delay) { export function debounce (fn, delay) {
let timeout let timeout
return function () { const newFn = function () {
clearTimeout(timeout) clearTimeout(timeout)
const timerFn = () => fn.apply(this, arguments) const timerFn = () => fn.apply(this, arguments)
timeout = setTimeout(timerFn, delay) timeout = setTimeout(timerFn, delay)
} }
newFn.cancel = function () {
clearTimeout(timeout)
}
return newFn
}
export function throttle (fn, wait) {
let last = 0
let timeout
const newFn = function (...arg) {
const now = Date.now()
clearTimeout(timeout)
const waitCallback = () => {
last = now
fn.apply(this, arg)
}
if (now - last < wait) {
timeout = setTimeout(waitCallback, wait - (now - last))
return
}
waitCallback()
}
newFn.cancel = function () {
clearTimeout(timeout)
}
return newFn
} }
export function kebabCase (string) { export function kebabCase (string) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册