Как реализовать функцию рисованного благословения Alipay Wufu (включая операцию отмены) с помощью Flutter?

внешний интерфейс Android Flutter
Как реализовать функцию рисованного благословения Alipay Wufu (включая операцию отмены) с помощью Flutter?

PK Creative празднует Китайский Новый год. Я участвую в "Творческом конкурсе Весеннего фестиваля". Подробности см.:Творческий конкурс "Праздник весны"

Я проснулся сегодня утром и обнаружил, что снова началось ежегодное мероприятие Alipay, посвященное пяти благословениям. Оно включает в себя функцию рукописного благословения символов, в том числе отмену штриха, очистку перезаписи, сохранение альбомов и т. д. Итак, как мне использовать Flutter для достижения этих функций? Шерстяная ткань?

нужно

Конкретные требования включают:

  • В интерфейсе отображается пешеходная дорожка по мере скольжения пальца пользователя, то есть соответствующий ход.
  • Нажмите кнопку «Очистить», чтобы очистить все штрихи.
  • Нажмите кнопку «Отменить», чтобы удалить штрихи, нарисованные на предыдущем шаге.
  • Сохраните стиль письменного текста в фотоальбом.

Реализовать идеи

Показать дорожку обводки

использоватьListenerКомпонент отслеживает действия пальца пользователя при опускании, скольжении и втягивании.onPointerDown,onPointerMove,onPointerUpВозвращается в 3 методах слушателяPointerMoveEventОбъект содержит смещение координат положения пальцаlocalPosition, каждый раз, когда пользователь скользит, точки координат, через которые проходит траектория, будут записываться, и эти точки координат соединяются в линию. Во-вторых, используйте его вместеCustomPainterДля самостоятельного рисования холста соедините все нарисованные точки в линию и используйте кисть для рисования на интерфейсе.

Соберите точки координат:

Listener(
  child: Container(
    alignment: Alignment.center,
    color: Colors.transparent,
    width: double.infinity,
    height: MediaQuery.of(context).size.height,
  ),
  onPointerDown: (PointerDownEvent event) {
    setState(() {
      
    });
  },
  onPointerMove: (PointerMoveEvent event) {
    setState(() {
      
    });
  },
  onPointerUp: (PointerUpEvent event) {
    setState(() {
      
    });
  },
),

рисовать:

@override
void paint(Canvas canvas, Size size) {
  myPaint.strokeCap = StrokeCap.round;
  myPaint.strokeWidth = 15.0;
  if (lines.isEmpty) {
    canvas.drawPoints(PointMode.polygon, [Offset.zero, Offset.zero], myPaint);
  } else {
    for (int k = 0; k < lines.length; k++) {
      for (int i = 0; i < lines[k].length - 1; i++) {
        if (lines[k][i] != Offset.zero && lines[k][i + 1] != Offset.zero) {
          canvas.drawLine(lines[k][i], lines[k][i + 1], myPaint);
        }
      }
    }
  }
}

Отменить и очистить

图片

Некоторые люди, которые видят приведенный выше код, могут быть более сбиты с толку, почему это так сложно рисовать, и есть двойной цикл. Это связано с функцией отмены. Предположим, что функция отмены не нужна. На самом деле, мы можем напрямую соединить точки всех штрихов, чтобы рисовать вместе, но как только функция отмены введена, мы должны записывать каждый штрих. 13 штрихов, поэтому теоретически необходимо записать 13 штрихов, чтобы последний почерк мог нормально возвращаться каждый раз при отмене, поэтому первая реакция — использовать коллекцию для записи каждого штриха. Выше также сказано, что каждый штрих на самом деле является набором нескольких координатных точек, поэтому все штрихи представляют собой набор наборов координатных точек, а именно:

/// 所有笔画划线集合
List<List<Offset>> _lines = [];

Кроме того, нетрудно подумать, что мы можем легко различить начало и конец штриха с помощью обратного вызова метода нажатия пальца и пальца пальца. Добавляйте и обновляйте штрихи двумя способами.

onPointerDown: (PointerDownEvent event) {
  setState(() {
    _event = event;
    _points.add(_event?.localPosition ?? Offset.zero);
    _lines.add(_points);
  });
},
onPointerMove: (PointerMoveEvent event) {
  setState(() {
    _event = event;
    _points.add(_event?.localPosition ?? Offset.zero);
    _lines.last = _points;
  });
},
onPointerUp: (PointerUpEvent event) {
  setState(() {
    _event = event;
    _points.add(Offset.zero);
    _lines.last = _points;
  });
  _points = [];
},

Двойной обход, упомянутый ранее, также легче понять в настоящее время:

  • Первый слой цикла должен пройти все штрихи, а количество обходов — это количество штрихов символа Фу.
  • Второй слой цикла представляет собой количество координатных точек, входящих в каждый ход, проходимых и используемых.drawLineМетод рисует линию к интерфейсу.

Таким образом, когда выполняется операция отмены, вызов метода removeLast списка для удаления последнего элемента, а затем обновление интерфейса может привести к возврату штриха. Empty означает очистку коллекции штрихов.

сохранить в альбом

Сохранение альбомов в основном представляет две подключаемые библиотеки:permission_handlerа такжеimage_gallery_saver, один используется для получения разрешения на хранение, а другой используется для сохранения в альбом. использоватьRepaintBoundaryКомпоненты холста завернуты, и указав ключ, вызовите следующий метод для того, чтобы при нажатии кнопки Сохранить сохранить снимок экрана:

RenderRepaintBoundary boundary =
    key.currentContext!.findRenderObject() as RenderRepaintBoundary;
var image = await boundary.toImage(pixelRatio: 3.0);
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
_postBytes = byteData?.buffer.asUint8List();
var result = await ImageGallerySaver.saveImage(_postBytes!);

Полный код и демонстрация

гитхаб-адрес

Загрузка кода сканирования телефона Android

Категории