前幾天發現了一個使用 <canvas>
繪製圖形的教程 generative artistry 感受頗有意思,嘗試用 Flutter 實現。本文實現第一篇教程的圖形 Tiled Lines 效果以下。web
首先使用一個 Container
控件建立一個 320*320
大小的繪製區域,添加 CustomPaint
畫布和一個繼承 CustomPainter
的畫筆 TiledLinesPainter
。關於 CustomPaint
和 CustomPainter
的知識能夠查閱這篇文章 使用 Flutter 繪製圖表(一)柱狀圖📊。canvas
import 'package:flutter/material.dart';
class TiledLines extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Container( width: 320.0, height: 320.0, decoration: BoxDecoration( border: Border.all( color: Colors.black, width: 1.0, ), ), child: CustomPaint( painter: TiledLinesPainter(), ), ), ), ); } } class TiledLinesPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) {} bool shouldRepaint(TiledLinesPainter oldDelegate) => false; } 複製代碼
建立好畫布後在 TiledLinesPainter
的 paint
方法裏進行繪製。添加一個 _drawLine
方法用來繪製線條,繪製線條須要起始點和終止點,經過參數將數值傳入。less
class TiledLinesPainter extends CustomPainter {
void _drawLine( Canvas canvas, double x, double y, double width, double height, ) { final Paint paint = Paint() ..strokeCap = StrokeCap.square ..strokeWidth = 2; Offset p1 = Offset(x, y); Offset p2 = Offset(x + width, y + height); canvas.drawLine(p1, p2, paint); } @override void paint(Canvas canvas, Size size) { _drawLine(canvas, 0, 0, size.width, size.height); } bool shouldRepaint(TiledLinesPainter oldDelegate) => false; } 複製代碼
使用 Random().nextBool()
方法建立一個隨機的布爾值,在繪製線條以前改變起始點和終止點的座標,這樣 _drawLine
方法就有了繪製不一樣方向的線條的能力。dom
void _drawLine(
Canvas canvas, double x, double y, double width, double height, ) { final bool isLeftToRight = Random().nextBool(); final Paint paint = Paint() ..strokeCap = StrokeCap.square ..strokeWidth = 2; Offset p1; Offset p2; if (isLeftToRight) { p1 = Offset(x, y); p2 = Offset(x + width, y + height); } else { p1 = Offset(x + width, y); p2 = Offset(x, y + height); } canvas.drawLine(p1, p2, paint); } 複製代碼
能夠繪製更多的線條嘍!給 TiledLinesPainter
添加一個 step
屬性,表示在畫布上每隔多長距離繪製一條線。使用 step
將畫布分割爲多個小的方格,在每一個小的方格里面繪製線條。編輯器
class TiledLinesPainter extends CustomPainter {
final double step; TiledLinesPainter(this.step); void _drawLine( Canvas canvas, double x, double y, double width, double height, ) { final bool isLeftToRight = Random().nextBool(); final Paint paint = Paint() ..strokeCap = StrokeCap.square ..strokeWidth = 2; Offset p1; Offset p2; if (isLeftToRight) { p1 = Offset(x, y); p2 = Offset(x + width, y + height); } else { p1 = Offset(x + width, y); p2 = Offset(x, y + height); } canvas.drawLine(p1, p2, paint); } @override void paint(Canvas canvas, Size size) { for (double x = 0; x < size.width; x += step) { for (double y = 0; y < size.height; y += step) { _drawLine(canvas, x, y, step, step); } } } bool shouldRepaint(TiledLinesPainter oldDelegate) => false; } //... TiledLinesPainter(20) 複製代碼
最後給畫布添加邊框和陰影效果,大功告成!👏 感謝閱讀。ide
import 'dart:math';
import 'package:flutter/material.dart'; class TiledLines extends StatelessWidget { @override Widget build(BuildContext context) { List<BoxShadow> shadows = []; double opacity = 0.1; // 添加畫布陰影 for (double i = 1; i <= 16; i++) { opacity -= 0.01; opacity = opacity > 0.01 ? opacity : 0.01; shadows.add( BoxShadow( offset: Offset(-i, i), color: Color.fromRGBO(0, 0, 0, opacity), blurRadius: 2, spreadRadius: 1, ), ); } return Scaffold( body: Center( child: Container( width: 320.0, height: 320.0, decoration: BoxDecoration( // 添加畫布邊框 border: Border.all( color: Colors.black, width: 20.0, ), boxShadow: shadows, ), child: Container( color: Colors.white, padding: const EdgeInsets.all(20.0), child: CustomPaint( painter: TiledLinesPainter(20), ), ), ), ), ); } } class TiledLinesPainter extends CustomPainter { final double step; TiledLinesPainter(this.step); void _drawLine( Canvas canvas, double x, double y, double width, double height, ) { // 建立隨機性 final bool isLeftToRight = Random().nextBool(); final Paint paint = Paint() ..strokeCap = StrokeCap.square ..strokeWidth = 2; Offset p1; Offset p2; // 設置線條的起始點和終止點 if (isLeftToRight) { p1 = Offset(x, y); p2 = Offset(x + width, y + height); } else { p1 = Offset(x + width, y); p2 = Offset(x, y + height); } canvas.drawLine(p1, p2, paint); } @override void paint(Canvas canvas, Size size) { // 使用 step 分割畫布,建立小的繪製方格 for (double x = 0; x < size.width; x += step) { for (double y = 0; y < size.height; y += step) { _drawLine(canvas, x, y, step, step); } } } bool shouldRepaint(TiledLinesPainter oldDelegate) => false; } 複製代碼
Tiled Linespost
本文使用 mdnice 排版ui