In the side dish section, I tried to draw a simple pie chart. Today, I tried to add a little gesture operation. You can rotate the pie chart with your finger.

ACEPieWidget

Gesture

The side dish adds a simple rotation gesture operation on the basis of the pie chart drawn before;

1. Gesture range

The small dish is used to rewrite PanGestureRecognizer to monitor gestures, or to do it directly by Gesture.

return Container( width: double.infinity, height: double.infinity, child: RawGestureDetector( child: CustomPaint( key: _key, painter: PiePainter(widget.listData, this.rotateAngle)), gestures: <Type, GestureRecognizerFactory>{ ACEPieGestureRecognizer: GestureRecognizerFactoryWithHandlers<ACEPieGestureRecognizer>( () => ACEPieGestureRecognizer(), (ACEPieGestureRecognizer gesture) { gesture.onDown = (detail) { }; gesture.onUpdate = (detail) { }; gesture.onEnd = (detail) { }; })}));Copy the code

2. Calculate the rotation Angle

The idea is to position the rotation Angle by using gesture.onUpdate, which updates the gesture coordinates and differs from the initial coordinates. The pie chart drawing is the Cartesian coordinate system, with the upper left corner as the origin of the coordinate system; The center of the pie chart is at the center of the screen size where the entire component is located.

RenderBox box = _key.currentContext.findRenderObject(); Offset offset = box.localToGlobal(Offset.zero); Width * 0.5, Offset. Dy + box.size. Height * 0.5);Copy the code

The side dishes are genericRenderBoxThe way to get customACEPieWidgetScreen size and get the center coordinates of the pie chart;

One of the things to note is the gesture listeningOffset detailsThe method of obtaining coordinates is slightly different:detail.localPositionIs the relative position of the current component relative to the origin of the upper-left coordinates, anddetail.globalPositionRetrieves the actual position of the coordinates in the upper left corner of the screen of the entire device, just as the dish begins to passlocalPositionMethod to obtain, calculate the Angle byWidgetThe position and size of the impact, the difference is large, recommended useglobalPositionWay;

By combining the center coordinates of the pie chart with the updated and pre-updated coordinate points, a triangle is determined by three points, and the Angle of gesture operation is obtained by the law of cosines, so as to redraw the pie chart.

_rotateAngle() {
  var _onDownLen = sqrt(pow(_startOffset.dx - _centerOffset.dx, 2) +
      pow(_startOffset.dy - _centerOffset.dy, 2));
  var _onUpdateLen = sqrt(pow(_updateOffset.dx - _centerOffset.dx, 2) +
      pow(_updateOffset.dy - _centerOffset.dy, 2));
  var _downToUpdateLen = sqrt(pow((_startOffset.dx - _updateOffset.dx), 2) +
      pow((_startOffset.dy - _updateOffset.dy), 2));
  var _cosAngle = (_onDownLen * _onDownLen + _onUpdateLen * _onUpdateLen -
          _downToUpdateLen * _downToUpdateLen) / (2 * _onDownLen * _onUpdateLen);
  rotateAngle += acos(_cosAngle);
  setState(() {});
}
Copy the code

3. Direction of rotation

After obtaining the triangle Angle of the side dish in the above way, it is found that the rotation direction can only be clockwise, and the reverse counterclockwise gesture is not effective. The reason is that the angles transformed by the law of cosine are all positive, so the direction of the vector should be judged. Therefore, xiaocai replaced another way, taking the center of the pie chart as the origin of the coordinate axis, setting a unit vector horizontally to the right, and then calculating the two angles through the coordinate changes of the gesture before and after, the difference is the included Angle;

_rotateAngle() {
  if (_startOffset.dy < _centerOffset.dy) {
    gestureDirection = -1;
  } else {
    gestureDirection = 1;
  }
  var _updateAngle = gestureDirection *
      _angle(_updateOffset, Offset(_centerOffset.dx + 100, _centerOffset.dy), _centerOffset);
  if (_updateOffset.dy < _centerOffset.dy) {
    gestureDirection = -1;
  } else {
    gestureDirection = 1;
  }
  var _startAngle = gestureDirection *
      _angle(_startOffset, Offset(_centerOffset.dx + 100, _centerOffset.dy), _centerOffset);
  return (_updateAngle - _startAngle);
}

_angle(_aPoint, _bPoint, _oPoint) {
  var _oALen = sqrt(pow(_aPoint.dx - _oPoint.dx, 2) + pow(_aPoint.dy - _oPoint.dy, 2));
  var _oBLen = sqrt(pow(_bPoint.dx - _oPoint.dx, 2) + pow(_bPoint.dy - _oPoint.dy, 2));
  var _aBLen = sqrt(pow(_aPoint.dx - _bPoint.dx, 2) + pow(_aPoint.dy - _bPoint.dy, 2));
  var _cosAngle = (pow(_oALen, 2) + pow(_oBLen, 2) - pow(_aBLen, 2)) /
      (2 * _oALen * _oBLen);
  return acos(_cosAngle);
}
Copy the code

Dart: Math library. Dart: Math library. The calculated Angle can be added to the Angle of pie chart traversal plot; Note that in the text drawing should also pay attention to the rotation of coordinate system Angle;

if (_listData ! = null) { for (int i = 0; i < _listData.length; i++) { startAngle += sweepAngle; sweepAngle = _listData[i].values.first * 2 * pi / _sum; canvas.drawArc(_circle, startAngle + _rotateAngle, sweepAngle, true, _paint.. color = _subPaint(_listData[i].keys.first)); If (sweepAngle >= PI / 6) {canvas. Translate (sie.width * 0.5, sie.height * 0.5); Canvas. rotate(startAngle + sweepAngle * 0.5 + _rotateAngle); Paragraph paragraph = (_pb.. addText(_subName)).build().. layout(_paragraph); Canvas. drawParagraph(paragraph, Offset(50.0, 0.0-paragraph. Height * 0.5)); Canvas. rotate(-startangle-sweepangle * 0.5-_rotateangle); Canvas. Translate (-sie.width * 0.5, -sie.height * 0.5); }}}Copy the code

dart:math

In the process of drawing pie charts, side dishes need to use trigonometric functions to draw offsets, which need some basic mathematical calculations. Dart also has the simple Dart: Math library, which is used for mathematical constants and functions, as well as random number generators.

1. Constant data

Dart: Math provides us with the natural number base e, logarithm ln, PI, etc., which is accurate in many bits and avoids our own definition.

// the base of the natural logarithm is e' e -> $e'; // log base e of 10' ln10 -> $ln10'; $ln2 -> $ln2; $log2e -> $log2e'; $log10e -> $log10e; $PI -> $PI; $sqrt2 -> $sqrt2; $sqrt2 -> $sqrt2;Copy the code

2. Multiple/exponential functions

Dart: Math provides convenient functional methods for square roots, exponents, exponential functions, and more;

// double SQRT (num x); // double exp(num x); // double log(num x); T min<T extends num>(T a, T b); T Max <T extends num>(T a, T b); Num pow(num x, num exponent);Copy the code

3. Trig functions

For trigonometric functions, it provides sine/cosine/tangent functions that convert radians into angles, and also provides conversion methods from Angle values to radians. Special scenarios such as negative numbers, 0, infinite numbers, irrational numbers, etc., need to be paid attention to.

// double sine (num radians); // Double cos(num radians); // Double tan(num radians); // double asin(num x); // double acos(num x); // double atan(num x);Copy the code


ACEPieWidget case source code

Dart: Math case source code


Source: Little Monk A Ce