Flutter로 누르면 파문이 일어납니다.

32208 단어 FlutterDarttech

개시하다


스마트폰 게임에서는 가볍게 두드리는 곳에 어떤 효과가 있는지 자주 볼 수 있는 UI.
이번에 그거 해보니까 소개할게요.
Animation 사용법에 익숙하지 않기 때문에 장황하게 쓸 수 있습니다.
더 좋은 방법이 있으면 알려주세요!
상세한 해설이 코드로 넘어가면 본문의 구성이 비교적 적다

만들어진 물건


방침.


대체로 아래의 순서에 따라 실시하였다.
각각의 절차에 대해 샘플 코드 등을 섞어 설명한다.

  • 헤드의 위치를 감지하다

  • 탭 위치에서 원 루프 확장하기

  • 휠 애니메이션 사용하기

  • 빙글빙글 돌게 하다
  • 1. 헤드셋의 위치 측정


    Gesture Detector 사용


    Widget build(BuildContext context) {
      return GestureDetector(
          // onTap では position はとれないけれど、onTapDown ならとれる。
          // 他にもいろいろな動きをとれて便利です。
          // https://api.flutter.dev/flutter/widgets/GestureDetector-class.html
          onTapDown: (details) => print(details.globalPosition),
          child: const Scaffold(),
      );
    }
    

    2. 탭 위치에서 링 펴기


    Custom Paint 사용


    코드
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      
      Widget build(BuildContext context) =>
          MaterialApp(theme: ThemeData(), home: MyHomePage());
    }
    
    class MyHomePage extends StatefulWidget {
      
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      
      Widget build(BuildContext context) {
        return GestureDetector(
          onTapDown: ripplePointer, // 引数が同じだとこうやって短く書ける
          child: Scaffold(
            body: offset != null
                ? CustomPaint(painter: RipplePainter(offset: offset))
                : const SizedBox(),
          ),
        );
      }
    
      Offset offset;
      void ripplePointer(TapDownDetails details) {
        // 単純に offset を更新しているだけ
        setState(() {
          offset = details.globalPosition;
          print(details.globalPosition);
        });
      }
    }
    
    class RipplePainter extends CustomPainter {
      RipplePainter({ this.offset});
      final Offset offset;
      
      void paint(Canvas canvas, Size size) {
        final paint = Paint()
          ..style = PaintingStyle.stroke // 図形をぬりつずつかどうか
          ..color = Colors.blue // 色の指定
          ..strokeWidth = 2; // 線の太さの指定
        canvas.drawCircle(offset, 100, paint); // 位置と輪っかの大きさを指定
      }
    
      
      bool shouldRepaint(CustomPainter oldDelegate) => true;
    }
    

    3. 라운드 애니메이션 재생


    Custom Paint에 Animation Controller 만들기


    아직 동작이 없어서 외로워요.
    이제 애니메이션을 시작합니다.
    코드
    class RipplePointer extends StatefulWidget {
      const RipplePointer({Key key,  this.offset,  this.duration})
          : super(key: key);
      final Offset offset;
      final Duration duration;
      
      _RipplePointerState createState() => _RipplePointerState();
    }
    
    class _RipplePointerState extends State<RipplePointer>
        with SingleTickerProviderStateMixin {
      // これを忘れずに
    
      
      Widget build(BuildContext context) {
        return CustomPaint(
          painter: RipplePainter(controller: controller, offset: widget.offset),
        );
      }
    
      AnimationController controller;
      
      void initState() {
        super.initState();
        // SingleTickerProviderStateMixin を使うことで, vsync に this で渡せる。
        // これによりアニメーションの変化に合わせて画面が再描画されていく。
        controller = AnimationController(vsync: this, duration: widget.duration);
        controller.forward(); // アニメーションをスタート
      }
    
      
      void dispose() {
        controller.dispose();
        super.dispose();
      }
    }
    
    class RipplePainter extends CustomPainter {
      RipplePainter({ this.controller,  this.offset})
          : super(repaint: controller); // repaint に controller を渡さないと再描画されない
      final Offset offset;
      final Animation<double> controller;
    
      
      void paint(Canvas canvas, Size size) {
        /// ```dart
        /// Tween<T>(begin: ,end: ) // はじめとおわりの値を指定できる
        /// CurveTween(curve: ) // Curves. でアニメーションカーブを指定できる
        /// ```
        /// - 輪っかの大きさ
        /// - 線の太さ
        /// - 透明度
        /// この 3 つをアニメーションで制御している。
        final circleValue = Tween<double>(begin: 8, end: 80)
            .animate(controller.drive(CurveTween(curve: Curves.easeOutExpo)))
            .value;
        final widthValue = Tween<double>(begin: 12, end: 2)
            .animate(controller.drive(CurveTween(curve: Curves.easeInOut)))
            .value;
        final opacityValue = Tween<double>(begin: 1, end: 0)
            .animate(controller.drive(CurveTween(curve: Curves.easeInOut)))
            .value;
    
        final paint = Paint()
          ..style = PaintingStyle.stroke
          ..color = Colors.blue.withOpacity(opacityValue)
          ..strokeWidth = widthValue;
        canvas.drawCircle(offset, circleValue, paint);
      }
    
      
      bool shouldRepaint(CustomPainter oldDelegate) => true;
    }
    
    

    4. 동그라미 노출


    이 의외의 어려움은 지금까지도 아직 가장 적합한 해를 모른다.
    Stack에 리스트를 넘기면 여러 개의 물결이 생기고 애니메이션이 끝나면 remove를 할 수 있습니다.
    예를 들어 Unity 같은 거,destroy나generate 같은 거 있는데 화면에서 대상을 생성하거나 폐기하는 건 간단하지만 Flutter는 어떻게 설치하는 게 좋을까요?
    코드
    class MyHomePage extends StatefulWidget {
      
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      
      Widget build(BuildContext context) {
        return GestureDetector(
          onTapDown: generateRipplePointer,
          child: Scaffold(
            body: Stack(children: ripplePointerList),
          ),
        );
      }
    
      // RipplePointer のリストをつくってタップと同時にそこに追加している
      // アニメーションの終了を Future<void>.delayed で待ち、終わった時に removeAt(0) でリストから取り出している
      // 取り出すと そのタイミングで dispose が呼ばれる。
      List<RipplePointer> ripplePointerList = <RipplePointer>[];
      Future<void> generateRipplePointer(TapDownDetails details) async {
        const duration = const Duration(milliseconds: 800);
        final ripplePointer = RipplePointer(
          key: UniqueKey(), // 必ずキーを与えること。これによりそれぞれが独立した描画になります。
          offset: details.globalPosition,
          duration: duration,
        );
        setState(() {
          ripplePointerList.add(ripplePointer);
        });
        await Future<void>.delayed(duration);
        setState(() {
          ripplePointerList.removeAt(0);
        });
      }
    }
    

    전체 이미지


    애니메이션 곡선, 선 굵기, 크기 등 매개 변수를 하세요!
    코드 전체 텍스트
    그럼 어디서 뵐까요?

    좋은 웹페이지 즐겨찾기