mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
fix(textInput): hide cursor during scroll
This commit is contained in:
@ -2,6 +2,7 @@ import {Component, ElementRef, Optional, NgZone} from 'angular2/angular2';
|
|||||||
|
|
||||||
import {Ion} from '../ion';
|
import {Ion} from '../ion';
|
||||||
import {Config} from '../../config/config';
|
import {Config} from '../../config/config';
|
||||||
|
import {raf} from '../../util/dom';
|
||||||
import {Keyboard} from '../../util/keyboard';
|
import {Keyboard} from '../../util/keyboard';
|
||||||
import {ViewController} from '../nav/view-controller';
|
import {ViewController} from '../nav/view-controller';
|
||||||
import {Animation} from '../../animations/animation';
|
import {Animation} from '../../animations/animation';
|
||||||
@ -71,19 +72,34 @@ export class Content extends Ion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onScrollEnd(callback, debounceWait=400) {
|
onScrollEnd(callback) {
|
||||||
let timerId, deregister;
|
let lastScrollTop = null;
|
||||||
|
let framesUnchanged = 0;
|
||||||
|
let scrollElement = this.scrollElement;
|
||||||
|
|
||||||
function debounce() {
|
function next() {
|
||||||
console.debug('onScroll')
|
let currentScrollTop = scrollElement.scrollTop;
|
||||||
clearTimeout(timerId);
|
if (lastScrollTop !== null) {
|
||||||
timerId = setTimeout(() => {
|
|
||||||
deregister();
|
if (Math.round(lastScrollTop) === Math.round(currentScrollTop)) {
|
||||||
callback();
|
framesUnchanged++;
|
||||||
}, debounceWait);
|
} else {
|
||||||
|
framesUnchanged = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framesUnchanged > 9) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScrollTop = currentScrollTop;
|
||||||
|
|
||||||
|
raf(() => {
|
||||||
|
raf(next);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deregister = this.addScrollEventListener(debounce);
|
setTimeout(next, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,11 +83,19 @@ export class TextInput {
|
|||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
self.scrollMove = (ev) => {
|
self.scrollMove = (ev) => {
|
||||||
console.debug('content scrollMove');
|
if (!self.app.isTransitioning()) {
|
||||||
self.deregListeners();
|
self.deregMove();
|
||||||
|
|
||||||
if (self.hasFocus) {
|
if (self.hasFocus) {
|
||||||
//self.input.hideFocus();
|
self.input.hideFocus(true);
|
||||||
|
this.scrollView.onScrollEnd(() => {
|
||||||
|
self.input.hideFocus(false);
|
||||||
|
|
||||||
|
if (self.hasFocus) {
|
||||||
|
self.regMove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -131,6 +139,7 @@ export class TextInput {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
this.setFocus();
|
this.setFocus();
|
||||||
|
this.regMove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,10 +158,12 @@ export class TextInput {
|
|||||||
let ele = this.elementRef.nativeElement;
|
let ele = this.elementRef.nativeElement;
|
||||||
|
|
||||||
let scrollData = TextInput.getScollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), this.keyboardHeight, this.platform.height());
|
let scrollData = TextInput.getScollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), this.keyboardHeight, this.platform.height());
|
||||||
if (scrollData.noScroll) {
|
if (scrollData.scrollAmount > -3 && scrollData.scrollAmount < 3) {
|
||||||
// the text input is in a safe position that doesn't require
|
// the text input is in a safe position that doesn't require
|
||||||
// it to be scrolled into view, just set focus now
|
// it to be scrolled into view, just set focus now
|
||||||
return this.setFocus();
|
this.setFocus();
|
||||||
|
this.regMove();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add padding to the bottom of the scroll view (if needed)
|
// add padding to the bottom of the scroll view (if needed)
|
||||||
@ -178,11 +189,13 @@ export class TextInput {
|
|||||||
// all good, allow clicks again
|
// all good, allow clicks again
|
||||||
this.app.setEnabled(true);
|
this.app.setEnabled(true);
|
||||||
this.app.setTransitioning(false);
|
this.app.setTransitioning(false);
|
||||||
|
this.regMove();
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// not inside of a scroll view, just focus it
|
// not inside of a scroll view, just focus it
|
||||||
this.setFocus();
|
this.setFocus();
|
||||||
|
this.regMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -224,13 +237,6 @@ export class TextInput {
|
|||||||
7) Input top below safe area, no room to scroll, input larger than safe area
|
7) Input top below safe area, no room to scroll, input larger than safe area
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (inputTopWithinSafeArea && inputBottomWithinSafeArea) {
|
|
||||||
// Input top within safe area, bottom within safe area
|
|
||||||
// no need to scroll to a position, it's good as-is
|
|
||||||
return { noScroll: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
// looks like we'll have to do some auto-scrolling
|
|
||||||
let scrollData = {
|
let scrollData = {
|
||||||
scrollAmount: 0,
|
scrollAmount: 0,
|
||||||
scrollTo: 0,
|
scrollTo: 0,
|
||||||
@ -238,6 +244,13 @@ export class TextInput {
|
|||||||
inputSafeY: 0
|
inputSafeY: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (inputTopWithinSafeArea && inputBottomWithinSafeArea) {
|
||||||
|
// Input top within safe area, bottom within safe area
|
||||||
|
// no need to scroll to a position, it's good as-is
|
||||||
|
return scrollData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// looks like we'll have to do some auto-scrolling
|
||||||
if (inputTopBelowSafeArea || inputBottomBelowSafeArea) {
|
if (inputTopBelowSafeArea || inputBottomBelowSafeArea) {
|
||||||
// Input top and bottom below safe area
|
// Input top and bottom below safe area
|
||||||
// auto scroll the input up so at least the top of it shows
|
// auto scroll the input up so at least the top of it shows
|
||||||
@ -245,13 +258,13 @@ export class TextInput {
|
|||||||
if (safeAreaHeight > inputOffsetHeight) {
|
if (safeAreaHeight > inputOffsetHeight) {
|
||||||
// safe area height is taller than the input height, so we
|
// safe area height is taller than the input height, so we
|
||||||
// can bring it up the input just enough to show the input bottom
|
// can bring it up the input just enough to show the input bottom
|
||||||
scrollData.scrollAmount = (safeAreaBottom - inputBottom);
|
scrollData.scrollAmount = Math.round(safeAreaBottom - inputBottom);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// safe area height is smaller than the input height, so we can
|
// safe area height is smaller than the input height, so we can
|
||||||
// only scroll it up so the input top is at the top of the safe area
|
// only scroll it up so the input top is at the top of the safe area
|
||||||
// however the input bottom will be below the safe area
|
// however the input bottom will be below the safe area
|
||||||
scrollData.scrollAmount = (safeAreaTop - inputTop);
|
scrollData.scrollAmount = Math.round(safeAreaTop - inputTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollData.inputSafeY = -(inputTop - safeAreaTop) + 4;
|
scrollData.inputSafeY = -(inputTop - safeAreaTop) + 4;
|
||||||
@ -259,10 +272,9 @@ export class TextInput {
|
|||||||
} else if (inputTopAboveSafeArea) {
|
} else if (inputTopAboveSafeArea) {
|
||||||
// Input top above safe area
|
// Input top above safe area
|
||||||
// auto scroll the input down so at least the top of it shows
|
// auto scroll the input down so at least the top of it shows
|
||||||
scrollData.scrollAmount = (safeAreaTop - inputTop);
|
scrollData.scrollAmount = Math.round(safeAreaTop - inputTop);
|
||||||
|
|
||||||
scrollData.inputSafeY = (safeAreaTop - inputTop) + 4;
|
scrollData.inputSafeY = (safeAreaTop - inputTop) + 4;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// figure out where it should scroll to for the best position to the input
|
// figure out where it should scroll to for the best position to the input
|
||||||
@ -287,7 +299,7 @@ export class TextInput {
|
|||||||
// window.safeAreaEle = document.createElement('div');
|
// window.safeAreaEle = document.createElement('div');
|
||||||
// window.safeAreaEle.style.position = 'absolute';
|
// window.safeAreaEle.style.position = 'absolute';
|
||||||
// window.safeAreaEle.style.background = 'rgba(0, 128, 0, 0.7)';
|
// window.safeAreaEle.style.background = 'rgba(0, 128, 0, 0.7)';
|
||||||
// window.safeAreaEle.style.padding = '10px';
|
// window.safeAreaEle.style.padding = '2px 5px';
|
||||||
// window.safeAreaEle.style.textShadow = '1px 1px white';
|
// window.safeAreaEle.style.textShadow = '1px 1px white';
|
||||||
// window.safeAreaEle.style.left = '0px';
|
// window.safeAreaEle.style.left = '0px';
|
||||||
// window.safeAreaEle.style.right = '0px';
|
// window.safeAreaEle.style.right = '0px';
|
||||||
@ -316,7 +328,8 @@ export class TextInput {
|
|||||||
focusChange(hasFocus) {
|
focusChange(hasFocus) {
|
||||||
this.renderer.setElementClass(this.elementRef, 'has-focus', hasFocus);
|
this.renderer.setElementClass(this.elementRef, 'has-focus', hasFocus);
|
||||||
if (!hasFocus) {
|
if (!hasFocus) {
|
||||||
this.deregListeners();
|
this.deregMove();
|
||||||
|
this.input.hideFocus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,17 +353,24 @@ export class TextInput {
|
|||||||
// ensure the body hasn't scrolled down
|
// ensure the body hasn't scrolled down
|
||||||
document.body.scrollTop = 0;
|
document.body.scrollTop = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
regMove() {
|
||||||
if (this.scrollAssist && this.scrollView) {
|
if (this.scrollAssist && this.scrollView) {
|
||||||
this.deregListeners();
|
setTimeout(() => {
|
||||||
this.deregScroll = this.scrollView.addScrollEventListener(this.scrollMove);
|
this.deregMove();
|
||||||
|
this.deregScroll = this.scrollView.addScrollEventListener(this.scrollMove);
|
||||||
|
}, 80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
deregListeners() {
|
deregMove() {
|
||||||
this.deregScroll && this.deregScroll();
|
this.deregScroll && this.deregScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +385,7 @@ export class TextInput {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
this.deregListeners();
|
this.deregMove();
|
||||||
this.form.deregister(this);
|
this.form.deregister(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,12 +448,14 @@ export class TextInputElement {
|
|||||||
if (shouldRelocate) {
|
if (shouldRelocate) {
|
||||||
let clonedInputEle = focusedInputEle.cloneNode(true);
|
let clonedInputEle = focusedInputEle.cloneNode(true);
|
||||||
clonedInputEle.classList.add('cloned-input');
|
clonedInputEle.classList.add('cloned-input');
|
||||||
|
clonedInputEle.classList.remove('hide-focused-input');
|
||||||
clonedInputEle.setAttribute('aria-hidden', true);
|
clonedInputEle.setAttribute('aria-hidden', true);
|
||||||
clonedInputEle.tabIndex = -1;
|
clonedInputEle.tabIndex = -1;
|
||||||
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
|
|
||||||
|
|
||||||
focusedInputEle.classList.add('hide-focused-input');
|
focusedInputEle.classList.add('hide-focused-input');
|
||||||
focusedInputEle.style[dom.CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`;
|
focusedInputEle.style[dom.CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`;
|
||||||
|
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
|
||||||
|
|
||||||
this.wrapper.setFocus();
|
this.wrapper.setFocus();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -449,19 +471,27 @@ export class TextInputElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hideFocus() {
|
hideFocus(shouldHideFocus) {
|
||||||
console.debug('hideFocus');
|
|
||||||
let focusedInputEle = this.getNativeElement();
|
let focusedInputEle = this.getNativeElement();
|
||||||
|
|
||||||
let hiddenInputEle = focusedInputEle.parentNode.querySelector('.cloned-hidden');
|
if (shouldHideFocus) {
|
||||||
if (!hiddenInputEle) {
|
let clonedInputEle = focusedInputEle.cloneNode(true);
|
||||||
hiddenInputEle = focusedInputEle.cloneNode(true);
|
clonedInputEle.classList.add('cloned-hidden');
|
||||||
hiddenInputEle.classList.add('cloned-hidden');
|
clonedInputEle.setAttribute('aria-hidden', true);
|
||||||
hiddenInputEle.setAttribute('aria-hidden', true);
|
clonedInputEle.tabIndex = -1;
|
||||||
hiddenInputEle.tabIndex = -1;
|
|
||||||
focusedInputEle.parentNode.appendChild(hiddenInputEle);
|
focusedInputEle.classList.add('hide-focused-input');
|
||||||
|
focusedInputEle.style[dom.CSS.transform] = 'translate3d(-9999px,0,0)';
|
||||||
|
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
focusedInputEle.classList.remove('hide-focused-input');
|
||||||
|
focusedInputEle.style[dom.CSS.transform] = '';
|
||||||
|
let clonedInputEle = focusedInputEle.parentNode.querySelector('.cloned-hidden');
|
||||||
|
if (clonedInputEle) {
|
||||||
|
clonedInputEle.parentNode.removeChild(clonedInputEle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hiddenInputEle.focus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasFocus() {
|
get hasFocus() {
|
||||||
|
@ -48,7 +48,7 @@ Platform.register({
|
|||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
hoverCSS: false,
|
hoverCSS: false,
|
||||||
keyboardHeight: 290,
|
keyboardHeight: 300,
|
||||||
mode: 'md',
|
mode: 'md',
|
||||||
scrollAssist: true,
|
scrollAssist: true,
|
||||||
},
|
},
|
||||||
@ -71,7 +71,7 @@ Platform.register({
|
|||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
hoverCSS: false,
|
hoverCSS: false,
|
||||||
keyboardHeight: 290,
|
keyboardHeight: 300,
|
||||||
mode: 'ios',
|
mode: 'ios',
|
||||||
scrollAssist: isIOSDevice,
|
scrollAssist: isIOSDevice,
|
||||||
swipeBackEnabled: isIOSDevice,
|
swipeBackEnabled: isIOSDevice,
|
||||||
|
Reference in New Issue
Block a user