本文作者:IMWeb vienwu 原文出处:IMWeb社区 未经同意,禁止转载
使用cocos2d-js版开发跨平台手游非常简单,并且在手机端也拥有不错的性能。但因为推出时间并不够久,用户也不够多,项目里仍然存在不少bug,这里介绍一个常见的bug和个人解决方案。
大段中文文字无法自动换行并且在不同终端行为不一致的bug修复
这个bug具体表现为,js版的cc.LabelBMFont类实现存在缺陷。 该类中,判断是否自动换行时,首先检测字符是否结束或者是否存在空格,满足条件后才会换行。 当字符串为英文时,此逻辑可以良好执行,但面对中文时就不能正常处理了。 理论上,在cocos2d-html5/cocos2d/labels/cclabelbmfont.js
大约736行if (!self._lineBreakWithoutSpaces) {
应该判断是否为中文,或者在后续的寻找空格逻辑中,增加寻找中文的判断。
其次,cocos2d-js在手机端执行时,会将js代码编译为jsb字节码,调用的cc.LabelBMFont类是c 实现的,并且该类实现的算法和web端的实现不同,导致字体大小、换行行为不一致,尤其在单独控制某个文本字符颜色时,定位某个文本的索引都会不同。
另,cocos2d自身不支持单独设置一段文本中某个字符的颜色,如需要实现该效果,只能使用cc.LabelBMFont类(不支持cc.LabelTTF),再单独查找到对应的cc.Sprite并设置颜色。所以这里我重写了一个类单独处理文本,支持使用类似ubb的方式单独设置某部分文本的颜色,例如:
代码语言:javascript复制var text = '这是一段测试文本,[color=#ff0000]这里是红色[/color],[color=green]这里是绿色[/color]';
var label = new game.Label(text,24,'#ffffff',100,50);
以上代码即可实现默认颜色为#ffffff
即白色,这里是红色
会显示为红色,这里是绿色
会显示为绿色,字体大小24px,最大宽度100px,超过即会换行,最大高度为50px,超过时会自动添加一个滚动条。
以下为实现代码:(使用coffeescript编写,可使用相应工具转换为js代码)
代码语言:javascript复制注:cc.LabelBMFont使用了fnt字体文件,需要自行生成对应的fnt文件和png资源。
代码语言:javascript复制# 重新实现label类,by ying
defaultSize = 32
game.Label = cc.Sprite.extend
_str:''
ctor:(str,fontSize=24,color='#ffffff',width=0,height=0)->
this._super()
this._str = String(str) or ''
this._color = color or '#ffffff'
this._fontSize=fontSize
this._contentWidth = width
this._contentHeight = height
this.anchorY=1
return if not str
this.updateString()
updateString:->
locStr = String(this._str)
_str = locStr.replace /[color=#w*]/gi,''
_str = _str.replace /[/color]/gi,''
color = this._color
typeof color is 'string' and color = cc.color color
colorList = [color]
this.removeAllChildren()
if cc.sys.isNative
label=new cc.LabelBMFont _str,game.res.myfont,Math.floor(this._contentWidth*defaultSize/this._fontSize)
else
label=new cc.LabelBMFont _str,game.res.myfont,this._contentWidth
label.setLineBreakWithoutSpace true
label.setScale this._fontSize/defaultSize
if cc.sys.isNative
label.attr anchorY:0,anchorX:0,color:color
else
label.attr anchorY:0,anchorX:0,color:cc.color '#ffffff'
maxWidth=label.width*label.getScaleX()
maxHeight=label.height*label.getScaleY()
len=locStr.length or 0
tag=skip=i=0
if this._contentWidth>0 and this._contentHeight>0
size = cc.size this._contentWidth,this._contentHeight
labelBgSprite=new cc.Sprite()
labelBgSprite.attr anchorX:0,anchorY:0,width:size.width,height:maxHeight
labelBgSprite.addChild label
scroll=new cc.ScrollView size,labelBgSprite
scroll.attr direction:cc.SCROLLVIEW_DIRECTION_VERTICAL
scroll.setContentOffset x:0,y:scroll.minContainerOffset().y
this.addChild scroll
this.width = this._contentWidth
this.height = this._contentHeight
this.scroll = scroll
else
this.addChild label
this.width=maxWidth
this.height=maxHeight
if locStr.indexOf('[color=') isnt -1 or not cc.sys.isNative
while i<len
char=locStr[i]
key=char.charCodeAt 0
if char is '[' and locStr.substr(i,7) is '[color='
colorList.push cc.color(locStr.substr(i 7,7))
i=i 15
continue
if char is '[' and locStr.substr(i,8) is '[/color]'
colorList.pop()
i=i 8
continue
if key is 10
i
skip
continue
while true
break if tag skip>len
sprite=label.getChildByTag tag skip
if not sprite
skip
else
if cc.sys.isNative
nextSprite=label.getChildByTag tag skip 1
break if not nextSprite
break if nextSprite.getPositionX()>=sprite.getPositionX()
skip
else
break if sprite.visible
skip
sprite.color = colorList[colorList.length-1] if sprite
i
tag
# cc.log 'hello',this.width,this.height,this.anchorX,this.anchorY
setString:(str)->
this._str = String(str) or ''
this.updateString()
game.LabelTTF = cc.Sprite.extend
_str:'' # 文本
_fontSize:24 # 文字大小
_color:'#ffffff' # 文本颜色
_charSprites:null
_space:0 # 水平间距
_lineHeight:2 # 行距
_contentWidth:0 # 最大宽度 自动换行
_contentHeight:0 # 最大高度,超过最大高度自动加滚动条
ctor:(str,fontSize=24,color='#ffffff',width=0,height=0)->
this._super()
color = '#ffffff' if not color
this._str = String(str) or ''
this._fontSize = fontSize
# this._lineHeight = Math.round this._fontSize * 0.5
this._color = color
this._charSprites = []
this._contentWidth = width
this._contentHeight = height
this.anchorY=1
return if not str
this.updateString()
updateString:->
locStr = String this._str
color = this._color
fontSize = this._fontSize
typeof color is 'string' and color = cc.color color
colorList = [color]
len = locStr.length or 0
this.removeAllChildren()
this._charSprites=[]
posX = maxWidth = maxHeight = i = 0
rows = 1
fontHeight = 0 # 字体的高度和fontSize不一定相同
if locStr.indexOf('[color=') is -1 and this._contentWidth is 0 # 不需要区分颜色和换行
sprite = this.getStrSprite locStr,fontSize
sprite.attr x:0,y:-1*fontSize,anchorY:0,anchorX:0,fillStyle:color
maxWidth = sprite.width
maxHeight = fontHeight = sprite.height
this._charSprites.push sprite
else
while i<len
char = locStr[i]
key = locStr.charCodeAt i
if char is '[' and locStr.substr(i,7) is '[color='
colorList.push cc.color locStr.substr i 7,7
i=i 15
continue
if char is '[' and locStr.substr(i,8) is '[/color]'
colorList.pop()
i=i 8
continue
if key is 10 # 换行
rows
posX=0
i
continue
sprite = this.getStrSprite char,fontSize
this._charSprites.push sprite
fontHeight<sprite.height and fontHeight=sprite.height # 获取一个最大的字体高度用于计算
sprite.attr x:posX,anchorY:0,anchorX:0,fillStyle:colorList[colorList.length-1]
posX =sprite.width this._space # 增加字间距
if this._contentWidth>0 and posX>this._contentWidth # 换行
sprite.attr x:0
rows
posX=sprite.width this._space
sprite.attr y:(rows-1)*(fontHeight this._lineHeight)*-1-fontHeight
maxWidth<posX and maxWidth=posX
i
maxHeight = rows * fontHeight (rows-1) * this._lineHeight
if this._contentWidth>0 and this._contentHeight>0
# 如果设置了内容最大宽度高度,则添加一个可滚动层
size = cc.size this._contentWidth,this._contentHeight
labelBgSprite = new cc.Sprite()
for sprite in this._charSprites
sprite.attr y:maxHeight sprite.y
labelBgSprite.addChild sprite
labelBgSprite.attr width:maxWidth,height:maxHeight,anchorX:0,anchorY:0
scroll = new cc.ScrollView size,labelBgSprite
scroll.attr direction:cc.SCROLLVIEW_DIRECTION_VERTICAL
scroll.setContentOffset x:0,y:scroll.minContainerOffset().y
this.addChild scroll
this.width = this._contentWidth
this.height = this._contentHeight
this.scroll = scroll
else
this.width = maxWidth
this.height = maxHeight
for sprite in this._charSprites
# 最大高度 偏移 向下移动一小部分(行距和字体刚度不同,增加这部分使得单行文字可以上下居中)
sprite.attr y:maxHeight sprite.y-(fontHeight-fontSize)
this.addChild sprite
setString:(str)->
this._str = String(str) or ''
this.updateString()
getStrSprite:(str,fontSize)->
new cc.LabelTTF str,'',fontSize