都是 Canvas.setLineDash 惹的祸,就因为他是草案,所以有些浏览器还不支持。
需求: 画虚线。
分析:线包括直线与曲线。直线的虚线很好实现就不多说了,主要说下曲线的实现,即(3次)贝塞尔曲线的实现。
思路:1. 根据像素粒度值计算出3次贝塞尔曲线的点集合(参考wiki);
2. 根据求得的点集合,进行虚线的绘制; (难点是根据传入参数 dash list 进行像素点筛选)
code:
var DashedBezier = function (ctx, points, dashList, options) { this.ctx = ctx; this.points = points; this.dashList = dashList; this.options = $.extend( { particleSize: 100, strokeStyle: "#000", lineWidth: "1" }, options || {}); }; DashedBezier.prototype = { constructor: DashedBezier, _pointOnCubicBezier: function (cp, t) { var ax, bx, cx; var ay, by, cy; var tSquared, tCubed; var result = { x: 0.0, y: 0.0 }; /*計算多項式係數*/ cx = 3.0 * (cp[1].x - cp[0].x); bx = 3.0 * (cp[2].x - cp[1].x) - cx; ax = cp[3].x - cp[0].x - cx - bx; cy = 3.0 * (cp[1].y - cp[0].y); by = 3.0 * (cp[2].y - cp[1].y) - cy; ay = cp[3].y - cp[0].y - cy - by; /*計算位於參數值t的曲線點*/ tSquared = t * t; tCubed = tSquared * t; result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x; result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y; return result; }, _computeBezier: function (cp, numberOfPoints) { var dt; var i; var curve = []; dt = 1.0 / (numberOfPoints - 1); for (i = 0; i < numberOfPoints; i++) curve[i] = this._pointOnCubicBezier(cp, i * dt); return curve; }, _bezier: function (controlPoints, t) { return [controlPoints[t].x, controlPoints[t].y]; }, _calculateDashedBezier: function (controlPoints, dashPattern) { var step = 1; //this really should be set by an intelligent method, //rather than using a constant, but it serves as an //example. //possibly gratuitous helper functions var delta = function (p0, p1) { return [p1[0] - p0[0], p1[1] - p0[1]]; }; var arcLength = function (p0, p1) { var d = delta(p0, p1); return Math.sqrt(d[0] * d[0] + d[1] * d[1]); }; var subPaths = []; var loc = this._bezier(controlPoints, 0); var lastLoc = loc; var dashIndex = 0; var length = 0; var thisPath = []; for (var t = step; t < 100; t += step) { loc = this._bezier(controlPoints, t); length += arcLength(lastLoc, loc); lastLoc = loc; //detect when we come to the end of a dash or space if (length >= dashPattern[dashIndex]) { //if we are on a dash, we need to record the path. if (dashIndex % 2 == 0) subPaths.push(thisPath); //go to the next dash or space in the pattern dashIndex = (dashIndex + 1) % dashPattern.length; //clear the arclength and path. thisPath = []; length = 0; } //if we are on a dash and not a space, add a point to the path. if (dashIndex % 2 == 0) { thisPath.push(loc[0], loc[1]); } } if (thisPath.length > 0) subPaths.push(thisPath); return subPaths; }, _pathParts: function (ctx, pathParts) { var isMove = false; for (var i = 0; i < pathParts.length; i++) { if (pathParts[i].length !== 0) { if (!isMove) { ctx.moveTo(pathParts[i][0], pathParts[i][1]); } else { ctx.lineTo(pathParts[i][0], pathParts[i][1]); } isMove = !isMove; } //var part = pathParts[i]; //if (part.length > 0) // ctx.moveTo(part[0], part[1]); //for (var j = 1; j < part.length / 2; j++) { // ctx.lineTo(part[2 * j], part[2 * j + 1]); //} } }, _drawDashedBezier: function (ctx, controlPoints, dashPattern) { var dashes = this._calculateDashedBezier(controlPoints, dashPattern); ctx.beginPath(); ctx.strokeStyle = this.options.strokeStyle; ctx.lineWidth = this.options.lineWidth; this._pathParts(ctx, dashes); ctx.stroke(); }, set_Points: function (points) { this.points = points; }, draw: function (ctx) { ctx && (this.ctx = ctx); var cp = [{ x: this.points[0], y: this.points[1] }, { x: this.points[2], y: this.points[3] }, { x: this.points[4], y: this.points[5] }, { x: this.points[6], y: this.points[7] }], curve = this._computeBezier(cp, this.options.particleSize); this._drawDashedBezier(this.ctx, curve, this.dashList); } };
call:
var dashedBezierInstance = new DashedBezier( document.getElementById("dashCanvas").getContext("2d"), [0, 150, 75, 50, 225, 200, 300, 150], [2, 7], { strokeStyle: "#cc0000" }); dashedBezierInstance.draw();
call main
html:
<canvas width="400" height="300" id="dashCanvas"></canvas>
index.html
时间: 2024-11-12 02:49:12