UNPKG

@mapgis/webclient-es6-mapboxgl

Version:

515 lines (459 loc) 21.7 kB
import { Common } from '@mapgis/webclient-es6-service'; const { Zondy } = Common; import {Shape} from './Shape'; import {SUtil} from './SUtil'; /** * @private * @class Zondy.LevelRenderer.SmicText * @extends {Zondy.LevelRenderer.Shape} * @example * var shape = new Zondy.LevelRenderer.SmicText({ * style: { * text: 'Label', * x: 100, * y: 100, * textFont: '14px Arial' * } * }); * levelRenderer.addShape(shape); * */ class SmicText extends Shape { /** * @member {Object} Zondy.LevelRenderer.SmicText.prototype.style * @description 绘制样式。 * * @param {number} style.x - 横坐标,必设参数。 * @param {number} style.y - 纵坐标,必设参数。 * @param {string} style.text - 图形中的附加文本。默认值:""。 * @param {number} style.maxWidth - 最大宽度限制。默认值:null。 * @param {string} style.textFont - 附加文本样式。示例:'bold 18px verdana'。 * @param {string} style.textAlign - 附加文本水平对齐。可设值:"start", "end", "left", "right", "center"。默认根据 textPosition 自动设置。 * @param {string} style.textBaseline - 附加文本垂直对齐。可设值:"top", "bottom", "middle", "alphabetic", "hanging", "ideographic"。默认根据 textPosition 自动设置。 * @param {string} style.brushType -画笔类型。可设值:"fill", "stroke", "both"。默认值:"fill"。 * @param {string} style.color - 填充颜色。默认值:"#000000'"。 * @param {string} style.strokeColor - 描边颜色。默认值:"#000000'"。 * @param {number} style.lineWidth - 描边宽度。默认值:1。 * @param {number} style.opacity - 绘制透明度。默认值:1。 * @param {number} style.shadowBlur - 阴影模糊度,大于0有效。默认值:0。 * @param {number} style.shadowColor - 阴影颜色。默认值:"#000000'"。 * @param {number} style.shadowOffsetX - 阴影横向偏移。默认值:0。 * @param {number} style.shadowOffsetY - 阴影纵向偏移。默认值:0。 */ /** * @function Zondy.LevelRenderer.SmicText.constructor * @description 构造函数。 * * @param {Array} options - shape 的配置(options)项,可以是 shape 的自有属性,也可以是自定义的属性。 * */ constructor(options) { super(options); /** * @member {string} Zondy.LevelRenderer.SmicText.prototype.type * @description 图形类型. */ this.type = 'smictext'; if (!this.refOriginalPosition || this.refOriginalPosition.length !== 2) { this.refOriginalPosition = [0, 0]; } this.CLASS_NAME = "Zondy.LevelRenderer.SmicText"; } /** * @function Zondy.LevelRenderer.SmicText.prototype.destroy * @description 销毁对象,释放资源。调用此函数后所有属性将被置为 null。 */ destroy() { this.type = null; super.destroy(); } /** * @function Zondy.LevelRenderer.SmicText.prototype.brush * @description 笔触。 * * @param {CanvasRenderingContext2D} ctx - Context2D 上下文。 * @param {boolean} isHighlight - 是否使用高亮属性。 * */ brush(ctx, isHighlight) { if (!this.refOriginalPosition || this.refOriginalPosition.length !== 2) { this.refOriginalPosition = [0, 0]; } var __OP = this.refOriginalPosition; var style = this.style; if (isHighlight) { // 根据style扩展默认高亮样式 style = this.getHighlightStyle( style, this.highlightStyle || {} ); } if (typeof (style.text) == 'undefined' || style.text === false) { return; } ctx.save(); this.doClip(ctx); this.setContext(ctx, style); // 设置transform this.setTransform(ctx); if (style.textFont) { ctx.font = style.textFont; } ctx.textAlign = style.textAlign || 'start'; ctx.textBaseline = style.textBaseline || 'middle'; var text = (style.text + '').split('\n'); var lineHeight = SUtil.Util_area.getTextHeight('ZH', style.textFont); var rect = this.getRectNoRotation(style); // var x = style.x; var x = style.x + __OP[0]; var y; if (style.textBaseline === 'top') { y = rect.y; } else if (style.textBaseline === 'bottom') { y = rect.y + lineHeight; } else { y = rect.y + lineHeight / 2; } var ox = style.x + __OP[0]; var oy = style.y + __OP[1]; //文本绘制 for (var i = 0, l = text.length; i < l; i++) { //是否渲染矩形背景及颜色 if (style.labelRect) { //+4,-2是为了让文字距边框左右边缘有点间隔 ctx.fillRect(rect.x - 2, rect.y, rect.width + 4, rect.height); ctx.fillStyle = style.strokeColor; ctx.strokeRect(rect.x - 2, rect.y, rect.width + 4, rect.height); ctx.fillStyle = style.textColor; } switch (style.brushType) { case 'stroke': this.setCtxGlobalAlpha(ctx, "stroke", style); if (style.textRotation && style.textRotation !== 0) { ctx.save(); ctx.translate(ox, oy); ctx.rotate(style.textRotation * Math.PI / 180); if (style.textBaseline === 'top') { if (style.maxWidth) { ctx.strokeText(text[i], 0, lineHeight * i, style.maxWidth); } else { ctx.strokeText(text[i], 0, lineHeight * i); } } else if (style.textBaseline === 'bottom') { if (style.maxWidth) { ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height, style.maxWidth); } else { ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height); } } else { if (style.maxWidth) { ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2, style.maxWidth); } else { ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2); } } ctx.restore(); } else { if (style.maxWidth) { ctx.strokeText(text[i], x, y, style.maxWidth); } else { ctx.strokeText(text[i], x, y); } } this.setCtxGlobalAlpha(ctx, "reset", style); break; case 'both': if (style.textRotation && style.textRotation !== 0) { ctx.save(); ctx.translate(ox, oy); ctx.rotate(style.textRotation * Math.PI / 180); if (style.textBaseline === 'top') { if (style.maxWidth) { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], 0, lineHeight * i, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], 0, lineHeight * i, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); } else { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], 0, lineHeight * i); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], 0, lineHeight * i); this.setCtxGlobalAlpha(ctx, "reset", style); } } else if (style.textBaseline === 'bottom') { if (style.maxWidth) { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); } else { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height); this.setCtxGlobalAlpha(ctx, "reset", style); } } else { if (style.maxWidth) { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); } else { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2); this.setCtxGlobalAlpha(ctx, "reset", style); } } ctx.restore(); } else { if (style.maxWidth) { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], x, y, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], x, y, style.maxWidth); this.setCtxGlobalAlpha(ctx, "reset", style); } else { this.setCtxGlobalAlpha(ctx, "fill", style); ctx.fillText(text[i], x, y); this.setCtxGlobalAlpha(ctx, "reset", style); this.setCtxGlobalAlpha(ctx, "stroke", style); ctx.strokeText(text[i], x, y); this.setCtxGlobalAlpha(ctx, "reset", style); } } break; default: //fill or others this.setCtxGlobalAlpha(ctx, "fill", style); if (style.textRotation && style.textRotation !== 0) { ctx.save(); ctx.translate(ox, oy); ctx.rotate(style.textRotation * Math.PI / 180); if (style.textBaseline === 'top') { if (style.maxWidth) { ctx.fillText(text[i], 0, lineHeight * i, style.maxWidth); } else { ctx.fillText(text[i], 0, lineHeight * i); } } else if (style.textBaseline === 'bottom') { if (style.maxWidth) { ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height, style.maxWidth); } else { ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height); } } else { if (style.maxWidth) { ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2, style.maxWidth); } else { ctx.fillText(text[i], 0, lineHeight * (i + 1) - rect.height / 2 - lineHeight / 2); } } ctx.restore(); } else { if (style.maxWidth) { ctx.fillText(text[i], x, y, style.maxWidth); } else { ctx.fillText(text[i], x, y); } } this.setCtxGlobalAlpha(ctx, "reset", style); } y += lineHeight; } ctx.restore(); return; } /** * @function Zondy.LevelRenderer.SmicText.prototype.getRect * @description 返回文字包围盒矩形 */ getRect(style) { if (style.__rect) { return style.__rect; } var left, top, right, bottom; var tbg = this.getTextBackground(style, true); for (var i = 0, len = tbg.length; i < len; i++) { var poi = tbg[i]; //用第一个点初始化 if (i === 0) { left = poi[0]; right = poi[0]; top = poi[1]; bottom = poi[1]; } else { if (poi[0] < left) { left = poi[0] } if (poi[0] > right) { right = poi[0] } if (poi[1] < top) { top = poi[1] } if (poi[1] > bottom) { bottom = poi[1] } } } style.__rect = { x: left, y: top, width: right - left, height: bottom - top }; return style.__rect; } /** * @function Zondy.LevelRenderer.SmicText.prototype.getRectNoRotation * @description 返回忽略旋转和maxWidth时文字包围盒矩形 */ getRectNoRotation(style) { if (!this.refOriginalPosition || this.refOriginalPosition.length !== 2) { this.refOriginalPosition = [0, 0]; } var __OP = this.refOriginalPosition; var lineHeight = SUtil.Util_area.getTextHeight('ZH', style.textFont); var width = SUtil.Util_area.getTextWidth(style.text, style.textFont); var height = SUtil.Util_area.getTextHeight(style.text, style.textFont); //处理文字位置,注:文本的绘制是由此 rect 决定 var textX = style.x + __OP[0]; // 默认start == left if (style.textAlign === 'end' || style.textAlign === 'right') { textX -= width; } else if (style.textAlign === 'center') { textX -= (width / 2); } var textY; if (style.textBaseline === 'top') { // textY = style.y; textY = style.y + __OP[1]; } else if (style.textBaseline === 'bottom') { textY = (style.y + __OP[1]) - height; } else { // middle textY = (style.y + __OP[1]) - height / 2; } var isWidthChangeByMaxWidth = false; var widthBeforeChangeByMaxWidth; //处理 maxWidth if (style.maxWidth) { var maxWidth = parseInt(style.maxWidth); if (maxWidth < width) { widthBeforeChangeByMaxWidth = width; isWidthChangeByMaxWidth = true; width = maxWidth; } textX = style.x + __OP[0]; if (style.textAlign === 'end' || style.textAlign === 'right') { textX -= width; } else if (style.textAlign === 'center') { textX -= (width / 2); } } //处理斜体字 if (style.textFont) { var textFont = style.textFont; var textFontStr = textFont.toLowerCase() if (textFontStr.indexOf("italic") > -1) { if (widthBeforeChangeByMaxWidth && isWidthChangeByMaxWidth === true) { width += (lineHeight / 3) * (width / widthBeforeChangeByMaxWidth); } else { width += lineHeight / 3; } } } var rect = { x: textX, y: textY, width: width, height: height }; return rect; } /** * @function Zondy.LevelRenderer.SmicText.prototype.getTextBackground * @description 获取文本背景框范围 * * @param {Object} style - 样式。 * @param {boolean} redo - 是否强制重新计算 textBackground。 */ getTextBackground(style, redo) { if (!this.refOriginalPosition || this.refOriginalPosition.length !== 2) { this.refOriginalPosition = [0, 0]; } var __OP = this.refOriginalPosition; if ((!redo || redo === false) && style.__textBackground) { return style.__textBackground; } //不旋转时矩形框 var rect = this.getRectNoRotation(style); //旋转中心点 var ox = style.x + __OP[0]; var oy = style.y + __OP[1]; //背景框 var background = []; if (style.textRotation && style.textRotation !== 0) { let textRotation = style.textRotation; let ltPoi = this.getRotatedLocation(rect.x, rect.y, ox, oy, textRotation); let rtPoi = this.getRotatedLocation(rect.x + rect.width, rect.y, ox, oy, textRotation); let rbPoi = this.getRotatedLocation(rect.x + rect.width, rect.y + rect.height, ox, oy, textRotation); let lbPoi = this.getRotatedLocation(rect.x, rect.y + rect.height, ox, oy, textRotation); background.push(ltPoi); background.push(rtPoi); background.push(rbPoi); background.push(lbPoi); } else { let ltPoi = [rect.x, rect.y]; let rtPoi = [rect.x + rect.width, rect.y]; let rbPoi = [rect.x + rect.width, rect.y + rect.height]; let lbPoi = [rect.x, rect.y + rect.height]; background.push(ltPoi); background.push(rtPoi); background.push(rbPoi); background.push(lbPoi); } style.__textBackground = background; return style.__textBackground; } /** * @function Zondy.LevelRenderer.SmicText.prototype.getRotatedLocation * @description 获取一个点绕旋转中心顺时针旋转后的位置。(此方法用于屏幕坐标) * * @param {number} x - 旋转点横坐标。 * @param {number} y - 旋转点纵坐标。 * @param {number} rx - 旋转中心点横坐标。 * @param {number} ry - 旋转中心点纵坐标。 * @param {number} angle - 旋转角度(度)。 * @return {Array} 旋转后的坐标位置,长度为 2 的一维数组,数组第一个元素表示 x 坐标,第二个元素表示 y 坐标。 */ getRotatedLocation(x, y, rx, ry, angle) { var loc = new Array(), x0, y0; y = -y; ry = -ry; angle = -angle;//顺时针旋转 x0 = (x - rx) * Math.cos((angle / 180) * Math.PI) - (y - ry) * Math.sin((angle / 180) * Math.PI) + rx; y0 = (x - rx) * Math.sin((angle / 180) * Math.PI) + (y - ry) * Math.cos((angle / 180) * Math.PI) + ry; loc[0] = x0; loc[1] = -y0; return loc; } } export {SmicText}; Zondy.LevelRenderer.SmicText = SmicText;