一. cocos creator 提供的几种适配策略
EXACT_FIT:
整个应用程序在指定区域可见,无需尝试保留原始纵横比。可能会出现失真,应用程序会被拉伸或压缩。也就是说设计分辨率的长和宽不会等比缩放,它们会各自进行缩放,设计分辨率的宽会用 “屏幕宽/设计分辨率宽” 作为缩放因子,设计分辨率的高会用 “屏幕高/设计分辨率高” 作为缩放因子。(2.0版本这个策略有问题,等修复)
NO_BORDER:
整个应用程序填充指定区域,没有失真,但可能会有一些裁剪,同时保持原样应用程序的纵横比。这个是等比进行缩放设计分辨率,取 “屏幕宽/设计分辨率宽” “屏幕高/设计分辨率高” 中较大的一个作为缩放因子,比如:“屏幕宽/设计分辨率宽”是2,“屏幕高/设计分辨率高”是1,那么取2作为缩放因子,这个时候高放大两倍,自然超出屏幕之外看不见了。
SHOW_ALL:
整个应用程序在指定区域可见而不失真,同时保持原样应用程序的纵横比。边界可以出现在应用程序的两侧。这个是等比进行缩放设计分辨率,取 “屏幕宽/设计分辨率宽” “屏幕高/设计分辨率高” 中较小的一个作为缩放因子,比如上述的那个例子,取1作为缩放因子,宽只放大一倍,明显不够,虽然整个应用程序都能看到,但会有黑边。
FIXED_HEIGHT:
应用程序采用设计分辨率大小的高度并修改内部的宽度画布使其适合设备的纵横比,不会发生失真。这个是等比进行缩放设计分辨率,保持设计分辨率的高度不变,根据屏幕分辨率改变设计分辨率的宽度,这个时候设计分辨率和屏幕分辨率一样了,再进行等比缩放。
FIXED_WIDTH:
这个和 FIXED_HEIGHT 类似,唯一不同的是它是保持的宽度不变。
二. cocos提供的几个获取View的函数
cc.view.getDesignResolutionSize()
获取的是你在编辑器中设计的分辨率,也就是canvas 组件下设置的设计分辨率。
cc.view.getFrameSize()
获取各种手机、pad上的屏幕分辨率,也就是硬件分辨率。
cc.view.getVisibleSizeInPixel()
获取的是 visibleSize 的基础上乘以各种适配策略下的缩放比例后的分辨率。
cc.view.getVisibleSize()
官方文档上说返回视图窗口可见区域尺寸,经过输出对比发现,这个可见区域尺寸实际上是指设计分辨率,不过它返回的是经过各种适配策略后的设计分辨率,在EXACT_FIT,NO_BORDER,SHOW_ALL中因为不改变设计分辨率,所以和cc.view.getDesignResolutionSize()返回的结果一样。但在FIXED_HEIGHT,FIXED_WIDTH中改变了设计分辨率,所以返回的是修改后的设计分辨率。
后续通过查看creator源码也证实了这一点,以下是源码解释:
1 /** 2 * !#en 3 * Returns the visible area size of the view port. 4 * !#zh 返回视图窗口可见区域尺寸。 5 * @method getVisibleSize 6 * @return {Size} 7 */ 8 getVisibleSize: function () { 9 return cc.size(this._visibleRect.width,this._visibleRect.height); 10 },
getVisibleSize()返回的是this._visbleRect的值,我们来找找this._visbleRect在哪进行赋值
1 cc.js.mixin(View.prototype, { 2 init () { 3 ......... 4 5 var w = cc.game.canvas.width, h = cc.game.canvas.height; 6 this._designResolutionSize.width = w; 7 this._designResolutionSize.height = h; 8 this._originalDesignResolutionSize.width = w; 9 this._originalDesignResolutionSize.height = h; 10 this._viewportRect.width = w; 11 this._viewportRect.height = h; 12 this._visibleRect.width = w; 13 this._visibleRect.height = h; 14 15 cc.winSize.width = this._visibleRect.width; 16 cc.winSize.height = this._visibleRect.height; 17 ...... 18 }, 19 ........ 20 }
可以看到在这里给一些保持分辨率的对象赋了同一个值,那就是cc.game.canvas里面的值,这个值代表的是屏幕分辨率,因为后续进行适配策略是用的也是这个值,这就奇怪了,都一样了还怎么比较,每个函数返回的值也应该一样呀,所以一定有一个地方改变了它们,继续找。
1 setDesignResolutionSize: function (width, height, resolutionPolicy) { 2 ......... 8 this.setResolutionPolicy(resolutionPolicy); 9 var policy = this._resolutionPolicy; 10 if (policy) { 11 policy.preApply(this); 12 } 13 14 ..........28 29 this._originalDesignResolutionSize.width = this._designResolutionSize.width = width; 30 this._originalDesignResolutionSize.height = this._designResolutionSize.height = height; 31 32 var result = policy.apply(this, this._designResolutionSize); 33 34 if(result.scale && result.scale.length === 2){ 35 this._scaleX = result.scale[0]; 36 this._scaleY = result.scale[1]; 37 } 38 39 if(result.viewport){ 40 var vp = this._viewportRect, 41 vb = this._visibleRect, 42 rv = result.viewport; 43 44 vp.x = rv.x; 45 vp.y = rv.y; 46 vp.width = rv.width; 47 vp.height = rv.height; 48 49 vb.x = 0; 50 vb.y = 0; 51 vb.width = rv.width / this._scaleX; 52 vb.height = rv.height / this._scaleY; 53 } 54 55 policy.postApply(this); 56 cc.winSize.width = this._visibleRect.width; 57 cc.winSize.height = this._visibleRect.height; 58 ........64 },
我们直接来看setDesignResolutionSize()这个函数,它是通过设置设计分辨率和匹配模式来进行游戏画面的屏幕适配。所以它必然会改变相应的值。可以看到第29,30行把我们传进来的设计分辨率赋值给了this._originalDesignResolutionSize和this._designResolutionSize,改变了它们的值。那么什么时候改变this._visbleRect的值呢,我们看39行到52行,很明显就是在这里改变的。vb.width = rv.width / this._scaleX; vb.height = rv.height / this._scaleY;
那么凭什么说this._visbleRect是适配策略后到设计分辨率呢,我们来看看result.viewport里面是什么东西,var result = policy.apply(this, this._designResolutionSize);policy是一个适配策略的实例,它根据你传入的适配策略来决定调用哪个适配策略的方法,类似于c++中的多态。然后我们来看看每个适配策略做了什么,这里主要就拿SHOW_ALL和FIXED_HEIGHT来加以说明:
1 var ShowAll = cc.Class({ 2 name: "ShowAll", 3 extends: cc.ContentStrategy, 4 apply: function (view, designedResolution) { 5 var containerW = cc.game.canvas.width, containerH = cc.game.canvas.height, 6 designW = designedResolution.width, designH = designedResolution.height, 7 scaleX = containerW / designW, scaleY = containerH / designH, scale = 0, 8 contentW, contentH; 9 10 scaleX < scaleY ? (scale = scaleX, contentW = containerW, contentH = designH * scale) 11 : (scale = scaleY, contentW = designW * scale, contentH = containerH); 12 13 return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); 14 } 15 }); 16 17 var FixedHeight = cc.Class({ 18 name: "FixedHeight", 19 extends: cc.ContentStrategy, 20 apply: function (view, designedResolution) { 21 var containerW = cc.game.canvas.width, containerH = cc.game.canvas.height, 22 designH = designedResolution.height, scale = containerH / designH, 23 contentW = containerW, contentH = containerH; 24 25 return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); 26 } 27 });
可以看到它们都继承于cc.ContentStrategy。在apply函数中返回值来自于this._buildResult()函数,我们来看这个函数:
1 _buildResult: function (containerW, containerH, contentW, contentH, scaleX, scaleY) { 2 // Makes content fit better the canvas 3 Math.abs(containerW - contentW) < 2 && (contentW = containerW); 4 Math.abs(containerH - contentH) < 2 && (contentH = containerH); 5 6 var viewport = cc.rect((containerW - contentW) / 2, (containerH - contentH) / 2, contentW, contentH); 7 8 // Translate the content 9 if (cc.game.renderType === cc.game.RENDER_TYPE_CANVAS){ 10 //TODO: modify something for setTransform 11 //cc.game._renderContext.translate(viewport.x, viewport.y + contentH); 12 } 13 14 this._result.scale = [scaleX, scaleY]; 15 this._result.viewport = viewport; 16 return this._result; 17 },
主要来看this._result中的viewport的值,它的width是contentW,height是contentH。到这里,一切都清楚了。我们假设屏幕分辨率的宽高是Pw和Ph,设计分辨率的宽高是Sw和Sh。
那么在SHOW_ALL中:假设Pw/Sw更小,contentW=Pw,contentH=Sh*(Pw/Sw),在进行this._visbleRect赋值时,width = Pw/(Pw/Sw) = Sw,height = (Sh*(Pw/Sw))/(Pw/Sw) = Sh,所以设计分辨率没有改变。
在FIXED_HEIGHT中:contentW=Pw,contentH=Ph,scale = Ph/Sh,在进行this._visbleRect赋值时,width = Pw/(Ph/Sh) = Sh*(Pw/Ph),height = Ph/(Ph/Sh) = Sh,所以设计分辨率是改变的。
从上述代码还发现cc.winSize和cc.view.getVisibleSize()的返回值是一样的,二者可以通用。
在进行屏幕适配时主要用到cc.view.getDesignResolutionSize()和cc.view.getFrameSize()两个函数。