首页 > 代码库 > 2014/09/16 – Canvas & Bezier

2014/09/16 – Canvas & Bezier

都是 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

2014/09/16 – Canvas & Bezier