日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[原创]egret的WebView实现(基于egret2.5)

發布時間:2023/12/16 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [原创]egret的WebView实现(基于egret2.5) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

egret的WebView實現(基于egret2.5)

標簽(空格分隔): egret webview


客戶端開發中有一種很常見的場景就是展現web頁面,也就是 WebView 。例如 IOS 、 Android ,都為開發者提供了 WebView 組件,而在 egret 中暫時并沒有提供 WebView 組件,所以我們只能自己動手實現一個能在 egret 引擎中可用的 WebView 組件。

由于egret運行在瀏覽器中,所以想要實現一個能展現web頁面的組件,第一想法應該是使用iframe。
那么這些問題需要解決:

  • 與 egret 集成,并提供與 egret 組件一致的調用方式
  • 融入 egret 的展現,即 WebView 的坐標、大小需要與 egret 一致

查看 egret 源碼,我們不難發現 egret 的 HtmlInput 組件也是使用上面我們所考慮的方式實現在游戲中進行文本輸入的。所以,我們可以采用 egret HtmlInput 類似的方式實現 WebView 。因此,我們首先從 egret HtmlInuput 源碼入手:

1. Egret中HtmlInput的實現

1.1 HtmlInput實現方式:

在 egret.web.js 中可以很找到 egret.web.HTMLInput 的初始化相關的源碼:

HtmlInput.prototype._initStageDelegateDiv = function (container, canvas) {this.canvas = canvas;var self = this;var stageDelegateDiv;if (!stageDelegateDiv) {stageDelegateDiv = document.createElement("div");this.StageDelegateDiv = stageDelegateDiv;stageDelegateDiv.id = "StageDelegateDiv";container.appendChild(stageDelegateDiv);self.initValue(stageDelegateDiv);self._inputDIV = document.createElement("div");self.initValue(self._inputDIV);self._inputDIV.style.width = "0px";self._inputDIV.style.height = "0px";self._inputDIV.style.left = 0 + "px";self._inputDIV.style.top = "-100px";self._inputDIV.style[egret.web.getPrefixStyleName("transformOrigin")] = "0% 0% 0px";stageDelegateDiv.appendChild(self._inputDIV);this.canvas.addEventListener("click", function (e) {if (self._needShow) {self._needShow = false;self._stageText._onClickHandler(e);self.show();}else {if (self._inputElement) {self.clearInputElement();self._inputElement.blur();self._inputElement = null;}}});self.initInputElement(true);self.initInputElement(false);}}; HtmlInput.prototype.initInputElement = function (multiline) {var self = this;//增加1個空的textareavar inputElement;if (multiline) {inputElement = document.createElement("textarea");inputElement.style["resize"] = "none";self._multiElement = inputElement;inputElement.id = "egretTextarea";}else {inputElement = document.createElement("input");self._simpleElement = inputElement;inputElement.id = "egretInput";}inputElement.type = "text";self._inputDIV.appendChild(inputElement);inputElement.setAttribute("tabindex", "-1");inputElement.style.width = "1px";inputElement.style.height = "12px";self.initValue(inputElement);inputElement.style.outline = "thin";inputElement.style.background = "none";inputElement.style.overflow = "hidden";inputElement.style.wordBreak = "break-all";//隱藏輸入框inputElement.style.opacity = 0;inputElement.oninput = function () {if (self._stageText) {self._stageText._onInput();}};};

很明顯,是在id為 StageDelegateDiv 的 dom 中嵌入 <input type=”text” > 和 <textarea> ,并在這兩個 html 元素上做文章。

1.2 HtmlInput 渲染展現:

那么 text 輸入框和 textarea 文本輸入域是如何正確展現到 egret 的 canvas 上的呢?
在 egret.web.js 的 egret.web.HTML5StageText 中我們看到如下代碼:

p.$setTextField = function (textfield) {this.$textfield = textfield;return true;};/*** @private**/p.$addToStage = function () {this.htmlInput = egret.web.$getTextAdapter(this.$textfield);};/*** @private**/p._initElement = function () {var point = this.$textfield.localToGlobal(0, 0);var x = point.x;var y = point.y;var cX = this.$textfield.$renderMatrix.a;var cY = this.$textfield.$renderMatrix.d;var scaleX = this.htmlInput.$scaleX;var scaleY = this.htmlInput.$scaleY;this.inputDiv.style.left = x * scaleX + "px";this.inputDiv.style.top = y * scaleY + "px";if (this.$textfield.multiline) {this.inputDiv.style.top = (y) * scaleY + "px";this.inputElement.style.top = (-this.$textfield.lineSpacing / 2) + "px";}else {this.inputDiv.style.top = y * scaleY + "px";this.inputElement.style.top = 0 + "px";}this._gscaleX = scaleX * cX;this._gscaleY = scaleY * cY;};

從這幾段關鍵代碼中我們可以看到, HtmlInput 的文本使用 egret.TextFiled 組件進行渲染的,并且 HtmlInput 對應的 Dom 位置、寬高直接使用 TextFiled 的位置、寬高,這樣 HtmlInputDom 就能與 egret 的坐標寬高一致了。

1.3 HtmlInput文本輸入:

那么是怎么實現輸入的呢?
在 egret.web.js 的 egret.web.HTML5StageText 的源碼中能找到如下代碼:

HTML5StageText.prototype.executeShow = function () {var self = this;//打開this.inputElement.value = this.$getText();if (this.inputElement.onblur == null) {this.inputElement.onblur = this.onBlurHandler.bind(this);}this.$resetStageText();if (this.$textfield.maxChars > 0) {this.inputElement.setAttribute("maxlength", this.$textfield.maxChars);}else {this.inputElement.removeAttribute("maxlength");}this.inputElement.selectionStart = this.inputElement.value.length;this.inputElement.selectionEnd = this.inputElement.value.length;this.inputElement.focus();};

從最后一行代碼可以看到,直接調用 HtmlInput 對應的 dom 的 onfocus() ,從而彈出輸入框,即可進行文本輸入操作。


2. WebView的實現

看完 HtmlInput 的實現,可見 WebView 的實現有所不同:
HtmlInput 依賴 egret.TextField 來進行渲染展現,egret.TextField 是 egret 中內建的組件,所以 HtmlInput 坐標寬高直接使用 textfield 的坐標、寬高就可以了。但是 WebView 依賴的是 iframe ,是 html 的標準組件,并不是 egret 的內建組件,而且在 egret 的 canvas 之外,所以必然會涉及到 webview 組件的坐標寬高的換算。結合上文提到的 WebView 應該達到的效果,我們可以考慮如下實 現WebView :

  • WebView 繼承degret.displayObjectContailner,override相關方法(getX,getY,setX,setY,getWidht,getHeight,setWidth,setHeight等)以提供與egret引擎中內建組件一致的調用方式:
class WebView extends egret.DisplayObjectContainer {private _x:number=0;private _y:number=0;private _width:number=0;private _height:number=0;private _src:string="";//getter setter ...}
  • 與 HtmlInput 一樣,在 StageDelegatDiv 中 加入 iframe, WebView 對象持有此 iframe dom 的引用,但是必須根據其 x,y,width,height 進行相應的坐標、大小的轉化并進行顯示:

要實現以上想法,以下需要處理:

(1) 了解egret的scalemode( 縮放模式)
(2) 根據各種scalemode的實現方式換算webview的x,y,width,height
(3) 控制WebView中iframe的樣式及顯示時機

2.1 egret 的 scalemode (縮放模式)

egret的六種 scalemode :

StageScaleMode.NO_SCALE = "noScale"; StageScaleMode.SHOW_ALL = "showAll"; StageScaleMode.NO_BORDER = "noBorder"; StageScaleMode.EXACT_FIT = "exactFit"; StageScaleMode.FIXED_WIDTH = "fixedWidth"; StageScaleMode.FIXED_HEIGHT = "fixedHeight";
  • StageScaleMode.NO_SCALE:
    不縮放應用程序內容。即使在更改播放器視口大小時,它仍然保持不變。如果播放器視口比內容小,則可能進行一些裁切。 在此模式下,舞臺尺寸(Stage.stageWidth,Stage.stageHeight)始終跟播放器視口大小保持一致。

  • StageScaleMode.SHOW_ALL:
    保持原始寬高比縮放應用程序內容,縮放后應用程序內容的較寬方向填滿播放器視口,另一個方向的兩側可能會不夠寬而留有黑邊。在此模式下,舞臺尺寸(Stage.stageWidth,Stage.stageHeight)始終等于初始化時外部傳入的應用程序內容尺寸。

  • StageScaleMode.NO_BORDER:
    保持原始寬高比縮放應用程序內容,縮放后應用程序內容的較窄方向填滿播放器視口,另一個方向的兩側可能會超出播放器視口而被裁切在此模式下,舞臺尺寸(Stage.stageWidth,Stage.stageHeight)始終等于初始化時外部傳入的應用程序內容尺寸。

  • StageScaleMode.EXACT_FIT:
    不保持原始寬高比縮放應用程序內容,縮放后應用程序內容正好填滿播放器視口。在此模式下,舞臺尺寸(Stage.stageWidth,Stage.stageHeight)始終等于初始化時外部傳入的應用程序內容尺寸。

  • StageScaleMode.FIXED_WIDTH :
    保持原始寬高比縮放應用程序內容,縮放后應用程序內容在水平和垂直方向都填滿播放器視口,但只保持應用程序內容的原始高度不變,寬度可能會改變。在此模式下,舞臺高度(Stage.stageHeight)始終等于初始化時外部傳入的應用程序內容高度。舞臺寬度(Stage.stageWidth)由當前的縮放比例與播放器視口寬度決定。

  • StageScaleMode.FIXED_HEIGHT :
    保持原始寬高比縮放應用程序內容,縮放后應用程序內容在水平和垂直方向都填滿播放器視口,但只保持應用程序內容的原始高度不變,寬度可能會改變。在此模式下,舞臺高度(Stage.stageHeight)始終等于初始化時外部傳入的應用程序內容高度。舞臺寬度(Stage.stageWidth)由當前的縮放比例與播放器視口寬度決定。

引用官方的一張圖:

egret官方文章的介紹: egret scalemode ,有很詳細的講解。

可見,不同的縮放模式對于舞臺大小、顯示寬高的大小有決定性的影響。我們僅考慮SHOW_ALL,NO_BORDER,FIXED_WIDTH,FIXED_HEIGHT這幾種在實際生產中用得比較多的縮放模式。
各種縮放模式下是如何計算舞臺寬高、顯示寬高的呢?
在egret.js的源碼中,我們可以找到如下代碼片段:

/*** @private* 計算舞臺顯示尺寸* @param scaleMode 當前的縮放模式* @param screenWidth 播放器視口寬度* @param screenHeight 播放器視口高度* @param contentWidth 初始化內容寬度* @param contentHeight 初始化內容高度*/ScreenAdapter.prototype.calculateStageSize = function (scaleMode, screenWidth, screenHeight, contentWidth, contentHeight) {var displayWidth = screenWidth;var displayHeight = screenHeight;var stageWidth = contentWidth;var stageHeight = contentHeight;var scaleX = (screenWidth / stageWidth) || 0;var scaleY = (screenHeight / stageHeight) || 0;switch (scaleMode) {case egret.StageScaleMode.EXACT_FIT:break;case egret.StageScaleMode.FIXED_HEIGHT:stageWidth = Math.round(screenWidth / scaleY);break;case egret.StageScaleMode.FIXED_WIDTH:stageHeight = Math.round(screenHeight / scaleX);break;case egret.StageScaleMode.NO_BORDER:if (scaleX > scaleY) {displayHeight = Math.round(stageHeight * scaleX);}else {displayWidth = Math.round(stageWidth * scaleY);}break;case egret.StageScaleMode.SHOW_ALL:if (scaleX > scaleY) {displayWidth = Math.round(stageWidth * scaleY);}else {displayHeight = Math.round(stageHeight * scaleX);}break;default:stageWidth = screenWidth;stageHeight = screenHeight;break;}return {stageWidth: stageWidth,stageHeight: stageHeight,displayWidth: displayWidth,displayHeight: displayHeight};

2.2 根據各種 scalemode 的實現方式換算 webview 的 x,y,width,height

我們可以清楚看到各種縮放模式下,舞臺寬高、顯示寬高的計算,因此我們可以根據相應的縮放模式來換算WebView的x,y,widht,height對應在web窗口中的具體數值,這里以WebView.setwidth(w:number)為例:

public set width(value:number) {this._width = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ){this._iframe.width=this._width/this._stageW*this._windowW+"px";this._iframeWrapper.style.width=this._width/this._stageW*this._windowW+"px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowW==this._displayW){this._iframe.style.width = this._width / this._stageW * this._windowW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._windowW + "px";}else{this._iframe.style.width = this._width / this._stageW * this._displayW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._displayW + "px";}}}

setX,setY,setHeight等方法坐標換算與上述方式相同。
通過坐標換算,我們就能以egret組件一樣的方式調用WebView并且展現在正確的位置。

2.3 控制 WebView 中 iframe 的樣式及顯示時機

坐標寬高都確定了,就可以設置 iframe 的樣式了,這個比較簡單,通過js即可設置樣式。另外,iframe在網頁完全加載完再顯示會比較友好。

3. IOS 系統中 iframe 寬高撐大、無法滑動的解決方案

測試如上方法開發的 WebView 時,發現在 ios 中, iframe 寬高會被里面的內容撐大、并且無法滑動:

通過google,找到了解決方案:
http://stackoverflow.com/questions/23083462/how-to-get-an-iframe-to-be-responsive-in-ios-safari
或者
http://davidwalsh.name/scroll-iframes-ios

即:
給iframe一個父級div,設置如下css屬性:

#iframe-wrapper {-webkit-overflow-scrolling: touch;overflow-y: scroll;/* important: dimensions or positioning here! */ }#iframe-wrapper iframe {/* nada! */ } private _iframeWrapper:HTMLDivElement=null;private _iframe:HTMLIFrameElement=null;/*** @param src*/public constructor(src:string){super();var stageDelegateDom:HTMLElement=document.getElementById("StageDelegateDiv"),playerContainer:HTMLElement=stageDelegateDom.parentElement;var iframeWrapperDom=document.getElementById("iframe-wrapper");if(!iframeWrapperDom){iframeWrapperDom=document.createElement("div");iframeWrapperDom.style.display="none";iframeWrapperDom.attributes['style'].value+='position:absolute;-webkit-overflow-scrolling: touch;overflow-y: scroll;';//解決iframe在ios下的顯示問題iframeWrapperDom.id="iframe-wrapper";stageDelegateDom.appendChild(iframeWrapperDom);}this._iframeWrapper=<HTMLDivElement>iframeWrapperDom;this._iframeWrapper.style.display="none";this._iframeWrapper.style.opacity="0";var iframe = document.createElement("iframe"),t=new Date().getTime();iframe.src=src;iframe.id="webview-iframe-"+t;iframe.name="webview-iframe-"+t;iframe.style.position="absolute";iframe.style.top="0";iframe.style.left="0";iframe.style.opacity="0";iframe.style.display='none';iframe.frameBorder='0';iframe.border="0";this._iframeWrapper.appendChild(iframe);this._iframe=<HTMLIFrameElement>document.getElementById("webview-iframe-"+t);var self=this;this._iframe.onload=function(){self._iframeWrapper.style.opacity="1";self._iframe.style.opacity="1";}}

4. WebView.ts完整代碼及使用方式:

WebView.ts:

/*** WebView* 適配FIXED_WIDTH、FIXED_HEIGHT、NO_BORDER、SHOW_ALL四種縮放模式* 暫未考慮屏幕大小改變、屏幕旋轉以及單頁面多Webplay實例的情形* Created by yxiao on 2015/9/30.*/ class WebView extends egret.DisplayObjectContainer {private _x:number=0;private _y:number=0;private _width:number=0;private _height:number=0;private _src:string="";private _scaleMode:string=egret.MainContext.instance.stage.scaleMode;private _stageW:number;private _stageH:number;private _windowW:number;private _windowH:number;private _displayH:number;private _displayW:number;private _designH:number;private _designW:number;private _iframeWrapper:HTMLDivElement=null;private _iframe:HTMLIFrameElement=null;/*** @param src*/public constructor(src:string){super();var stageDelegateDom:HTMLElement=document.getElementById("StageDelegateDiv"),playerContainer:HTMLElement=stageDelegateDom.parentElement;var iframeWrapperDom=document.getElementById("iframe-wrapper");if(!iframeWrapperDom){iframeWrapperDom=document.createElement("div");iframeWrapperDom.style.display="none";iframeWrapperDom.attributes['style'].value+='position:absolute;-webkit-overflow-scrolling: touch;overflow-y: scroll;';//解決iframe在ios下的顯示問題iframeWrapperDom.id="iframe-wrapper";stageDelegateDom.appendChild(iframeWrapperDom);}this._iframeWrapper=<HTMLDivElement>iframeWrapperDom;this._iframeWrapper.style.display="none";this._iframeWrapper.style.opacity="0";var iframe = document.createElement("iframe"),t=new Date().getTime();iframe.src=src;iframe.id="webview-iframe-"+t;iframe.name="webview-iframe-"+t;iframe.style.position="absolute";iframe.style.top="0";iframe.style.left="0";iframe.style.opacity="0";iframe.style.display='none';iframe.frameBorder='0';iframe.border="0";this._iframeWrapper.appendChild(iframe);this._iframe=<HTMLIFrameElement>document.getElementById("webview-iframe-"+t);var self=this;this._iframe.onload=function(){self._iframeWrapper.style.opacity="1";self._iframe.style.opacity="1";}this._stageW=egret.MainContext.instance.stage.stageWidth;this._stageH=egret.MainContext.instance.stage.stageHeight;this._windowW=window.innerWidth;this._windowH=window.innerHeight;this._designH=parseInt(playerContainer.attributes['data-content-height'].value);this._designW=parseInt(playerContainer.attributes['data-content-width'].value);var stageSize = egret.sys.screenAdapter.calculateStageSize(egret.MainContext.instance.stage.scaleMode, this._windowW, this._windowH, this._designW, this._designH);this._displayH=stageSize.displayHeight;this._displayW=stageSize.displayWidth;console.log("windowW:"+this._windowW);console.log("stageW:"+this._stageW);console.log("disPlayW:"+this._displayW);console.log("windowH:"+this._windowH);console.log("stageH:"+this._stageH);console.log("displayH:"+this._displayH);}public show():void {this._iframe.style.display='block';this._iframeWrapper.style.display='block';}public destroy():void {if(this._iframe){this._iframeWrapper.style.display="none";this._iframeWrapper.removeChild(this._iframe);}}public get width():number {return this._width;}public set width(value:number) {this._width = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ){this._iframe.width=this._width/this._stageW*this._windowW+"px";this._iframeWrapper.style.width=this._width/this._stageW*this._windowW+"px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowW==this._displayW){this._iframe.style.width = this._width / this._stageW * this._windowW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._windowW + "px";}else{this._iframe.style.width = this._width / this._stageW * this._displayW + "px";this._iframeWrapper.style.width = this._width / this._stageW * this._displayW + "px";}}}public get height():number {return this._height;}public set height(value:number) {this._height = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ) {this._iframe.height=this._height/this._stageH*this._windowH+"px";this._iframeWrapper.style.height=this._height/this._stageH*this._windowH+"px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowH==this._displayH){this._iframe.style.height = this._height / this._stageH * this._windowH + "px";this._iframeWrapper.style.height = this._height / this._stageH * this._windowH + "px";}else{this._iframe.style.height = this._height / this._stageH * this._displayH + "px";this._iframeWrapper.style.height = this._height / this._stageH * this._displayH + "px";}}}public set x(value:number) {this._x = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT) {this._iframeWrapper.style.left = this._x / this._stageW * this._windowW + "px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER ) {if(this._windowW==this._displayW){this._iframeWrapper.style.left = this._x / this._stageW * this._windowW + "px";}else{this._iframeWrapper.style.left = this._x / this._stageW * this._displayW + "px";}}}public set y(value:number) {this._y = value;if(this._scaleMode==egret.StageScaleMode.FIXED_WIDTH || this._scaleMode==egret.StageScaleMode.FIXED_HEIGHT ) {this._iframeWrapper.style.top = this._y / this._stageH * this._windowH + "px";}if(this._scaleMode==egret.StageScaleMode.SHOW_ALL || this._scaleMode==egret.StageScaleMode.NO_BORDER){if(this._windowH==this._displayH){this._iframeWrapper.style.top = this._y / this._stageH * this._windowH + "px";}else{this._iframeWrapper.style.top =this._y / this._stageH * this._displayH + "px";}}}public get x():number {return this._x;}public get y():number {return this._y;}public get src():string {return this._src;}public set src(value:string) {this._src = value;} }

使用方式:

var webview=new WebView("http://www.sina.com"); webview.x=100; webview.y=100; webview.width = 500; webview.height = 800; webview.show();

效果:

5. 后續

考慮屏幕大小改變、屏幕旋轉以及單頁面多Webplay實例的情形

總結

以上是生活随笔為你收集整理的[原创]egret的WebView实现(基于egret2.5)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。