@@ -12,6 +12,7 @@ import {
12
12
import { isGradient } from './util/style' ;
13
13
import { createEl } from './util/dom' ;
14
14
import Browser from './Browser' ;
15
+ import Point from '../geo/Point' ;
15
16
import { getFont , getAlignPoint } from './util/strings' ;
16
17
17
18
const DEFAULT_STROKE_COLOR = '#000' ;
@@ -534,65 +535,51 @@ const Canvas = {
534
535
}
535
536
} ,
536
537
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 ) {
587
539
if ( ! points ) {
588
540
return ;
589
541
}
590
542
if ( points . length <= 2 || ! smoothValue ) {
591
543
Canvas . path ( ctx , points , lineOpacity ) ;
592
544
return ;
593
545
}
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
+
594
581
//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 ) {
596
583
// Assume we need to calculate the control
597
584
// points between (x1,y1) and (x2,y2).
598
585
// Then x0,y0 - the previous vertex,
@@ -620,13 +607,19 @@ const Canvas = {
620
607
ctrl2X = xm2 + ( xc2 - xm2 ) * smoothValue + x2 - xm2 ,
621
608
ctrl2Y = ym2 + ( yc2 - ym2 ) * smoothValue + y2 - ym2 ;
622
609
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
+ }
624
616
}
625
- const count = points . length ;
626
- const l = close ? count : count - 1 ;
617
+ let count = points . length ;
618
+ let l = close ? count : count - 1 ;
627
619
628
620
ctx . beginPath ( ) ;
629
621
ctx . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ;
622
+ if ( tailRatio !== undefined ) l -= Math . max ( l - tailIdx - 1 , 0 ) ;
630
623
let preCtrlPoints ;
631
624
for ( let i = 0 ; i < l ; i ++ ) {
632
625
const x1 = points [ i ] . x , y1 = points [ i ] . y ;
@@ -662,17 +655,28 @@ const Canvas = {
662
655
y3 = points [ i + 2 - count ] . y ;
663
656
}
664
657
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
+ }
667
669
points [ i ] . nextCtrlPoint = ctrlPoints . slice ( 0 , 2 ) ;
668
670
points [ i ] . prevCtrlPoint = preCtrlPoints ? preCtrlPoints . slice ( 2 ) : null ;
669
671
preCtrlPoints = ctrlPoints ;
670
672
}
671
- if ( ! close ) {
673
+ if ( ! close && points [ 1 ] . prevCtrlPoint ) {
672
674
points [ 0 ] . nextCtrlPoint = points [ 1 ] . prevCtrlPoint ;
673
675
delete points [ 0 ] . prevCtrlPoint ;
674
676
}
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
+ }
676
680
Canvas . _stroke ( ctx , lineOpacity ) ;
677
681
} ,
678
682
0 commit comments