flutter 围绕中心旋转绘制的文本

nnsrf1az  于 2023-02-09  发布在  Flutter
关注(0)|答案(2)|浏览(216)

我试图旋转画布上绘制的文本的中心。相反,在下面的代码中,当我按下浮动按钮时,文本将围绕文本的左上角旋转。
按下按钮可增加Angular ,该Angular 将传递给CanvasPainter以绘制文本。
矩形的左上角最初应定位在offset

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double _angle = 0;
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: CustomPaint(
            painter: CanvasPainter(_angle),
            child: Container(),
          )
         ),
        appBar: AppBar(title: Text('Test')),
        floatingActionButton: FloatingActionButton(
          onPressed: () => setState(() => _angle += .1),
          child: const Icon(Icons.add),
      )
      ),
    );
  }
}

class CanvasPainter extends CustomPainter {
  final double angle;
  final Offset offset = Offset(50, 50);
  
  CanvasPainter(this.angle);
  
  @override
  void paint(Canvas canvas, Size size) {
    final fill = TextPainter(
      text: TextSpan(text: 'This is a test', style: TextStyle(fontSize: 80)),
      textDirection: TextDirection.rtl);
    
    fill.layout();
    
    canvas.save();
    //canvas.translate(-fill.width/2, -fill.height/2);
    canvas.rotate(angle);
    canvas.translate(offset.dx, offset.dy);
      
    fill.paint(canvas, Offset.zero);
    
    canvas.restore();
  }
  
  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}
yhqotfr8

yhqotfr81#

这是你必须要做的

[...]
canvas.save();
final pivot = fill.size.center(offset); 
canvas.translate(pivot.dx, pivot.dy); 
canvas.rotate(angle);
canvas.translate(-pivot.dx, -pivot.dy);
fill.paint(canvas, offset);
canvas.restore();
enxuqcxy

enxuqcxy2#

更新:旋转文本、下标和上标

import 'package:flutter/material.dart';
import 'dart:math' as math;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter += 0.1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            CustomPaint(
              painter: CustomText(multiplier: _counter),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class CustomText extends CustomPainter {
  CustomText({required this.multiplier});

  double multiplier;

  // Text centre about which the text should rotate
  final textCentre = const Offset(0, 0);

  // Angle of rotation
  double get theta => -math.pi * multiplier;

  // Text styles
  double textFontSize = 24;
  double get subTextFontSize => textFontSize / 2;
  double get supTextFontSize => textFontSize / 2;

  TextStyle get textStyle => TextStyle(
      color: Colors.red, fontSize: textFontSize, fontStyle: FontStyle.italic);

  TextStyle get subTextStyle => TextStyle(
      color: Colors.green,
      fontSize: subTextFontSize,
      fontStyle: FontStyle.italic);

  TextStyle get supTextStyle => TextStyle(
      color: Colors.cyan,
      fontSize: subTextFontSize,
      fontStyle: FontStyle.italic);

  @override
  void paint(Canvas canvas, Size size) {
    //
    // MAIN TEXT //
    final textPainter = TextPainter(
      text: TextSpan(text: 'Example text', style: textStyle),
      textDirection: TextDirection.ltr,
    );

    textPainter.layout();

    // Calculate delta offset with reference to which any text should
    // paint, such that the centre of the text will always be
    // at the given textCentre
    final delta = Offset(textCentre.dx - textPainter.size.width / 2,
        textCentre.dy - textPainter.size.height / 2);

    // Rotate the text about textCentrePoint
    canvas.save();
    canvas.translate(textCentre.dx, textCentre.dy);
    canvas.rotate(theta);
    canvas.translate(-textCentre.dx, -textCentre.dy);
    textPainter.paint(canvas, delta);
    canvas.restore();
    //

    // SUBSCRIPT TEXT //
    final subTextPainter = TextPainter(
      text: TextSpan(
        text: 'subscript',
        style: subTextStyle,
      ),
      textDirection: TextDirection.ltr,
    );

    subTextPainter.layout();

    // Position of top left point of the subscript text
    final deltaSubtextDx = textCentre.dx +
        // Cos
        math.cos(theta) * (textPainter.size.width / 2) +
        // Sine
        math.sin(theta) *
            (-textPainter.size.height / 2 + subTextPainter.size.height / 2);

    final deltaSubtextDy = textCentre.dy +
        // Cos
        math.cos(theta) *
            (textPainter.size.height / 2 - subTextPainter.size.height / 2) +
        // Sine
        math.sin(theta) * (textPainter.size.width / 2);

    final deltaSubText = Offset(deltaSubtextDx, deltaSubtextDy);

    // Rotate the text about textCentrePoint
    canvas.save();
    canvas.translate(deltaSubText.dx, deltaSubText.dy);
    canvas.rotate(theta);
    canvas.translate(-deltaSubText.dx, -deltaSubText.dy);
    subTextPainter.paint(canvas, deltaSubText);
    canvas.restore();
    //

    // SUPERSCRIPT TEXT //
    final supTextPainter = TextPainter(
      text: TextSpan(
        text: 'superscript',
        style: supTextStyle,
      ),
      textDirection: TextDirection.ltr,
    );

    supTextPainter.layout();

    // Position of top left point of the superscript text
    final deltaSuptextDx = textCentre.dx +
        // Cos
        math.cos(theta) * (textPainter.size.width / 2) +
        // Sine
        math.sin(theta) *
            (textPainter.size.height / 2 + supTextPainter.size.height / 2);

    final deltaSuptextDy = textCentre.dy +
        // Cos
        math.cos(theta) *
            (-textPainter.size.height / 2 - supTextPainter.size.height / 2) +
        // Sine
        math.sin(theta) * (textPainter.size.width / 2);

    final deltaSupText = Offset(deltaSuptextDx, deltaSuptextDy);

    // Rotate the text about textCentrePoint
    canvas.save();
    canvas.translate(deltaSupText.dx, deltaSupText.dy);
    canvas.rotate(theta);
    canvas.translate(-deltaSupText.dx, -deltaSupText.dy);
    supTextPainter.paint(canvas, deltaSupText);
    canvas.restore();
    //

    // Centre point marker
    final pointPaint = Paint()..color = Colors.blue;
    canvas.drawCircle(textCentre, 4, pointPaint);
    // Subscript point marker
    final pointPaint2 = Paint()..color = Colors.orange;
    canvas.drawCircle(deltaSubText, 4, pointPaint2);
    // Superscript point marker
    final pointPaint3 = Paint()..color = Colors.brown;
    canvas.drawCircle(deltaSupText, 4, pointPaint3);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

原始答案

上述代码的问题是,如果字符数发生变化,文本将在全局X方向上移动。
Image with long text
Image with short text
为了定位并旋转一个动态文本,我稍微修改了代码,使文本围绕一个给定的中心点旋转。红点在中心点。
Text rotated about given offset/center point
如果你想围绕它的底部中心点旋转文本,那么只需在文本字符串的末尾添加新的行字符。
Rotated about bottom center of the text

class NewPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var text = 'You have pushed the button this many times:\n'; //Remove \n if you don't want to rotate about bottom centre

    // I want my text centred at this point
    const textCentrePoint = Offset(200, 300);

    const textStyle1 = TextStyle(color: Colors.black, fontSize: 20);

    final pointPaint = Paint()..color = const Color.fromARGB(255, 255, 0, 0);

    // Rotated text
    final textPainter1 = TextPainter(
      text: TextSpan(text: text, style: textStyle1),
      textDirection: TextDirection.ltr,
    );
    textPainter1.layout();

    // Calculate delta offset with reference to which any text should
    // paint, such that the centre of the text will be
    // at the given textCentrePoint
    final delta = Offset(textCentrePoint.dx - textPainter1.size.width / 2,
        textCentrePoint.dy - textPainter1.size.height / 2);

    // Rotate the text about textCentrePoint

    canvas.save();
    canvas.translate(textCentrePoint.dx, textCentrePoint.dy);
    canvas.rotate(-pi / 2);
    canvas.translate(-textCentrePoint.dx, -textCentrePoint.dy);
    textPainter1.paint(canvas, delta);
    canvas.restore();

    canvas.drawCircle(textCentrePoint, 4, pointPaint);

    // Normal horizontal text
    const textStyle2 =
        TextStyle(color: Color.fromARGB(255, 139, 0, 0), fontSize: 20);

    final textPainter2 = TextPainter(
      text: TextSpan(text: text, style: textStyle2),
      textDirection: TextDirection.ltr,
    );
    textPainter2.layout();
    textPainter2.paint(canvas, delta);
  }

相关问题