Abstract

Let’s take a look at the effect picture first (this is a GIF wow) :(attached source code at the bottom)

To achieve such a jigsaw function; It can be divided into three parts:

  1. Irregular clipping of jigsaw cards;
  2. Card drag and target position check;
  3. Feedback on correct or incorrect results;

1. Irregular clipping of puzzle cards

In order to simulate the patterns of real life puzzles, the cards in the puzzles are usually irregular; So first of all, we have to think about how to do irregular clipping of the graph; Since Xfermode has similar irregularly clipping methods on Android (there is a mode in Xfermode where two layers are superimposed and intersected), my first thought was whether there was a similar method in Flutter.

The Paint class has a method that sets blendMode, and blendMode has a mode called blendmode. srcIn, which caught my attention. After verification, it is exactly the same as Android Xfermode (indeed, both are the sons of Google);

So the solution is found, and now it can be done like this: ask the designer to provide an empty picture of the puzzle card; Something like this:

Then I superimposed a cut with the original picture, the effect came out, a thought is delectation. However, it suddenly occurred to me that the effect of directly superimposing layers can achieve irregular image cropping; However, the designer requested that the irregular card be traced, and that both success and failure should have an edge glow effect feedback (which will be discussed below).

Clearly this approach is problematic; Although the shape we see is correct, the image itself is a rectangle, but the rest of it is transparent; If you draw edges on this image, it’s actually a rectangle.

So what else can we do? Can we draw the path on the edge, and then superimpose that path on the image? The answer, of course, is OK; So now the problem is how to extract path;

Svg_path_parser (pub.dev/packages/sv… The path of a flutter is resolved to the path of a flutter

The key codes and steps are as follows:

// borderPath is extracted from SVG
/ / such as: M160,0 L160,75 C160,86.5979797 169.40202,96 181,96 C181.487961,96 181.972034,96.0166428 182.451684,96.0493917 C182.963713,96.0165423 183.479925,96 184,96 C197.254834,96 208,106.745166 208,120 C208,133.254834 197.254834,144 184,144 C183.479925,144 182.963713,143.983458 182.451863,143.95087 C181.972034,143.983357 181.487961,144 181,144 C169.40202,144 160,153.40202 160,165 L160,240 L125,240 C113.40202,240 104,230.59798 104,219 C104,218.512039 103.983357,218.027966 103.95087,217.548137 C103.983458,217.036287 104,216.520075 104,216 C104,202.745166 93.254834,192 80,192 c66.745166,192 56,202.745166 56,216 C56,216.520075 56.0165423,217.036287 56.0493917,217.548316 C56.0166428,218.027966 56,218.512039 56,219 C56,230.59798 46.5979797,240 35,240 L35,240 L4.54747351e-13,240 L4.54747351e-13,64 C4.50418687e-13,28.653776 28.653776, 6.49299601 64, 0 L160 e-15, 0, Z
path = parseSvgPath(borderPath);


void _drawImageCard(Canvas canvas, Size size) {
  Paint paint = Paint();
  paint.style = PaintingStyle.fill;
  paint.isAntiAlias = true;
  canvas.saveLayer(Offset.zero & size, Paint());
  canvas.drawPath(path, paint);
  paint.blendMode =  BlendMode.srcIn;
  if(! active) { paint.colorFilter = ColorFilter.mode(Colors.grey, BlendMode.color); } canvas.drawImageRect( srcImage, Rect.fromLTWH(0.0,
      srcImage.width.toDouble(),
      srcImage.height.toDouble(),
    ),
    Rect.fromLTWH(left, top, 480.480),
    paint,
  );
  canvas.restore();
}

Copy the code
  1. BorderPath is the path extracted from SVG; Path is the resolved flutter path.
  2. First draw path on canvas;
  3. Then set blendMode to blendmode. srcIn, which can be simply interpreted as taking the overlapping layer after overlay. (You can refer to the document for details about what each mode represents.);
  4. Finally draw the picture; Rect.fromLTWH(left, top, 480, 480) rect. fromLTWH(left, top, 480) rect. fromLTWH(left, top, 480, 480)

This completes cropping of irregular images; In addition, the edge path is also available, so drawing white edge lines is very easy; I won’t go into the details here. Interested can look at the source code.

So the first step has been taken, and we can now break down the image into little cards. So the next part is the splicing;

2. Drag the card and check the target position

Jigsaw part, mainly involves two problems, one is drag; Another is to drag to judge whether it is right, right on the light; Wrong words prompt;

Flutter does a good job of supporting this, mainly using Draggable and DragTarget;

Draggable, as the name suggests, is used to drag widgets.

DragTarget is used to receive the Draggable widget. After dragging the Draggable widget to the DragTarget, you can judge whether it is right or wrong. Of course you need some data to correlate it;

Here we will introduce the main parameters and methods. There are many parameters, not all of them. If you are interested, you can go to the document:

Draggable:

  • Data: Pass in your relevant data, mainly for association with DragTarget, preferably unique data;
  • Feedback: Follow the widget below your finger when dragging;
  • ChildWhenDragging: When dragging, the widget is dragged away from the placeholder widget in its original position;
  • Child: This is the component that you want to drag;
  • Drag events: onDragStarted, onDragUpdate, onDraggableCanceled, onDraggableCanceled, onDragCompleted; This few need not go into too much, mainly drag some callbacks;

DragTarget:

  • Builder: main return your target widget;
  • OnWillAccept: callback method; This is triggered when the Draggable is dragged over the DragTarget. If this method returns true, the onAccept method is triggered;
  • OnAccept: callback method; Emitted when onWillAccept returns true;
  • OnLeave: callback method; Trigger when finger leaves the DragTarget Widget;
  • OnMove: callback method; Callback when the finger moves over the DragTarget Widget

Above are the two main classes and some main parameters and methods; Once it’s clear, the logic is not hard;

As you can see from the above renderings, after the card is dragged away, the original position has a dashed line placeholder effect; So this involves drawing dashed lines, but Flutter itself does not provide a way to draw dashed lines; Then we need to masturbate ourselves.

Draw a dotted line. The idea here is to first determine the path, and then dot it from the path, and then connect the spaced points, which is the effect of the dotted line; There are two main steps:

1. Path dotting, that is, along the path, point coordinates are extracted according to fixed spacing;

List<Offset> _getIconOffsets() {
  try {
    PathMetrics pms = path.computeMetrics();
    PathMetric pm = pms.elementAt(0);
    int len = pm.length ~/ 10;
    double start = pm.length % 10 / 2;
    List<Offset> iconOffsets = [];
    for (int i = 0; i <= len; i++) {
      Tangent? t = pm.getTangentForOffset(i.toDouble() * 10+ start); Offset point = t! .position;double x = point.dx;
      double y = point.dy;
      iconOffsets.add(Offset(x, y));
    }
    return iconOffsets;
  } catch (e) {
    return List.empty(); }}Copy the code

2, connect two adjacent points, then leave a point, and then connect the next two points;

void_drawBorder(Canvas canvas, Size size) { Paint paint = Paint() .. style = PaintingStyle.stroke .. strokeWidth =4
    ..isAntiAlias = true
    ..color = Colors.black12;

  List<Offset> points = _getIconOffsets();
  for (int i = 0; i < points.length - 1; i++) {
    if (i % 2= =0) {
      canvas.drawLine(points[i], points[i + 1], paint); }}}Copy the code

3. Feedback on correct or incorrect results

The last part is that you need to have an edge glow after dragging right and wrong;

Edge flicker glow, in the final analysis is the combination of edge path + flicker + glow; Edge paths we’ve already done, so let me draw them with path; Flicker is also relatively simple, add a fade in and out animation is OK; All that’s left is the glow effect.

The luminous effect is to blur the edge path with color value. So you have an astigmatism effect; For paint, you can set a maskFilter. Use this paint to draw the path; This completes a glow effect;

paint.maskFilter = MaskFilter.blur(BlurStyle.outer, 25); 
paint.style = PaintingStyle.fill;
Copy the code

So here are the main key steps; But there are more details involved in it that are not detailed; If you are interested, you can read the source code or comment exchange. Finally, summarize the technical points needed to achieve such a jigsaw effect.

conclusion

To sum up, in fact, do this puzzle mainly involves the following technical points, master, the rest is some details.

  • Irregular graph drawing: Extract SVG Path, then draw Path & dotted line;
  • Cropped irregular images: Use BlendMode to crop layers.
  • Drag logic: Draggable & DragTarget;
  • Luminous edge: MaskFilter;

Source: github.com/linkaipeng/…