[0]laya3.0富文本链接悬浮+单行处理
传统游戏中,针对富文本这块,一般情况下都会有链接处于一行,且需要添加鼠标悬浮时上移效果,针对这2个问题,laya3.0的富文本可以这样扩展:
[/*]
[/list]
另外,Laya3.0的富文本有很强的扩展性,如果有特殊需求(如:添加spine特效类型的表情),我们可以直接新增个HtmlElementType类型,在HtmlParser.parse中针对这个类型的标签做出识别,并针对这个类型新增一个如Laya.HtmlImage式的处理类,同时注册进Laya.HtmlParser.classMap中就好,可以参考fairygui中fgui.GHtmlImage的做法
- 单行处理:在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(); } } } }
- 链接对象悬浮上移效果,这个需要对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(); }}
- 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的做法
没有找到相关结果
已邀请:
要回复问题请先登录
1 个回复
谷主
赞同来自: