[0]laya3.0富文本链接悬浮+单行处理

传统游戏中,针对富文本这块,一般情况下都会有链接处于一行,且需要添加鼠标悬浮时上移效果,针对这2个问题,laya3.0的富文本可以这样扩展:
  1. 单行处理:在Laya.Text.doLayout.run方法中,验证element为link时,链接文本宽度是否超过一行,如果超过一行,则执行换行操作。具体操作如下(改造Laya.Text.doLayout.run()方法,新增Laya.Text.doLayout.isLinkNewLine方法,具体修改可以对比Laya.Text源码):
            let isLinkNewLine = (sIdx: number, elements: Array<HtmlElement>) => {            let ele: HtmlElement, wid: number = 0;            for (let i: number = sIdx, len: number = elements.length; i < len; ++i) {                ele = elements[i];                if (ele.type == HtmlElementType.LinkEnd) {                    break;                }                if (ele.type == HtmlElementType.Text) {                    wid += getTextWidth(ele.text);                }            }            return lineX + wid > rectWidth;        }        let run = () => {            lineX = lineY = charWidth = charHeight = 0;            curLine = null;            lastCmd = null;            recoverLines(this._lines);            startLine();            let elements = this._elements;            for (let i = 0, n = elements.length; i < n; i++) {                let ele = elements[i];                if (ele.type == HtmlElementType.Text) {                    fontSize = Math.floor(ele.style.fontSize * this._fontSizeScale);                    if (fontSize == 0)                        fontSize = 1;                    buildLines(ele.text, ele.style);                }                else if (ele.type == HtmlElementType.LinkEnd) {                    if (lastCmd)                        lastCmd.linkEnd = true;                }                else {                    let htmlObj = ele.obj;                    if (!htmlObj) {                        let cls = HtmlParser.classMap[ele.type];                        if (cls) {                            htmlObj = Pool.createByClass(cls);                            htmlObj.create(this, ele);                            ele.obj = htmlObj;                        }                    }                    if (htmlObj) {                        if (wordWrap) {                            let remainWidth = rectWidth - lineX;                            if (remainWidth < htmlObj.width + 1) {                                if (lineX > 0) { //如果已经是开始位置了,就算放不下也不换行                                    endLine();                                    startLine();                                }                            }                        }                        addCmd(htmlObj, ele.style);                        if (ele.type == HtmlElementType.Link && isLinkNewLine(i + 1, elements)) {                            endLine();                            startLine();                        }                    }                }            }
  2. 链接对象悬浮上移效果,这个需要对HtmlLink和Laya.Text.renderText方法做出修改。在HtmlLink中添加链接文本和下划线指令集容器,并对_shape添加鼠标悬浮、移出等事件,并在事件中对添加的指令集做位移处理;Laya.Text.rednerText方法中对链接文本和下划线的绘制指令添加进HtmlLink中。[list=1][*]HtmlLink如下:
    import {Sprite} from "../display/Sprite";import {Text} from "../display/Text";import {Event} from "../events/Event";import {Rectangle} from "../maths/Rectangle";import {IHitArea} from "../utils/IHitArea";import {HtmlElement} from "./HtmlElement";import {IHtmlObject} from "./IHtmlObject";import {DrawLineCmd} from '../display/cmd/DrawLineCmd'export class HtmlLink implements IHtmlObject, IHitArea {    private static InitY: string = "$cmd_init_y";    /** 链接相关事件回调 */    public static onLinkMouse: (link: HtmlLink, type: string, evt: Event) => void;    private _owner: Text;    private _element: HtmlElement;    private _shape: Sprite;    private _rects: Array<Rectangle>;    private _rectCnt: number;    private _cmds: Array<any>;    public constructor() {        let that = this;        this._shape = new Sprite();        this._shape.hitArea = this;        this._shape.on(Event.CLICK, (evt: Event) => {            this._owner.bubbleEvent(Event.LINK, this._element.getAttrString("href"));            if (null != HtmlLink.onLinkMouse) {                HtmlLink.onLinkMouse(that, Event.LINK, evt);            }        });        this._shape.on(Event.ROLL_OVER, this, this.onRollEvent);        this._shape.on(Event.ROLL_OUT, this, this.onRollEvent);        this._shape.on(Event.MOUSE_DOWN, this, this.onMouseEvent);        this._shape.on(Event.MOUSE_UP, this, this.onMouseEvent);        this._rects = [];        this._rectCnt = 0;        this._cmds = [];    }    public get element(): HtmlElement {        return this._element;    }    public get width(): number {        return 0;    }    public get height(): number {        return 0;    }    public get rects(): Array<Rectangle> {        return this._rects;    }    public create(owner: Text, element: HtmlElement): void {        this._owner = owner;        this._element = element;        this._owner.objContainer.addChild(this._shape);        if (null != HtmlLink.onLinkMouse) {            HtmlLink.onLinkMouse(this, Event.ADDED, null);        }    }    public resetArea() {        this._rectCnt = 0;    }    public addRect(x: number, y: number, width: number, height: number) {        let rect = this._rects[this._rectCnt];        if (!rect)            rect = this._rects[this._rectCnt] = new Rectangle();        this._rectCnt++;        rect.setTo(x, y, width, height);        if (null != HtmlLink.onLinkMouse) {            HtmlLink.onLinkMouse(this, Event.CHANGE, null);        }    }    public contains(x: number, y: number): boolean {        for (let i = 0; i < this._rectCnt; i++) {            if (this._rects[i].contains(x, y))                return true;        }        return false;    }    public pos(x: number, y: number): void {    }    /** 添加本链接相关绘制指令 */    public addCmd(cmd: any): void {        cmd[HtmlLink.InitY] = cmd.cmdID == DrawLineCmd.ID  cmd.fromY : cmd.y;        this._cmds[this._cmds.length] = cmd;    }    /** 鼠标悬浮事件 */    private onRollEvent(evt: Event): void {        let offset: number = evt.type == Event.ROLL_OVER  -2 : 0;        let cmd: any;        for (let i: number = 0; i < this._cmds.length; ++i) {            cmd = this._cmds[i];            if (cmd.cmdID == DrawLineCmd.ID) {                cmd.fromY = cmd.toY = cmd[HtmlLink.InitY] + offset;            } else {                cmd.y = cmd[HtmlLink.InitY] + offset;            }        }        this._owner.repaint();        this.onMouseEvent(evt);    }    /** 其他事件 */    private onMouseEvent(evt: Event): void {        if (null != HtmlLink.onLinkMouse) {            HtmlLink.onLinkMouse(this, evt.type, evt);        }    }    public release(): void {        this._shape.removeSelf();        this._shape.offAll();        this._owner = null;        this._element = null;        // 清理指令集        for (let i: number = 0; i < this._cmds.length; ++i) {            delete this._cmds[i][HtmlLink.InitY];        }        this._cmds.length = 0;    }    public destroy(): void {        this._cmds.length = 0;        this._shape.destroy();    }}
  3. Laya.Text.rednerText修改如下:
        /**    * @private    * 渲染文字。    * @param	begin 开始渲染的行索引。    * @param	visibleLineCount 渲染的行数。    */    protected renderText(): void {        let graphics = this.graphics;        graphics.clear(true);        this.drawBg();        let padding = this._padding;        let paddingLeft = padding[3];        let paddingTop = padding[0];        let bfont = this._bitmapFont;        let scrollPos = this._scrollPos;        let rectWidth = (this._isWidthSet  this._width : this._textWidth) - padding[3] - padding[1];        let rectHeight = (this._isHeightSet  this._height : this._textHeight) - padding[0] - padding[2];        let bottom = paddingTop + rectHeight;        let clipped = this._overflow == Text.HIDDEN || this._overflow == Text.SCROLL;        if (clipped) {            graphics.save();            graphics.clipRect(paddingLeft, paddingTop, rectWidth, rectHeight);            this.repaint();        }        let x = 0, y = 0;        let lines = this._lines;        let lineCnt = lines.length;        let curLink: HtmlLink;        let linkStartX: number;        let gCmd: any;        for (let i = 0; i < lineCnt; i++) {            let line = lines[i];            x = paddingLeft + line.x;            y = paddingTop + line.y;            if (scrollPos) {                x -= scrollPos.x;                y -= scrollPos.y;            }            let lineClipped = clipped && ((y + line.height) <= paddingTop || y >= bottom);            let cmd = line.cmd;            while (cmd) {                gCmd = null;                if (cmd.linkEnd) {                    if (curLink) {                        curLink.addRect(linkStartX, y, x + cmd.x + cmd.width - linkStartX, line.height);                        // curLink = null;                    }                }                if (cmd.obj) {                    cmd.obj.pos(x + cmd.x, y + cmd.y);                    if (cmd.obj.element.type == HtmlElementType.Link) {                        curLink = <HtmlLink>cmd.obj;                        curLink.resetArea();                        linkStartX = x + cmd.x;                    }                } else if (!lineClipped) {                    if (bfont) {                        let tx: number = 0;                        let str = cmd.wt.text;                        let color = bfont.tint  cmd.style.color : "#FFFFFF";                        let scale = Math.floor((bfont.autoScaleSize  cmd.style.fontSize : bfont.fontSize) * this._fontSizeScale) / bfont.fontSize;                        for (let i = 0, n = str.length; i < n; i++) {                            let c = str.charCodeAt(i);                            let g = bfont.dict[c];                            if (g) {                                if (g.texture)                                    gCmd = graphics.drawImage(g.texture, x + cmd.x + tx + g.x * scale, y + cmd.y + g.y * scale, g.width * scale, g.height * scale, color);                                tx += Math.round(g.advance * scale);                            }                        }                    } else {                        let ctxFont = (<any>cmd.style)._ctxFont;                        if (cmd.style.stroke)                            gCmd = graphics.fillBorderText(cmd.wt, x + cmd.x, y + cmd.y, ctxFont, cmd.style.color, null, cmd.style.stroke, cmd.style.strokeColor);                        else                            gCmd = graphics.fillText(cmd.wt, x + cmd.x, y + cmd.y, ctxFont, cmd.style.color, null);                    }                    if (null != curLink) {                        curLink.addCmd(gCmd);                    }                }                if (!lineClipped && cmd.style.underline) {                    let thickness = Math.max(1, cmd.style.fontSize * this._fontSizeScale / 16);                    gCmd = graphics.drawLine(x + cmd.x, y + line.height - thickness, x + cmd.x + cmd.width, y + line.height - thickness, cmd.style.underlineColor || cmd.style.color, thickness);                    if (null != curLink) {                        curLink.addCmd(gCmd);                    }                }                if (cmd.linkEnd) {                    curLink = null;                }                cmd = cmd.next;            }            if (curLink) {                curLink.addRect(linkStartX, y, rectWidth - linkStartX + paddingLeft, line.height);                linkStartX = paddingLeft;            }        }        if (clipped)            graphics.restore();    }

[/*]
[/list]
 
另外,Laya3.0的富文本有很强的扩展性,如果有特殊需求(如:添加spine特效类型的表情),我们可以直接新增个HtmlElementType类型,在HtmlParser.parse中针对这个类型的标签做出识别,并针对这个类型新增一个如Laya.HtmlImage式的处理类,同时注册进Laya.HtmlParser.classMap中就好,可以参考fairygui中fgui.GHtmlImage的做法
已邀请:

谷主

赞同来自:

大佬给力,谢谢贡献,我吸收一下,然后加入到引擎。

要回复问题请先

商务合作
商务合作