nemesiscodex/openfonacide

View on GitHub
static/js/dncp/util/shape/MarkLine.js

Summary

Maintainability
A
3 hrs
Test Coverage
/**
 * zrender
 *
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *         Yi Shen(https://github.com/pissang)
 *
 * shape类:标线
 */

/**
 * @typedef {Object} IMarkLineStyle
 * @property {number} xStart 起点x坐标
 * @property {number} yStart 起点y坐标
 * @property {number} xEnd 终止点x坐标
 * @property {number} yEnd 终止点y坐标
 * @property {number} cpX1 控制点x坐标,可以使用updatePoints自动根据curveness计算
 * @property {number} cpY1 控制点y坐标,可以使用updatePoints自动根据curveness计算
 * @property {number} curveness 曲度
 * @property {Array.<string>} symbol
 * @property {Array.<number>} symbolRotate
 */
define(function (require) {
    var Base = require('zrender/shape/Base');
    var IconShape = require('./Icon');
    var LineShape = require('zrender/shape/Line');
    var lineInstance = new LineShape({});
    var CurveShape = require('zrender/shape/BezierCurve');
    var curveInstance = new CurveShape({});

    var area = require('zrender/tool/area');
    var dashedLineTo = require('zrender/shape/util/dashedLineTo');
    var zrUtil = require('zrender/tool/util');
    var curveTool = require('zrender/tool/curve');

    function MarkLine(options) {
        Base.call(this, options);

        if (this.style.curveness > 0) {
            this.updatePoints(this.style);
        }
        if (this.highlightStyle.curveness > 0) {
            this.updatePoints(this.highlightStyle);
        }
    }

    MarkLine.prototype =  {
        type : 'mark-line',
        /**
         * 画刷
         * @param ctx 画布句柄
         * @param isHighlight   是否为高亮状态
         * @param updateCallback 让painter更新视图,base.brush没用,需要的话重载brush
         */
        brush : function (ctx, isHighlight) {
            var style = this.style;

            if (isHighlight) {
                // 根据style扩展默认高亮样式
                style = this.getHighlightStyle(
                    style,
                    this.highlightStyle || {}
                );
            }

            ctx.save();
            this.setContext(ctx, style);

            // 设置transform
            this.setTransform(ctx);

            ctx.save();
            ctx.beginPath();
            this.buildPath(ctx, style);
            ctx.stroke();
            ctx.restore();

            this.brushSymbol(ctx, style, 0);
            this.brushSymbol(ctx, style, 1);

            this.drawText(ctx, style, this.style);

            ctx.restore();
        },

        /**
         * 创建线条路径
         * @param {Context2D} ctx Canvas 2D上下文
         * @param {Object} style 样式
         */
        buildPath : function (ctx, style) {
            var lineType = style.lineType || 'solid';

            ctx.moveTo(style.xStart, style.yStart);
            if (style.curveness > 0) {
                // FIXME Bezier 在少部分浏览器上暂时不支持虚线
                var lineDash = null;
                switch (lineType) {
                    case 'dashed':
                        lineDash = [5, 5];
                        break;
                    case'dotted':
                        lineDash = [1, 1];
                        break;
                }
                if (lineDash && ctx.setLineDash) {
                    ctx.setLineDash(lineDash);
                }
                
                ctx.quadraticCurveTo(
                    style.cpX1, style.cpY1, style.xEnd, style.yEnd
                );
            }
            else {
                if (lineType == 'solid') {
                    ctx.lineTo(style.xEnd, style.yEnd);
                }
                else {
                    var dashLength = (style.lineWidth || 1) 
                        * (style.lineType == 'dashed' ? 5 : 1);
                    dashedLineTo(
                        ctx, style.xStart, style.yStart,
                        style.xEnd, style.yEnd, dashLength
                    );
                }
            }
        },

        /**
         * Update cpX1 and cpY1 according to curveniss
         * @param  {Object} style
         */
        updatePoints: function (style) {
            var curveness = style.curveness || 0;
            var inv = 1;

            var x0 = style.xStart;
            var y0 = style.yStart;
            var x2 = style.xEnd;
            var y2 = style.yEnd;
            var x1 = (x0 + x2) / 2 - inv * (y0 - y2) * curveness;
            var y1 =(y0 + y2) / 2 - inv * (x2 - x0) * curveness;

            style.cpX1 = x1;
            style.cpY1 = y1;
        },

        /**
         * 标线始末标注
         */
        brushSymbol : function (ctx, style, idx) {
            if (style.symbol[idx] == 'none') {
                return;
            }
            ctx.save();
            ctx.beginPath();

            ctx.lineWidth = style.symbolBorder;
            ctx.strokeStyle = style.symbolBorderColor;
            // symbol
            var symbol = style.symbol[idx].replace('empty', '')
                                              .toLowerCase();
            if (style.symbol[idx].match('empty')) {
                ctx.fillStyle = '#fff'; //'rgba(0, 0, 0, 0)';
            }

            // symbolRotate
            var x0 = style.xStart;
            var y0 = style.yStart;
            var x2 = style.xEnd;
            var y2 = style.yEnd;
            var x = idx === 0 ? x0 : x2;
            var y = idx === 0 ? y0 : y2;
            var curveness = style.curveness || 0;
            var rotate = style.symbolRotate[idx] != null ? (style.symbolRotate[idx] - 0) : 0;
            rotate = rotate / 180 * Math.PI;

            if (symbol == 'arrow' && rotate === 0) {
                if (curveness === 0) {
                    var sign = idx === 0 ? -1 : 1; 
                    rotate = Math.PI / 2 + Math.atan2(
                        sign * (y2 - y0), sign * (x2 - x0)
                    );
                }
                else {
                    var x1 = style.cpX1;
                    var y1 = style.cpY1;

                    var quadraticDerivativeAt = curveTool.quadraticDerivativeAt;
                    var dx = quadraticDerivativeAt(x0, x1, x2, idx);
                    var dy = quadraticDerivativeAt(y0, y1, y2, idx);

                    rotate = Math.PI / 2 + Math.atan2(dy, dx);
                }
            }
            
            ctx.translate(x, y);

            if (rotate !== 0) {
                ctx.rotate(rotate);
            }

            // symbolSize
            var symbolSize = style.symbolSize[idx];
            IconShape.prototype.buildPath(ctx, {
                x: -symbolSize,
                y: -symbolSize,
                width: symbolSize * 2,
                height: symbolSize * 2,
                iconType: symbol
            });

            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            ctx.restore();
        },

        /**
         * 返回矩形区域,用于局部刷新和文字定位
         * @param {Object} style
         */
        getRect : function (style) {
            style.curveness > 0 ? curveInstance.getRect(style)
                : lineInstance.getRect(style);
            return style.__rect;
        },

        isCover : function (x, y) {
            var originPos = this.transformCoordToLocal(x, y);
            x = originPos[0];
            y = originPos[1];

            // 快速预判并保留判断矩形
            if (this.isCoverRect(x, y)) {
                // 矩形内
                return this.style.curveness > 0
                       ? area.isInside(curveInstance, this.style, x, y)
                       : area.isInside(lineInstance, this.style, x, y);
            }

            return false;
        }
    };

    zrUtil.inherits(MarkLine, Base);

    return MarkLine;
});