|
1 | 1 | import 'dart:async'; |
| 2 | +import 'dart:math'; |
| 3 | +import 'dart:ui'; |
2 | 4 |
|
3 | 5 | import 'package:flutter/material.dart'; |
4 | 6 | import 'package:tutorial_coach_mark/src/paint/light_paint.dart'; |
@@ -365,11 +367,31 @@ class AnimatedPulseFocusLightState extends AnimatedFocusLightState { |
365 | 367 | } |
366 | 368 | return Stack( |
367 | 369 | children: <Widget>[ |
368 | | - SizedBox( |
369 | | - width: double.maxFinite, |
370 | | - height: double.maxFinite, |
371 | | - child: CustomPaint( |
372 | | - painter: _getPainter(_targetFocus), |
| 370 | + ClipPath( |
| 371 | + clipper: _targetFocus.shape == ShapeLightFocus.RRect |
| 372 | + ? MyRectClipper( |
| 373 | + progress: _progressAnimated, |
| 374 | + offset: _getPaddingFocus(), |
| 375 | + target: _targetPosition ?? |
| 376 | + TargetPosition(Size.zero, Offset.zero), |
| 377 | + radius: _targetFocus.radius ?? 0, |
| 378 | + borderSide: _targetFocus.borderSide, |
| 379 | + ) |
| 380 | + : MyCircleClipper( |
| 381 | + _progressAnimated, |
| 382 | + _positioned, |
| 383 | + _sizeCircle, |
| 384 | + _targetFocus.borderSide, |
| 385 | + ), |
| 386 | + child: BackdropFilter( |
| 387 | + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), |
| 388 | + child: SizedBox( |
| 389 | + width: double.maxFinite, |
| 390 | + height: double.maxFinite, |
| 391 | + child: CustomPaint( |
| 392 | + painter: _getPainter(_targetFocus), |
| 393 | + ), |
| 394 | + ), |
373 | 395 | ), |
374 | 396 | ), |
375 | 397 | Positioned( |
@@ -482,3 +504,101 @@ class AnimatedPulseFocusLightState extends AnimatedFocusLightState { |
482 | 504 | ); |
483 | 505 | } |
484 | 506 | } |
| 507 | + |
| 508 | +class MyCircleClipper extends CustomClipper<Path> { |
| 509 | + final double progress; |
| 510 | + final Offset positioned; |
| 511 | + final double sizeCircle; |
| 512 | + final BorderSide? borderSide; |
| 513 | + |
| 514 | + MyCircleClipper( |
| 515 | + this.progress, |
| 516 | + this.positioned, |
| 517 | + this.sizeCircle, |
| 518 | + this.borderSide, |
| 519 | + ); |
| 520 | + |
| 521 | + @override |
| 522 | + Path getClip(Size size) { |
| 523 | + if (positioned == Offset.zero) return Path(); |
| 524 | + var maxSize = max(size.width, size.height); |
| 525 | + |
| 526 | + double radius = maxSize * (1 - progress) + sizeCircle; |
| 527 | + final circleHole = Path() |
| 528 | + ..moveTo(0, 0) |
| 529 | + ..lineTo(0, positioned.dy) |
| 530 | + ..arcTo( |
| 531 | + Rect.fromCircle(center: positioned, radius: radius), |
| 532 | + pi, |
| 533 | + pi, |
| 534 | + false, |
| 535 | + ) |
| 536 | + ..arcTo( |
| 537 | + Rect.fromCircle(center: positioned, radius: radius), |
| 538 | + 0, |
| 539 | + pi, |
| 540 | + false, |
| 541 | + ) |
| 542 | + ..lineTo(0, positioned.dy) |
| 543 | + ..lineTo(0, size.height) |
| 544 | + ..lineTo(size.width, size.height) |
| 545 | + ..lineTo(size.width, 0) |
| 546 | + ..close(); |
| 547 | + return circleHole; |
| 548 | + } |
| 549 | + |
| 550 | + @override |
| 551 | + bool shouldReclip(covariant MyCircleClipper oldClipper) { |
| 552 | + return progress != oldClipper.progress; |
| 553 | + } |
| 554 | +} |
| 555 | + |
| 556 | +class MyRectClipper extends CustomClipper<Path> { |
| 557 | + final double progress; |
| 558 | + final TargetPosition target; |
| 559 | + final double offset; |
| 560 | + final double radius; |
| 561 | + final BorderSide? borderSide; |
| 562 | + |
| 563 | + MyRectClipper({ |
| 564 | + required this.progress, |
| 565 | + required this.target, |
| 566 | + required this.offset, |
| 567 | + required this.radius, |
| 568 | + this.borderSide, |
| 569 | + }); |
| 570 | + |
| 571 | + @override |
| 572 | + Path getClip(Size size) { |
| 573 | + if (target.offset == Offset.zero) return Path(); |
| 574 | + |
| 575 | + var maxSize = max(size.width, size.height) + |
| 576 | + max(target.size.width, target.size.height) + |
| 577 | + target.getBiggerSpaceBorder(size); |
| 578 | + |
| 579 | + double x = -maxSize / 2 * (1 - progress) + target.offset.dx - offset / 2; |
| 580 | + |
| 581 | + double y = -maxSize / 2 * (1 - progress) + target.offset.dy - offset / 2; |
| 582 | + |
| 583 | + double w = maxSize * (1 - progress) + target.size.width + offset; |
| 584 | + |
| 585 | + double h = maxSize * (1 - progress) + target.size.height + offset; |
| 586 | + return Path() |
| 587 | + ..moveTo(0, 0) |
| 588 | + ..lineTo(0, y) |
| 589 | + ..lineTo(x + w, y) |
| 590 | + ..lineTo(x + w, y + h) |
| 591 | + ..lineTo(x, y + h) |
| 592 | + ..lineTo(x, y) |
| 593 | + ..lineTo(0, y) |
| 594 | + ..lineTo(0, size.height) |
| 595 | + ..lineTo(size.width, size.height) |
| 596 | + ..lineTo(size.width, 0) |
| 597 | + ..close(); |
| 598 | + } |
| 599 | + |
| 600 | + @override |
| 601 | + bool shouldReclip(covariant MyRectClipper oldClipper) { |
| 602 | + return progress != oldClipper.progress; |
| 603 | + } |
| 604 | +} |
0 commit comments