improve perf of autosize.js

This commit is contained in:
Nolan Lawson 2018-02-25 22:29:12 -08:00
parent aed194fced
commit 96303583ad
2 changed files with 28 additions and 63 deletions

View File

@ -87,6 +87,7 @@
margin-top: 10px; margin-top: 10px;
resize: none; resize: none;
overflow: hidden; overflow: hidden;
word-wrap: break-word;
/* Text must be at least 16px or else iOS Safari zooms in */ /* Text must be at least 16px or else iOS Safari zooms in */
font-size: 1.2em; font-size: 1.2em;
/* Hack to make Edge stretch the element all the way to the right. /* Hack to make Edge stretch the element all the way to the right.
@ -152,10 +153,6 @@
export default { export default {
oncreate() { oncreate() {
this.set({inputText: store.get('currentInputTextInCompose')}) this.set({inputText: store.get('currentInputTextInCompose')})
mark('autosize()')
autosize(this.refs.textarea) // TODO: this layout thrashes, could maybe find a better design
stop('autosize()')
const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000) const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
this.observe('inputText', inputText => { this.observe('inputText', inputText => {
@ -166,6 +163,12 @@
saveText() saveText()
}, {init: false}) }, {init: false})
requestAnimationFrame(() => {
mark('autosize()')
autosize(this.refs.textarea)
stop('autosize()')
})
// Avoid input delays by updating these values after a rAF // Avoid input delays by updating these values after a rAF
this.observe('inputLengthToDisplay', inputLengthToDisplay => { this.observe('inputLengthToDisplay', inputLengthToDisplay => {
requestAnimationFrame(() => { requestAnimationFrame(() => {

View File

@ -1,4 +1,4 @@
// Modified from https://github.com/jackmoore/autosize/commit/113f1b345868901619d4b01cda02b09aa1690ebd // Modified from https://github.com/jackmoore/autosize/blob/113f1b3/src/autosize.js
// The only change is to remove IE-specific hacks, // The only change is to remove IE-specific hacks,
// remove parent overflow checks, make page resizes more performant, // remove parent overflow checks, make page resizes more performant,
// add deferredUpdate, and add perf marks. // add deferredUpdate, and add perf marks.
@ -11,30 +11,13 @@ const map = new Map()
let createEvent = (name) => new Event(name, {bubbles: true}) let createEvent = (name) => new Event(name, {bubbles: true})
function assign (ta) { function assign (ta) {
if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) return if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) {
return
}
let heightOffset = null
let cachedHeight = null let cachedHeight = null
function init () { function init () {
const style = window.getComputedStyle(ta, null)
if (style.resize === 'vertical') {
ta.style.resize = 'none'
} else if (style.resize === 'both') {
ta.style.resize = 'horizontal'
}
if (style.boxSizing === 'content-box') {
heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom))
} else {
heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth)
}
// Fix when a textarea is not on document body and heightOffset is Not a Number
if (isNaN(heightOffset)) {
heightOffset = 0
}
update() update()
} }
@ -50,7 +33,7 @@ function assign (ta) {
ta.style.height = '' ta.style.height = ''
let endHeight = ta.scrollHeight + heightOffset let endHeight = ta.scrollHeight
if (ta.scrollHeight === 0) { if (ta.scrollHeight === 0) {
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
@ -86,32 +69,20 @@ function assign (ta) {
const pageResize = debounce(update, 1000) const pageResize = debounce(update, 1000)
const destroy = (style => { const destroy = () => {
window.removeEventListener('resize', pageResize, false) window.removeEventListener('resize', pageResize, false)
ta.removeEventListener('input', deferredUpdate, false) ta.removeEventListener('input', deferredUpdate, false)
ta.removeEventListener('autosize:destroy', destroy, false) ta.removeEventListener('autosize:destroy', destroy, false)
ta.removeEventListener('autosize:update', update, false) ta.removeEventListener('autosize:update', update, false)
Object.keys(style).forEach(key => {
ta.style[key] = style[key]
})
map.delete(ta) map.delete(ta)
}).bind(ta, { }
height: ta.style.height,
resize: ta.style.resize,
overflowY: ta.style.overflowY,
overflowX: ta.style.overflowX,
wordWrap: ta.style.wordWrap
})
ta.addEventListener('autosize:destroy', destroy, false) ta.addEventListener('autosize:destroy', destroy, false)
window.addEventListener('resize', pageResize, false) window.addEventListener('resize', pageResize, false)
ta.addEventListener('input', deferredUpdate, false) ta.addEventListener('input', deferredUpdate, false)
ta.addEventListener('autosize:update', update, false) ta.addEventListener('autosize:update', update, false)
ta.style.overflowX = 'hidden'
ta.style.wordWrap = 'break-word'
map.set(ta, { map.set(ta, {
destroy, destroy,
@ -135,32 +106,23 @@ function update (ta) {
} }
} }
let autosize = null let autosize = (el, options) => {
if (el) {
// Do nothing in Node.js environment and IE8 (or lower) Array.prototype.forEach.call(el.length ? el : [el], x => assign(x, options))
if (!process.browser) {
autosize = el => el
autosize.destroy = el => el
autosize.update = el => el
} else {
autosize = (el, options) => {
if (el) {
Array.prototype.forEach.call(el.length ? el : [el], x => assign(x, options))
}
return el
} }
autosize.destroy = el => { return el
if (el) { }
Array.prototype.forEach.call(el.length ? el : [el], destroy) autosize.destroy = el => {
} if (el) {
return el Array.prototype.forEach.call(el.length ? el : [el], destroy)
} }
autosize.update = el => { return el
if (el) { }
Array.prototype.forEach.call(el.length ? el : [el], update) autosize.update = el => {
} if (el) {
return el Array.prototype.forEach.call(el.length ? el : [el], update)
} }
return el
} }
export { autosize } export { autosize }