770 lines
18 KiB
JavaScript
770 lines
18 KiB
JavaScript
var curve = require("./curve");
|
||
|
||
var vec2 = require("./vector");
|
||
|
||
var bbox = require("./bbox");
|
||
|
||
var BoundingRect = require("./BoundingRect");
|
||
|
||
var _config = require("../config");
|
||
|
||
var dpr = _config.devicePixelRatio;
|
||
|
||
/**
|
||
* Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
|
||
* 可以用于 isInsidePath 判断以及获取boundingRect
|
||
*
|
||
* @module zrender/core/PathProxy
|
||
* @author Yi Shen (http://www.github.com/pissang)
|
||
*/
|
||
// TODO getTotalLength, getPointAtLength
|
||
|
||
/* global Float32Array */
|
||
var CMD = {
|
||
M: 1,
|
||
L: 2,
|
||
C: 3,
|
||
Q: 4,
|
||
A: 5,
|
||
Z: 6,
|
||
// Rect
|
||
R: 7
|
||
}; // var CMD_MEM_SIZE = {
|
||
// M: 3,
|
||
// L: 3,
|
||
// C: 7,
|
||
// Q: 5,
|
||
// A: 9,
|
||
// R: 5,
|
||
// Z: 1
|
||
// };
|
||
|
||
var min = [];
|
||
var max = [];
|
||
var min2 = [];
|
||
var max2 = [];
|
||
var mathMin = Math.min;
|
||
var mathMax = Math.max;
|
||
var mathCos = Math.cos;
|
||
var mathSin = Math.sin;
|
||
var mathSqrt = Math.sqrt;
|
||
var mathAbs = Math.abs;
|
||
var hasTypedArray = typeof Float32Array !== 'undefined';
|
||
/**
|
||
* @alias module:zrender/core/PathProxy
|
||
* @constructor
|
||
*/
|
||
|
||
var PathProxy = function (notSaveData) {
|
||
this._saveData = !(notSaveData || false);
|
||
|
||
if (this._saveData) {
|
||
/**
|
||
* Path data. Stored as flat array
|
||
* @type {Array.<Object>}
|
||
*/
|
||
this.data = [];
|
||
}
|
||
|
||
this._ctx = null;
|
||
};
|
||
/**
|
||
* 快速计算Path包围盒(并不是最小包围盒)
|
||
* @return {Object}
|
||
*/
|
||
|
||
|
||
PathProxy.prototype = {
|
||
constructor: PathProxy,
|
||
_xi: 0,
|
||
_yi: 0,
|
||
_x0: 0,
|
||
_y0: 0,
|
||
// Unit x, Unit y. Provide for avoiding drawing that too short line segment
|
||
_ux: 0,
|
||
_uy: 0,
|
||
_len: 0,
|
||
_lineDash: null,
|
||
_dashOffset: 0,
|
||
_dashIdx: 0,
|
||
_dashSum: 0,
|
||
|
||
/**
|
||
* @readOnly
|
||
*/
|
||
setScale: function (sx, sy, segmentIgnoreThreshold) {
|
||
// Compat. Previously there is no segmentIgnoreThreshold.
|
||
segmentIgnoreThreshold = segmentIgnoreThreshold || 0;
|
||
this._ux = mathAbs(segmentIgnoreThreshold / dpr / sx) || 0;
|
||
this._uy = mathAbs(segmentIgnoreThreshold / dpr / sy) || 0;
|
||
},
|
||
getContext: function () {
|
||
return this._ctx;
|
||
},
|
||
|
||
/**
|
||
* @param {CanvasRenderingContext2D} ctx
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
beginPath: function (ctx) {
|
||
this._ctx = ctx;
|
||
ctx && ctx.beginPath();
|
||
ctx && (this.dpr = ctx.dpr); // Reset
|
||
|
||
if (this._saveData) {
|
||
this._len = 0;
|
||
}
|
||
|
||
if (this._lineDash) {
|
||
this._lineDash = null;
|
||
this._dashOffset = 0;
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* @param {number} x
|
||
* @param {number} y
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
moveTo: function (x, y) {
|
||
this.addData(CMD.M, x, y);
|
||
this._ctx && this._ctx.moveTo(x, y); // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
|
||
// xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
|
||
// 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要
|
||
// 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持
|
||
|
||
this._x0 = x;
|
||
this._y0 = y;
|
||
this._xi = x;
|
||
this._yi = y;
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* @param {number} x
|
||
* @param {number} y
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
lineTo: function (x, y) {
|
||
var exceedUnit = mathAbs(x - this._xi) > this._ux || mathAbs(y - this._yi) > this._uy // Force draw the first segment
|
||
|| this._len < 5;
|
||
this.addData(CMD.L, x, y);
|
||
|
||
if (this._ctx && exceedUnit) {
|
||
this._needsDash() ? this._dashedLineTo(x, y) : this._ctx.lineTo(x, y);
|
||
}
|
||
|
||
if (exceedUnit) {
|
||
this._xi = x;
|
||
this._yi = y;
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* @param {number} x1
|
||
* @param {number} y1
|
||
* @param {number} x2
|
||
* @param {number} y2
|
||
* @param {number} x3
|
||
* @param {number} y3
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
|
||
this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
|
||
|
||
if (this._ctx) {
|
||
this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
|
||
}
|
||
|
||
this._xi = x3;
|
||
this._yi = y3;
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* @param {number} x1
|
||
* @param {number} y1
|
||
* @param {number} x2
|
||
* @param {number} y2
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
quadraticCurveTo: function (x1, y1, x2, y2) {
|
||
this.addData(CMD.Q, x1, y1, x2, y2);
|
||
|
||
if (this._ctx) {
|
||
this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2) : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
|
||
}
|
||
|
||
this._xi = x2;
|
||
this._yi = y2;
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* @param {number} cx
|
||
* @param {number} cy
|
||
* @param {number} r
|
||
* @param {number} startAngle
|
||
* @param {number} endAngle
|
||
* @param {boolean} anticlockwise
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
|
||
this.addData(CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1);
|
||
this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
|
||
this._xi = mathCos(endAngle) * r + cx;
|
||
this._yi = mathSin(endAngle) * r + cy;
|
||
return this;
|
||
},
|
||
// TODO
|
||
arcTo: function (x1, y1, x2, y2, radius) {
|
||
if (this._ctx) {
|
||
this._ctx.arcTo(x1, y1, x2, y2, radius);
|
||
}
|
||
|
||
return this;
|
||
},
|
||
// TODO
|
||
rect: function (x, y, w, h) {
|
||
this._ctx && this._ctx.rect(x, y, w, h);
|
||
this.addData(CMD.R, x, y, w, h);
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
closePath: function () {
|
||
this.addData(CMD.Z);
|
||
var ctx = this._ctx;
|
||
var x0 = this._x0;
|
||
var y0 = this._y0;
|
||
|
||
if (ctx) {
|
||
this._needsDash() && this._dashedLineTo(x0, y0);
|
||
ctx.closePath();
|
||
}
|
||
|
||
this._xi = x0;
|
||
this._yi = y0;
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。
|
||
* stroke 同样
|
||
* @param {CanvasRenderingContext2D} ctx
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
fill: function (ctx) {
|
||
ctx && ctx.fill();
|
||
this.toStatic();
|
||
},
|
||
|
||
/**
|
||
* @param {CanvasRenderingContext2D} ctx
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
stroke: function (ctx) {
|
||
ctx && ctx.stroke();
|
||
this.toStatic();
|
||
},
|
||
|
||
/**
|
||
* 必须在其它绘制命令前调用
|
||
* Must be invoked before all other path drawing methods
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
setLineDash: function (lineDash) {
|
||
if (lineDash instanceof Array) {
|
||
this._lineDash = lineDash;
|
||
this._dashIdx = 0;
|
||
var lineDashSum = 0;
|
||
|
||
for (var i = 0; i < lineDash.length; i++) {
|
||
lineDashSum += lineDash[i];
|
||
}
|
||
|
||
this._dashSum = lineDashSum;
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* 必须在其它绘制命令前调用
|
||
* Must be invoked before all other path drawing methods
|
||
* @return {module:zrender/core/PathProxy}
|
||
*/
|
||
setLineDashOffset: function (offset) {
|
||
this._dashOffset = offset;
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
*
|
||
* @return {boolean}
|
||
*/
|
||
len: function () {
|
||
return this._len;
|
||
},
|
||
|
||
/**
|
||
* 直接设置 Path 数据
|
||
*/
|
||
setData: function (data) {
|
||
var len = data.length;
|
||
|
||
if (!(this.data && this.data.length === len) && hasTypedArray) {
|
||
this.data = new Float32Array(len);
|
||
}
|
||
|
||
for (var i = 0; i < len; i++) {
|
||
this.data[i] = data[i];
|
||
}
|
||
|
||
this._len = len;
|
||
},
|
||
|
||
/**
|
||
* 添加子路径
|
||
* @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
|
||
*/
|
||
appendPath: function (path) {
|
||
if (!(path instanceof Array)) {
|
||
path = [path];
|
||
}
|
||
|
||
var len = path.length;
|
||
var appendSize = 0;
|
||
var offset = this._len;
|
||
|
||
for (var i = 0; i < len; i++) {
|
||
appendSize += path[i].len();
|
||
}
|
||
|
||
if (hasTypedArray && this.data instanceof Float32Array) {
|
||
this.data = new Float32Array(offset + appendSize);
|
||
}
|
||
|
||
for (var i = 0; i < len; i++) {
|
||
var appendPathData = path[i].data;
|
||
|
||
for (var k = 0; k < appendPathData.length; k++) {
|
||
this.data[offset++] = appendPathData[k];
|
||
}
|
||
}
|
||
|
||
this._len = offset;
|
||
},
|
||
|
||
/**
|
||
* 填充 Path 数据。
|
||
* 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
|
||
*/
|
||
addData: function (cmd) {
|
||
if (!this._saveData) {
|
||
return;
|
||
}
|
||
|
||
var data = this.data;
|
||
|
||
if (this._len + arguments.length > data.length) {
|
||
// 因为之前的数组已经转换成静态的 Float32Array
|
||
// 所以不够用时需要扩展一个新的动态数组
|
||
this._expandData();
|
||
|
||
data = this.data;
|
||
}
|
||
|
||
for (var i = 0; i < arguments.length; i++) {
|
||
data[this._len++] = arguments[i];
|
||
}
|
||
|
||
this._prevCmd = cmd;
|
||
},
|
||
_expandData: function () {
|
||
// Only if data is Float32Array
|
||
if (!(this.data instanceof Array)) {
|
||
var newData = [];
|
||
|
||
for (var i = 0; i < this._len; i++) {
|
||
newData[i] = this.data[i];
|
||
}
|
||
|
||
this.data = newData;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* If needs js implemented dashed line
|
||
* @return {boolean}
|
||
* @private
|
||
*/
|
||
_needsDash: function () {
|
||
return this._lineDash;
|
||
},
|
||
_dashedLineTo: function (x1, y1) {
|
||
var dashSum = this._dashSum;
|
||
var offset = this._dashOffset;
|
||
var lineDash = this._lineDash;
|
||
var ctx = this._ctx;
|
||
var x0 = this._xi;
|
||
var y0 = this._yi;
|
||
var dx = x1 - x0;
|
||
var dy = y1 - y0;
|
||
var dist = mathSqrt(dx * dx + dy * dy);
|
||
var x = x0;
|
||
var y = y0;
|
||
var dash;
|
||
var nDash = lineDash.length;
|
||
var idx;
|
||
dx /= dist;
|
||
dy /= dist;
|
||
|
||
if (offset < 0) {
|
||
// Convert to positive offset
|
||
offset = dashSum + offset;
|
||
}
|
||
|
||
offset %= dashSum;
|
||
x -= offset * dx;
|
||
y -= offset * dy;
|
||
|
||
while (dx > 0 && x <= x1 || dx < 0 && x >= x1 || dx === 0 && (dy > 0 && y <= y1 || dy < 0 && y >= y1)) {
|
||
idx = this._dashIdx;
|
||
dash = lineDash[idx];
|
||
x += dx * dash;
|
||
y += dy * dash;
|
||
this._dashIdx = (idx + 1) % nDash; // Skip positive offset
|
||
|
||
if (dx > 0 && x < x0 || dx < 0 && x > x0 || dy > 0 && y < y0 || dy < 0 && y > y0) {
|
||
continue;
|
||
}
|
||
|
||
ctx[idx % 2 ? 'moveTo' : 'lineTo'](dx >= 0 ? mathMin(x, x1) : mathMax(x, x1), dy >= 0 ? mathMin(y, y1) : mathMax(y, y1));
|
||
} // Offset for next lineTo
|
||
|
||
|
||
dx = x - x1;
|
||
dy = y - y1;
|
||
this._dashOffset = -mathSqrt(dx * dx + dy * dy);
|
||
},
|
||
// Not accurate dashed line to
|
||
_dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
|
||
var dashSum = this._dashSum;
|
||
var offset = this._dashOffset;
|
||
var lineDash = this._lineDash;
|
||
var ctx = this._ctx;
|
||
var x0 = this._xi;
|
||
var y0 = this._yi;
|
||
var t;
|
||
var dx;
|
||
var dy;
|
||
var cubicAt = curve.cubicAt;
|
||
var bezierLen = 0;
|
||
var idx = this._dashIdx;
|
||
var nDash = lineDash.length;
|
||
var x;
|
||
var y;
|
||
var tmpLen = 0;
|
||
|
||
if (offset < 0) {
|
||
// Convert to positive offset
|
||
offset = dashSum + offset;
|
||
}
|
||
|
||
offset %= dashSum; // Bezier approx length
|
||
|
||
for (t = 0; t < 1; t += 0.1) {
|
||
dx = cubicAt(x0, x1, x2, x3, t + 0.1) - cubicAt(x0, x1, x2, x3, t);
|
||
dy = cubicAt(y0, y1, y2, y3, t + 0.1) - cubicAt(y0, y1, y2, y3, t);
|
||
bezierLen += mathSqrt(dx * dx + dy * dy);
|
||
} // Find idx after add offset
|
||
|
||
|
||
for (; idx < nDash; idx++) {
|
||
tmpLen += lineDash[idx];
|
||
|
||
if (tmpLen > offset) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
t = (tmpLen - offset) / bezierLen;
|
||
|
||
while (t <= 1) {
|
||
x = cubicAt(x0, x1, x2, x3, t);
|
||
y = cubicAt(y0, y1, y2, y3, t); // Use line to approximate dashed bezier
|
||
// Bad result if dash is long
|
||
|
||
idx % 2 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
|
||
t += lineDash[idx] / bezierLen;
|
||
idx = (idx + 1) % nDash;
|
||
} // Finish the last segment and calculate the new offset
|
||
|
||
|
||
idx % 2 !== 0 && ctx.lineTo(x3, y3);
|
||
dx = x3 - x;
|
||
dy = y3 - y;
|
||
this._dashOffset = -mathSqrt(dx * dx + dy * dy);
|
||
},
|
||
_dashedQuadraticTo: function (x1, y1, x2, y2) {
|
||
// Convert quadratic to cubic using degree elevation
|
||
var x3 = x2;
|
||
var y3 = y2;
|
||
x2 = (x2 + 2 * x1) / 3;
|
||
y2 = (y2 + 2 * y1) / 3;
|
||
x1 = (this._xi + 2 * x1) / 3;
|
||
y1 = (this._yi + 2 * y1) / 3;
|
||
|
||
this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
|
||
},
|
||
|
||
/**
|
||
* 转成静态的 Float32Array 减少堆内存占用
|
||
* Convert dynamic array to static Float32Array
|
||
*/
|
||
toStatic: function () {
|
||
var data = this.data;
|
||
|
||
if (data instanceof Array) {
|
||
data.length = this._len;
|
||
|
||
if (hasTypedArray) {
|
||
this.data = new Float32Array(data);
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @return {module:zrender/core/BoundingRect}
|
||
*/
|
||
getBoundingRect: function () {
|
||
min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE;
|
||
max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
|
||
var data = this.data;
|
||
var xi = 0;
|
||
var yi = 0;
|
||
var x0 = 0;
|
||
var y0 = 0;
|
||
|
||
for (var i = 0; i < data.length;) {
|
||
var cmd = data[i++];
|
||
|
||
if (i === 1) {
|
||
// 如果第一个命令是 L, C, Q
|
||
// 则 previous point 同绘制命令的第一个 point
|
||
//
|
||
// 第一个命令为 Arc 的情况下会在后面特殊处理
|
||
xi = data[i];
|
||
yi = data[i + 1];
|
||
x0 = xi;
|
||
y0 = yi;
|
||
}
|
||
|
||
switch (cmd) {
|
||
case CMD.M:
|
||
// moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
|
||
// 在 closePath 的时候使用
|
||
x0 = data[i++];
|
||
y0 = data[i++];
|
||
xi = x0;
|
||
yi = y0;
|
||
min2[0] = x0;
|
||
min2[1] = y0;
|
||
max2[0] = x0;
|
||
max2[1] = y0;
|
||
break;
|
||
|
||
case CMD.L:
|
||
bbox.fromLine(xi, yi, data[i], data[i + 1], min2, max2);
|
||
xi = data[i++];
|
||
yi = data[i++];
|
||
break;
|
||
|
||
case CMD.C:
|
||
bbox.fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);
|
||
xi = data[i++];
|
||
yi = data[i++];
|
||
break;
|
||
|
||
case CMD.Q:
|
||
bbox.fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);
|
||
xi = data[i++];
|
||
yi = data[i++];
|
||
break;
|
||
|
||
case CMD.A:
|
||
// TODO Arc 判断的开销比较大
|
||
var cx = data[i++];
|
||
var cy = data[i++];
|
||
var rx = data[i++];
|
||
var ry = data[i++];
|
||
var startAngle = data[i++];
|
||
var endAngle = data[i++] + startAngle; // TODO Arc 旋转
|
||
|
||
i += 1;
|
||
var anticlockwise = 1 - data[i++];
|
||
|
||
if (i === 1) {
|
||
// 直接使用 arc 命令
|
||
// 第一个命令起点还未定义
|
||
x0 = mathCos(startAngle) * rx + cx;
|
||
y0 = mathSin(startAngle) * ry + cy;
|
||
}
|
||
|
||
bbox.fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);
|
||
xi = mathCos(endAngle) * rx + cx;
|
||
yi = mathSin(endAngle) * ry + cy;
|
||
break;
|
||
|
||
case CMD.R:
|
||
x0 = xi = data[i++];
|
||
y0 = yi = data[i++];
|
||
var width = data[i++];
|
||
var height = data[i++]; // Use fromLine
|
||
|
||
bbox.fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
|
||
break;
|
||
|
||
case CMD.Z:
|
||
xi = x0;
|
||
yi = y0;
|
||
break;
|
||
} // Union
|
||
|
||
|
||
vec2.min(min, min, min2);
|
||
vec2.max(max, max, max2);
|
||
} // No data
|
||
|
||
|
||
if (i === 0) {
|
||
min[0] = min[1] = max[0] = max[1] = 0;
|
||
}
|
||
|
||
return new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);
|
||
},
|
||
|
||
/**
|
||
* Rebuild path from current data
|
||
* Rebuild path will not consider javascript implemented line dash.
|
||
* @param {CanvasRenderingContext2D} ctx
|
||
*/
|
||
rebuildPath: function (ctx) {
|
||
var d = this.data;
|
||
var x0;
|
||
var y0;
|
||
var xi;
|
||
var yi;
|
||
var x;
|
||
var y;
|
||
var ux = this._ux;
|
||
var uy = this._uy;
|
||
var len = this._len;
|
||
|
||
for (var i = 0; i < len;) {
|
||
var cmd = d[i++];
|
||
|
||
if (i === 1) {
|
||
// 如果第一个命令是 L, C, Q
|
||
// 则 previous point 同绘制命令的第一个 point
|
||
//
|
||
// 第一个命令为 Arc 的情况下会在后面特殊处理
|
||
xi = d[i];
|
||
yi = d[i + 1];
|
||
x0 = xi;
|
||
y0 = yi;
|
||
}
|
||
|
||
switch (cmd) {
|
||
case CMD.M:
|
||
x0 = xi = d[i++];
|
||
y0 = yi = d[i++];
|
||
ctx.moveTo(xi, yi);
|
||
break;
|
||
|
||
case CMD.L:
|
||
x = d[i++];
|
||
y = d[i++]; // Not draw too small seg between
|
||
|
||
if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len - 1) {
|
||
ctx.lineTo(x, y);
|
||
xi = x;
|
||
yi = y;
|
||
}
|
||
|
||
break;
|
||
|
||
case CMD.C:
|
||
ctx.bezierCurveTo(d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]);
|
||
xi = d[i - 2];
|
||
yi = d[i - 1];
|
||
break;
|
||
|
||
case CMD.Q:
|
||
ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
|
||
xi = d[i - 2];
|
||
yi = d[i - 1];
|
||
break;
|
||
|
||
case CMD.A:
|
||
var cx = d[i++];
|
||
var cy = d[i++];
|
||
var rx = d[i++];
|
||
var ry = d[i++];
|
||
var theta = d[i++];
|
||
var dTheta = d[i++];
|
||
var psi = d[i++];
|
||
var fs = d[i++];
|
||
var r = rx > ry ? rx : ry;
|
||
var scaleX = rx > ry ? 1 : rx / ry;
|
||
var scaleY = rx > ry ? ry / rx : 1;
|
||
var isEllipse = Math.abs(rx - ry) > 1e-3;
|
||
var endAngle = theta + dTheta;
|
||
|
||
if (isEllipse) {
|
||
ctx.translate(cx, cy);
|
||
ctx.rotate(psi);
|
||
ctx.scale(scaleX, scaleY);
|
||
ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
|
||
ctx.scale(1 / scaleX, 1 / scaleY);
|
||
ctx.rotate(-psi);
|
||
ctx.translate(-cx, -cy);
|
||
} else {
|
||
ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
|
||
}
|
||
|
||
if (i === 1) {
|
||
// 直接使用 arc 命令
|
||
// 第一个命令起点还未定义
|
||
x0 = mathCos(theta) * rx + cx;
|
||
y0 = mathSin(theta) * ry + cy;
|
||
}
|
||
|
||
xi = mathCos(endAngle) * rx + cx;
|
||
yi = mathSin(endAngle) * ry + cy;
|
||
break;
|
||
|
||
case CMD.R:
|
||
x0 = xi = d[i];
|
||
y0 = yi = d[i + 1];
|
||
ctx.rect(d[i++], d[i++], d[i++], d[i++]);
|
||
break;
|
||
|
||
case CMD.Z:
|
||
ctx.closePath();
|
||
xi = x0;
|
||
yi = y0;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
PathProxy.CMD = CMD;
|
||
var _default = PathProxy;
|
||
module.exports = _default; |