@@ -12,6 +12,7 @@ import {
1212import { isGradient } from './util/style' ;
1313import { createEl } from './util/dom' ;
1414import Browser from './Browser' ;
15+ import Point from '../geo/Point' ;
1516import { getFont , getAlignPoint } from './util/strings' ;
1617
1718const DEFAULT_STROKE_COLOR = '#000' ;
@@ -534,65 +535,51 @@ const Canvas = {
534535 }
535536 } ,
536537
537- // paintSmoothLine(ctx, points, lineOpacity, smoothValue, close) {
538- // if (!points || points.length <= 2) {
539- // return;
540- // }
541- // function getQuadControlPoint(x0, y0, x1, y1, smoothValue) {
542- // const dist = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
543- // const perpDist = dist * smoothValue;
544- // const midx = (x0 + x1) / 2, midy = (y0 + y1) / 2;
545- // const degree = Math.PI / 2 - computeDegree(x0, y0, x1, y1);
546- // const dx = Math.cos(degree) * perpDist, dy = Math.sin(degree) * perpDist;
547- // return [midx - dx, midy + dy];
548- // }
549- // ctx.beginPath();
550- // ctx.moveTo(points[0].x, points[0].y);
551- // if (points.length <= 2 || !smoothValue) {
552- // Canvas._path(ctx, points);
553- // return;
554- // }
555- // const l = close ? points.length + 1 : points.length;
556- // let prevCtrlPoint;
557- // for (let i = 1; i < l; i++) {
558- // const prevPoint = points[i - 1];
559- // const currentPoint = i === points.length ? points[0] : points[i];
560- // let ctrlPoint;
561- // if (i === 1) {
562- // // the first control point
563- // ctrlPoint = getQuadControlPoint(prevPoint.x, prevPoint.y, points[i].x, points[i].y, smoothValue);
564- // } else {
565- // // the following control point
566- // const x = 2 * prevPoint.x - prevCtrlPoint[0], y = 2 * prevPoint.y - prevCtrlPoint[1];
567- // ctrlPoint = [x, y];
568- // }
569- // if (i < points.length) {
570- // ctx.quadraticCurveTo(ctrlPoint[0], ctrlPoint[1], currentPoint.x, currentPoint.y);
571- // prevPoint.nextCtrlPoint = ctrlPoint;
572- // currentPoint.prevCtrlPoint = ctrlPoint;
573- // prevCtrlPoint = ctrlPoint;
574- // } else {
575- // //the closing curve, draw a bezierCurve
576- // //the second control point, the opposite one of the first vertex's next control point
577- // const x1 = 2 * currentPoint.x - currentPoint.nextCtrlPoint[0], y1 = 2 * currentPoint.y - currentPoint.nextCtrlPoint[1];
578- // ctx.bezierCurveTo(ctrlPoint[0], ctrlPoint[1], x1, y1, currentPoint.x, currentPoint.y);
579- // }
580-
581- // }
582- // // points[points.length - 1].prevCtrlPoint = prevCtrlPoint;
583- // Canvas._stroke(ctx, lineOpacity);
584- // },
585-
586- paintSmoothLine ( ctx , points , lineOpacity , smoothValue , close ) {
538+ paintSmoothLine ( ctx , points , lineOpacity , smoothValue , close , tailIdx , tailRatio ) {
587539 if ( ! points ) {
588540 return ;
589541 }
590542 if ( points . length <= 2 || ! smoothValue ) {
591543 Canvas . path ( ctx , points , lineOpacity ) ;
592544 return ;
593545 }
546+
547+ //推算 cubic 贝塞尔曲线片段的起终点和控制点坐标
548+ //t0: 片段起始比例 0-1
549+ //t1: 片段结束比例 0-1
550+ //x1, y1, 曲线起点
551+ //bx1, by1, bx2, by2,曲线控制点
552+ //x2, y2 曲线终点
553+ //结果是曲线片段的起点,2个控制点坐标和终点坐标
554+ function interpolate ( t0 , t1 , x1 , y1 , bx1 , by1 , bx2 , by2 , x2 , y2 ) {
555+ const u0 = 1.0 - t0 ;
556+ const u1 = 1.0 - t1 ;
557+
558+ const qxa = x1 * u0 * u0 + bx1 * 2 * t0 * u0 + bx2 * t0 * t0 ;
559+ const qxb = x1 * u1 * u1 + bx1 * 2 * t1 * u1 + bx2 * t1 * t1 ;
560+ const qxc = bx1 * u0 * u0 + bx2 * 2 * t0 * u0 + x2 * t0 * t0 ;
561+ const qxd = bx1 * u1 * u1 + bx2 * 2 * t1 * u1 + x2 * t1 * t1 ;
562+
563+ const qya = y1 * u0 * u0 + by1 * 2 * t0 * u0 + by2 * t0 * t0 ;
564+ const qyb = y1 * u1 * u1 + by1 * 2 * t1 * u1 + by2 * t1 * t1 ;
565+ const qyc = by1 * u0 * u0 + by2 * 2 * t0 * u0 + y2 * t0 * t0 ;
566+ const qyd = by1 * u1 * u1 + by2 * 2 * t1 * u1 + y2 * t1 * t1 ;
567+
568+ // const xa = qxa * u0 + qxc * t0;
569+ const xb = qxa * u1 + qxc * t1 ;
570+ const xc = qxb * u0 + qxd * t0 ;
571+ const xd = qxb * u1 + qxd * t1 ;
572+
573+ // const ya = qya * u0 + qyc * t0;
574+ const yb = qya * u1 + qyc * t1 ;
575+ const yc = qyb * u0 + qyd * t0 ;
576+ const yd = qyb * u1 + qyd * t1 ;
577+
578+ return [ xb , yb , xc , yc , xd , yd ] ;
579+ }
580+
594581 //from http://www.antigrain.com/research/bezier_interpolation/
595- function getCubicControlPoints ( x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 , smoothValue ) {
582+ function getCubicControlPoints ( x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 , smoothValue , t ) {
596583 // Assume we need to calculate the control
597584 // points between (x1,y1) and (x2,y2).
598585 // Then x0,y0 - the previous vertex,
@@ -620,13 +607,19 @@ const Canvas = {
620607 ctrl2X = xm2 + ( xc2 - xm2 ) * smoothValue + x2 - xm2 ,
621608 ctrl2Y = ym2 + ( yc2 - ym2 ) * smoothValue + y2 - ym2 ;
622609
623- return [ ctrl1X , ctrl1Y , ctrl2X , ctrl2Y ] ;
610+ const ctrlPoints = [ ctrl1X , ctrl1Y , ctrl2X , ctrl2Y ] ;
611+ if ( t < 1 ) {
612+ return interpolate ( 0 , t , x1 , y1 , ctrl1X , ctrl1Y , ctrl2X , ctrl2Y , x2 , y2 ) ;
613+ } else {
614+ return ctrlPoints ;
615+ }
624616 }
625- const count = points . length ;
626- const l = close ? count : count - 1 ;
617+ let count = points . length ;
618+ let l = close ? count : count - 1 ;
627619
628620 ctx . beginPath ( ) ;
629621 ctx . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ;
622+ if ( tailRatio !== undefined ) l -= Math . max ( l - tailIdx - 1 , 0 ) ;
630623 let preCtrlPoints ;
631624 for ( let i = 0 ; i < l ; i ++ ) {
632625 const x1 = points [ i ] . x , y1 = points [ i ] . y ;
@@ -662,17 +655,28 @@ const Canvas = {
662655 y3 = points [ i + 2 - count ] . y ;
663656 }
664657
665- const ctrlPoints = getCubicControlPoints ( x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 , smoothValue ) ;
666- ctx . bezierCurveTo ( ctrlPoints [ 0 ] , ctrlPoints [ 1 ] , ctrlPoints [ 2 ] , ctrlPoints [ 3 ] , x2 , y2 ) ;
658+ const ctrlPoints = getCubicControlPoints ( x0 , y0 , x1 , y1 , x2 , y2 , x3 , y3 , smoothValue , i === l - 1 ? tailRatio : 1 ) ;
659+ if ( i === l - 1 && tailRatio >= 0 && tailRatio < 1 ) {
660+ ctx . bezierCurveTo ( ctrlPoints [ 0 ] , ctrlPoints [ 1 ] , ctrlPoints [ 2 ] , ctrlPoints [ 3 ] , ctrlPoints [ 4 ] , ctrlPoints [ 5 ] ) ;
661+ points . splice ( l - 1 , count - ( l - 1 ) - 1 ) ;
662+ const lastPoint = new Point ( ctrlPoints [ 4 ] , ctrlPoints [ 5 ] ) ;
663+ lastPoint . prevCtrlPoint = new Point ( ctrlPoints [ 2 ] , ctrlPoints [ 3 ] ) ;
664+ points . push ( lastPoint ) ;
665+ count = points . length ;
666+ } else {
667+ ctx . bezierCurveTo ( ctrlPoints [ 0 ] , ctrlPoints [ 1 ] , ctrlPoints [ 2 ] , ctrlPoints [ 3 ] , x2 , y2 ) ;
668+ }
667669 points [ i ] . nextCtrlPoint = ctrlPoints . slice ( 0 , 2 ) ;
668670 points [ i ] . prevCtrlPoint = preCtrlPoints ? preCtrlPoints . slice ( 2 ) : null ;
669671 preCtrlPoints = ctrlPoints ;
670672 }
671- if ( ! close ) {
673+ if ( ! close && points [ 1 ] . prevCtrlPoint ) {
672674 points [ 0 ] . nextCtrlPoint = points [ 1 ] . prevCtrlPoint ;
673675 delete points [ 0 ] . prevCtrlPoint ;
674676 }
675- points [ count - 1 ] . prevCtrlPoint = points [ count - 2 ] . nextCtrlPoint ;
677+ if ( ! points [ count - 1 ] . prevCtrlPoint ) {
678+ points [ count - 1 ] . prevCtrlPoint = points [ count - 2 ] . nextCtrlPoint ;
679+ }
676680 Canvas . _stroke ( ctx , lineOpacity ) ;
677681 } ,
678682
0 commit comments