2997b33219db265604eabd869ec7b80ffc94accba32d9fa6ac9358b4a71c73578093b17a79d5c1df78e72b6f1bb3320f6e47f5b680ee7a9d57f4146bbb640a 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /**
  2. * 贝塞尔曲线
  3. */
  4. import Path, { PathProps } from '../Path';
  5. import * as vec2 from '../../core/vector';
  6. import {
  7. quadraticSubdivide,
  8. cubicSubdivide,
  9. quadraticAt,
  10. cubicAt,
  11. quadraticDerivativeAt,
  12. cubicDerivativeAt
  13. } from '../../core/curve';
  14. const out: number[] = [];
  15. export class BezierCurveShape {
  16. x1 = 0
  17. y1 = 0
  18. x2 = 0
  19. y2 = 0
  20. cpx1 = 0
  21. cpy1 = 0
  22. cpx2?: number
  23. cpy2?: number
  24. // Curve show percent, for animating
  25. percent = 1
  26. }
  27. function someVectorAt(shape: BezierCurveShape, t: number, isTangent: boolean) {
  28. const cpx2 = shape.cpx2;
  29. const cpy2 = shape.cpy2;
  30. if (cpx2 != null || cpy2 != null) {
  31. return [
  32. (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),
  33. (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)
  34. ];
  35. }
  36. else {
  37. return [
  38. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),
  39. (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)
  40. ];
  41. }
  42. }
  43. export interface BezierCurveProps extends PathProps {
  44. shape?: Partial<BezierCurveShape>
  45. }
  46. class BezierCurve extends Path<BezierCurveProps> {
  47. shape: BezierCurveShape
  48. constructor(opts?: BezierCurveProps) {
  49. super(opts);
  50. }
  51. getDefaultStyle() {
  52. return {
  53. stroke: '#000',
  54. fill: null as string
  55. };
  56. }
  57. getDefaultShape() {
  58. return new BezierCurveShape();
  59. }
  60. buildPath(ctx: CanvasRenderingContext2D, shape: BezierCurveShape) {
  61. let x1 = shape.x1;
  62. let y1 = shape.y1;
  63. let x2 = shape.x2;
  64. let y2 = shape.y2;
  65. let cpx1 = shape.cpx1;
  66. let cpy1 = shape.cpy1;
  67. let cpx2 = shape.cpx2;
  68. let cpy2 = shape.cpy2;
  69. let percent = shape.percent;
  70. if (percent === 0) {
  71. return;
  72. }
  73. ctx.moveTo(x1, y1);
  74. if (cpx2 == null || cpy2 == null) {
  75. if (percent < 1) {
  76. quadraticSubdivide(x1, cpx1, x2, percent, out);
  77. cpx1 = out[1];
  78. x2 = out[2];
  79. quadraticSubdivide(y1, cpy1, y2, percent, out);
  80. cpy1 = out[1];
  81. y2 = out[2];
  82. }
  83. ctx.quadraticCurveTo(
  84. cpx1, cpy1,
  85. x2, y2
  86. );
  87. }
  88. else {
  89. if (percent < 1) {
  90. cubicSubdivide(x1, cpx1, cpx2, x2, percent, out);
  91. cpx1 = out[1];
  92. cpx2 = out[2];
  93. x2 = out[3];
  94. cubicSubdivide(y1, cpy1, cpy2, y2, percent, out);
  95. cpy1 = out[1];
  96. cpy2 = out[2];
  97. y2 = out[3];
  98. }
  99. ctx.bezierCurveTo(
  100. cpx1, cpy1,
  101. cpx2, cpy2,
  102. x2, y2
  103. );
  104. }
  105. }
  106. /**
  107. * Get point at percent
  108. */
  109. pointAt(t: number) {
  110. return someVectorAt(this.shape, t, false);
  111. }
  112. /**
  113. * Get tangent at percent
  114. */
  115. tangentAt(t: number) {
  116. const p = someVectorAt(this.shape, t, true);
  117. return vec2.normalize(p, p);
  118. }
  119. };
  120. BezierCurve.prototype.type = 'bezier-curve';
  121. export default BezierCurve;