Laya 文本相關(guān)類(lèi)(TextArea高度自適應(yīng))

一、位于laya.display包下的兩個(gè)類(lèi)

1.class Text extends Sprite
內(nèi)部使用了graphics.fillBorderText繪制文本

2.class Input extends Text
內(nèi)部封裝了原生的文本輸入框蟆融,并且是全局唯一的

private static function _createInputElement():void {
    _initInput(area = Browser.createElement("textarea"));
    _initInput(input = Browser.createElement("input"));
    
    inputContainer = Browser.createElement("div");
    inputContainer.style.position = "absolute";
    inputContainer.style.zIndex = 1E5;
    Browser.container.appendChild(inputContainer);
    //[IF-SCRIPT] inputContainer.setPos = function(x:int, y:int):void
    { inputContainer.style.left = x + 'px'; inputContainer.style.top = y + 'px'; };
}

在focusin時(shí)把原生的textarea或input添加到div中顯示出來(lái)草巡,在focusout時(shí)再移除。

//Input.as
public function set focus(value:Boolean):void {
    var input:* = nativeInput;
    
    if (_focus !== value) {
        if (value) {
            if (input.target) {
                input.target._focusOut();
            } else {
                _setInputMethod();
            }
            input.target = this;
            
            _focusIn();
        } else {
            input.target = null;
            _focusOut();
            Browser.document.body.scrollTop = 0;
            input.blur();
            
            if (Render.isConchApp) {
                input.setPos(-10000, -10000);
            } else if (inputContainer.contains(input))
                inputContainer.removeChild(input);
        }
    }
}

private function _setInputMethod():void {
    input.parentElement && (inputContainer.removeChild(input));
    area.parentElement && (inputContainer.removeChild(area));
    
    inputElement = (_multiline ? area : input);
    inputContainer.appendChild(inputElement);
    if (Text.RightToLeft)
    {
        inputElement.style.direction = "rtl";
    }
}

3.自動(dòng)獲取焦點(diǎn)型酥,并彈出鍵盤(pán)
參考彈出一個(gè)面板 想讓焦點(diǎn)自動(dòng)在輸入文本上且彈出手機(jī)鍵盤(pán)山憨,官方答復(fù)是:“軟鍵盤(pán)彈出必須有觸發(fā)行為,不支持直接默認(rèn)彈出虛擬鍵盤(pán)弥喉!”
其實(shí)在安卓上可以做到的郁竟,設(shè)置完input.focus=true之后,再調(diào)用_popupInputMethod即可由境。但I(xiàn)OS上無(wú)效棚亩。

public focusInInput() {
    this.chatInput.inputChat.focus = true;
    //移動(dòng)平臺(tái)在單擊事件觸發(fā)后彈出輸入法
    var layaInput:any = Laya.Input;
    layaInput._popupInputMethod();
}

// 移動(dòng)平臺(tái)在單擊事件觸發(fā)后彈出輸入法
private static function _popupInputMethod(e:*):void {
    //e.preventDefault();
    if (!Input.isInputting) return;
    
    var input:* = Input.inputElement;
    
    // 彈出輸入法。
    input.focus();
}
二虏杰、位于laya.ui包下的兩個(gè)類(lèi)

1.class Component extends Sprite

2.class Label extends Component
//內(nèi)部封裝了一個(gè)Text讥蟆,全是調(diào)用Text的方法,基本沒(méi)有新代碼纺阔。
protected var _tf:Text;

3.class TextInput extends Label

/**@inheritDoc */
override protected function createChildren():void {
    addChild(_tf = new Input());
    _tf.padding = Styles.inputLabelPadding;
    _tf.on(Event.INPUT, this, _onInput);
    _tf.on(Event.ENTER, this, _onEnter);
    _tf.on(Event.BLUR, this, _onBlur);
    _tf.on(Event.FOCUS, this, _onFocus);
}

在這段代碼中瘸彤,把_tf實(shí)例化為一個(gè)Input(Input是Text的一個(gè)子類(lèi),沒(méi)有毛病)笛钝。所以可以看作TextInput是對(duì)Input的一個(gè)封裝质况,比如很多方法是這樣的:

public function get maxChars():int {
    return Input(_tf).maxChars;
}

public function set maxChars(value:int):void {
    Input(_tf).maxChars = value;
}

4.class TextArea extends TextInput
內(nèi)部封裝了兩個(gè)滾動(dòng)條愕宋,還有一些控制邏輯。

/**@private */
protected var _vScrollBar:VScrollBar;
/**@private */
protected var _hScrollBar:HScrollBar;
三结榄、在處理高度自適應(yīng)時(shí)中贝,追蹤了height屬性

在class TextInput extends Label看到這段:

//TextInput.as
override protected function initialize():void {
    width = 128;
    height = 22;
}

override public function set height(value:Number):void {
    super.height = value;
    _bg && (_bg.height = value);
}

調(diào)用了super.height,所以去Label.as中看一看:

//Label.as
override public function set height(value:Number):void {
    super.height = value;
    _tf.height = value;
}

調(diào)用了_tf.height潭陪,所以去了Text.as中:

//Text.as
override public function set height(value:Number):void {
    if (value != _height) {
        super.height = value;
        isChanged = true;
    }
}

Text.as的super.height就是指向Sprite.as類(lèi)了

//Sprite.as
public function set height(value:Number):void {
    if (this._height !== value) {
        this._height = value;
        conchModel && conchModel.size(this._width, value);
        repaint();
    }
}

再往回看雄妥,因?yàn)閏lass Input extends Text,所以Input類(lèi)繼承了Text類(lèi)的 set height依溯,但是Input類(lèi)的構(gòu)造方法中老厌,又指定了寬高值:

Input.as
/**創(chuàng)建一個(gè)新的 <code>Input</code> 類(lèi)實(shí)例。*/
public function Input() {
    _width = 100;
    _height = 20;
    
    multiline = false;
    overflow = Text.SCROLL;
    
    on(Event.MOUSE_DOWN, this, _onMouseDown);
    on(Event.UNDISPLAY, this, _onUnDisplay);
}
四黎炉、進(jìn)入正題枝秤,textarea高度自適應(yīng)

在上述引擎源碼分析中,可以看到class Input extends Text類(lèi)中慷嗜,是有一個(gè)原生的textarea的淀弹。作為靜態(tài)屬性,是全局唯一的庆械,在Laya.init()中就會(huì)將其初始化薇溃。在HTML頁(yè)面中也能看到基本屬性:

<textarea style="position: absolute; overflow: hidden; resize: none;
 transform-origin: 0px 0px 0px; background-color: transparent; border: none;
 outline: none; z-index: 1; white-space: pre-wrap; color: rgb(51, 51, 51);
 font-size: 32px; font-family: Arial; line-height: 32px; font-style: normal;
 font-weight: normal; text-align: left; padding: 0px; width: 532px;
 height: 62px;" maxlength="100000" placeholder="">
</textarea>

所以要找到原生JS是如何處理textarea高度自適應(yīng),這里參考SegmentFault 如何創(chuàng)建一個(gè)高度自適應(yīng)的textarea
第一種方式是把textarea替換成一個(gè)div缭乘,再把div的contentEditable設(shè)置為true沐序。

第二種就是利用scrollHeight,設(shè)置到height上堕绩。這個(gè)可以參考http://www.jacklmoore.com/autosize/策幼,我最終使用的就是這個(gè)庫(kù)。當(dāng)然也有手寫(xiě)的奴紧,這個(gè)網(wǎng)上帖子也比較多特姐,可以參考textarea如何實(shí)現(xiàn)高度自適應(yīng)(不出現(xiàn)滾動(dòng)條)?

第三種是1樓高贊回復(fù)黍氮,在Textarea同級(jí)放一個(gè)pre 和span標(biāo)簽唐含,然后把輸入內(nèi)容實(shí)時(shí)同步到span里。pre會(huì)隨內(nèi)容的高度變化而變化沫浆,expandingArea的高度又隨pre變化觉壶,因?yàn)閠extarea的高度100% textarea的高度會(huì)隨expandingArea變化,只要同步textarea的內(nèi)容到pre中件缸,就達(dá)到一個(gè)textarea隨內(nèi)容高度變化的目的了。

<div class="expandingArea">
    <pre><span></span><br></pre>
    <textarea placeholder="輸入文字"></textarea>
</div>
五叔遂、使用jackmoore autosize庫(kù)遇到的一些問(wèn)題

1.autosize庫(kù)源碼中的resize方法有這樣兩行:

ta.style.height = '';
ta.style.height = ta.scrollHeight + heightOffset + 'px';

將height置為空串這個(gè)操作他炊,經(jīng)過(guò)我的測(cè)試争剿,如果注釋掉的話,在刪除一行時(shí)痊末,textarea不會(huì)自動(dòng)縮回去蚕苇。但是這行代碼是有副作用的,那就是在輸入第一行時(shí)凿叠,會(huì)多出來(lái)一個(gè)空行涩笤。后來(lái)參考論壇回復(fù),把rows設(shè)置為1解決了盒件。在Laya中的代碼可以在Laya.init執(zhí)行后去操作Textarea的屬性蹬碧。

//Input.as
/**@private */
protected static var input:*;
/**@private */
protected static var area:*;
/**@private */
protected static var inputElement:*;
/**@private */
protected static var inputContainer:*;

private static function _createInputElement():void {
    _initInput(area = Browser.createElement("textarea"));
    _initInput(input = Browser.createElement("input"));
    ……

area無(wú)法直接訪問(wèn),所以只能這樣寫(xiě)了:

Laya.init(750, 1334, Laya.WebGL);
Laya.Input["area"].rows = 1;

2._syncInputTransform在手機(jī)上不執(zhí)行
在Input.as的_focusIn方法中炒刁,最后有這么一段:

// 輸入框重定位恩沽。
_syncInputTransform();
if (!Render.isConchApp && Browser.onPC)
    Laya.timer.frameLoop(1, this, _syncInputTransform);

為什么只有在Browser.onPC時(shí),才執(zhí)行這個(gè)重定位呢翔始,原因不明罗心。那么只能偵聽(tīng)Laya.Event.INPUT事件,自己再手動(dòng)執(zhí)行_syncInputTransform方法了城瞎。

/**
 * 在輸入期間渤闷,如果 Input 實(shí)例的位置改變,調(diào)用_syncInputTransform同步輸入框的位置脖镀。
 */
private function _syncInputTransform():void {
    var inputElement:Object = nativeInput;
    var transform:Object = 
    Utils.getTransformRelativeToWindow(this, padding[3], padding[0]);
    var inputWid:int = _width - padding[1] - padding[3];
    var inputHei:int = _height - padding[0] - padding[2];
    if (Render.isConchApp) {
        inputElement.setScale(transform.scaleX, transform.scaleY);
        inputElement.setSize(inputWid, inputHei);
        inputElement.setPos(transform.x, transform.y);
    } else {
        //[IF-SCRIPT]inputContainer.style.transform = 
        inputContainer.style.webkitTransform = 
        "scale(" + transform.scaleX + "," + 
        transform.scaleY + ") rotate(" + (Laya.stage.canvasDegree) + "deg)";
        //[IF-SCRIPT]inputElement.style.width = inputWid + 'px';
        //[IF-SCRIPT]inputElement.style.height = inputHei + 'px';
        //[IF-SCRIPT]inputContainer.style.left = transform.x + 'px';
        //[IF-SCRIPT]inputContainer.style.top  = transform.y + 'px';
    }
}

這里原始的_syncInputTransform會(huì)考慮padding后飒箭,去設(shè)置style.width和height。因?yàn)槲覀円呀?jīng)在autosize中計(jì)算了寬高认然,所以_syncInputTransform方法中就不用再計(jì)算了补憾,只要設(shè)置inputContainer.style.left和top即可。

六卷员、彈出鍵盤(pán)不遮擋輸入框

參考H5移動(dòng)端彈出鍵盤(pán)時(shí)遮擋輸入框盈匾,使用了文中第一種方式

private initLayaInput():void{
    var ta:any = Laya.Input["area"];
    //避免textarea出現(xiàn)空行
    ta.rows = 1;
    ta.onfocus = this.taFocusIn;
    ta.onblur = this.taFocusOut;
}

private taFocusIn: any = this.delayScrollBody.bind(this);
private delayScrollBody(): void {
    Laya.timer.loop(500,this,this.scrollDocBody);
}

private scrollDocBody():void{
    Laya.Browser.document.body.scrollTop = 
    Laya.Browser.document.body.scrollHeight;
}

private taFocusOut: any = this.removeDelayScrollBody.bind(this);
private removeDelayScrollBody(): void {
    Laya.timer.clear(this,this.scrollDocBody);
}

另外,也可以參考移動(dòng)端iOS第三方輸入法遮擋底部input及android鍵盤(pán)回落后留白問(wèn)題

七毕骡、焦點(diǎn)控制
//Input.as
/**創(chuàng)建一個(gè)新的 <code>Input</code> 類(lèi)實(shí)例削饵。*/
public function Input() {
    _width = 100;
    _height = 20;
    
    multiline = false;
    overflow = Text.SCROLL;
    
    on(Event.MOUSE_DOWN, this, _onMouseDown);
    on(Event.UNDISPLAY, this, _onUnDisplay);
}

private function _onUnDisplay(e:Event = null):void {
    focus = false;
}

private function _onMouseDown(e:Event):void {
    focus = true;
}

// 移動(dòng)平臺(tái)最后單擊畫(huà)布才會(huì)調(diào)用focus
// 因此 調(diào)用focus接口是無(wú)法都在移動(dòng)平臺(tái)立刻彈出鍵盤(pán)的
public function set focus(value:Boolean):void {
    var input:* = nativeInput;
    
    if (_focus !== value) {
        if (value) {
            if (input.target) {
                input.target._focusOut();
            } else {
                _setInputMethod();
            }
            input.target = this;
            
            _focusIn();
        } else {
            input.target = null;
            _focusOut();
            Browser.document.body.scrollTop = 0;
            input.blur();
            
            if (Render.isConchApp) {
                input.setPos(-10000, -10000);
            } else if (inputContainer.contains(input))
                inputContainer.removeChild(input);
        }
    }
}

這里看到,偵聽(tīng)Event.MOUSE_DOWN獲得了焦點(diǎn)未巫。那么點(diǎn)擊INPUT之外的區(qū)域窿撬,是怎么失去焦點(diǎn)的呢。經(jīng)過(guò)查找叙凡,在MouseManager.as中找到了:

private function onMouseDown(ele:*):void {
    if (Input.isInputting && Laya.stage.focus && 
    Laya.stage.focus["focus"] && !Laya.stage.focus.contains(_target)) {
        // 從UI Input組件中取得Input引用
        // _tf 是TextInput的屬性
        var pre_input:* = Laya.stage.focus['_tf'] || Laya.stage.focus;
        var new_input:Input = ele['_tf'] || ele;
        
        // 新的焦點(diǎn)是Input的情況下劈伴,不需要blur;
        // 不過(guò)如果是Input和TextArea之間的切換握爷,還是需要重新彈出輸入法跛璧;
        if (new_input is Input && new_input.multiline == pre_input.multiline)
            pre_input['_focusOut']();
        else
            pre_input.focus = false;
    }
    TouchManager.I.onMouseDown(ele, _tTouchID, _isLeftMouse);
}

參考一下Input.focus代碼严里,可以看出執(zhí)行focus=false與執(zhí)行_focusOut方法的區(qū)別。最重要一點(diǎn)是追城,focus=false會(huì)額外執(zhí)行input.blur()刹碾,這將導(dǎo)致收起軟鍵盤(pán)

// 移動(dòng)平臺(tái)最后單擊畫(huà)布才會(huì)調(diào)用focus
// 因此 調(diào)用focus接口是無(wú)法都在移動(dòng)平臺(tái)立刻彈出鍵盤(pán)的
public function set focus(value:Boolean):void {
    var input:* = nativeInput;
    
    if (_focus !== value) {
        if (value) {
            if (input.target) {
                input.target._focusOut();
            } else {
                _setInputMethod();
            }
            input.target = this;
            
            _focusIn();
        } else {
            input.target = null;
            _focusOut();
            Browser.document.body.scrollTop = 0;
            input.blur();
            
            if (Render.isConchApp) {
                input.setPos(-10000, -10000);
            } else if (inputContainer.contains(input))
                inputContainer.removeChild(input);
        }
    }
}

默認(rèn)情況下,在點(diǎn)擊聊天發(fā)送按鈕后座柱,輸入框input會(huì)失去焦點(diǎn)迷帜,導(dǎo)致軟鍵盤(pán)收起。這時(shí)繼續(xù)輸入色洞,就要重新點(diǎn)擊輸入框input戏锹,是不是很煩。但是想更改這個(gè)默認(rèn)設(shè)定锋玲,也只能改源碼了……

if ((new_input instanceof laya.display.Input )&& new_input.multiline==pre_input.multiline)
    pre_input['_focusOut']();
else if((new_input instanceof laya.ui.Button )&& new_input.name == "sendBtn"){
    //聊天點(diǎn)擊發(fā)送按鈕(name==sendBtn)景用,輸入框不會(huì)失去焦點(diǎn)
}else{
    pre_input.focus=false;
}
八、沒(méi)有MOUSE_UP事件

在輸入框輸入文本時(shí)惭蹂,直接點(diǎn)擊發(fā)送按鈕伞插,在部分瀏覽器上會(huì)出現(xiàn)只是縮回鍵盤(pán),文本并未發(fā)出的情況盾碗。經(jīng)過(guò)檢查媚污,是沒(méi)有拋出MOUSE_UP事件,進(jìn)而導(dǎo)致沒(méi)有CLICK事件廷雅。將發(fā)送按鈕改為偵聽(tīng)MOUSE_DOWN事件即可耗美。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市航缀,隨后出現(xiàn)的幾起案子商架,更是在濱河造成了極大的恐慌,老刑警劉巖芥玉,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛇摸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡灿巧,警方通過(guò)查閱死者的電腦和手機(jī)赶袄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抠藕,“玉大人饿肺,你說(shuō)我怎么就攤上這事《芩疲” “怎么了敬辣?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我购岗,道長(zhǎng)汰聋,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任喊积,我火速辦了婚禮,結(jié)果婚禮上玄妈,老公的妹妹穿的比我還像新娘乾吻。我一直安慰自己,他們只是感情好拟蜻,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布绎签。 她就那樣靜靜地躺著,像睡著了一般酝锅。 火紅的嫁衣襯著肌膚如雪诡必。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天搔扁,我揣著相機(jī)與錄音爸舒,去河邊找鬼。 笑死稿蹲,一個(gè)胖子當(dāng)著我的面吹牛扭勉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苛聘,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涂炎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了设哗?” 一聲冷哼從身側(cè)響起唱捣,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎网梢,沒(méi)想到半個(gè)月后震缭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澎粟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年蛀序,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片活烙。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徐裸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出啸盏,到底是詐尸還是另有隱情重贺,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站气笙,受9級(jí)特大地震影響次企,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜潜圃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一缸棵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谭期,春花似錦堵第、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至胀瞪,卻和暖如春针余,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凄诞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工圆雁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幔摸。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓摸柄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親既忆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驱负,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 問(wèn)答題47 /72 常見(jiàn)瀏覽器兼容性問(wèn)題與解決方案? 參考答案 (1)瀏覽器兼容問(wèn)題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,728評(píng)論 1 92
  • 一患雇、《保質(zhì)期》 任何東西都有保質(zhì)期 比如剛剛做好的冰淇淋 比如你和我之間的感情 二跃脊、《青蛙王子》 像最后一次分別前...
    不敢說(shuō)愛(ài)你閱讀 246評(píng)論 0 2
  • 午加餐:餅干干晚水果:香蕉 參考目標(biāo): 1份豆2份肉3份“新鮮”水果4份谷物/薯5份蔬菜,深綠色葉菜最好6杯水 今...
    靜趣_兒童心理師閱讀 262評(píng)論 0 0
  • 約不可失 魏文侯與虞人期獵苛吱。是日酪术,飲酒樂(lè),天雨翠储。文侯將出绘雁,左右曰:“今日飲酒樂(lè),天又雨援所,公將焉之庐舟?”文侯曰:“吾與...
    學(xué)霸愛(ài)學(xué)習(xí)閱讀 301評(píng)論 0 0