修复cocos2d-jsv3.1文本换行bug

2019-12-03 18:07:37 浏览数 (1)

本文作者: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

0 人点赞