Flutter在CustomPaint中绘制SVG(画布)

时间:2019-09-10 15:54:34

标签: svg flutter dart paint

我有这样的东西:

CustomPaint(
       painter: CurvePainter(),
)

这堂课我在画画:

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import './myState.dart';
import './models/mode.dart';
final String rawSvg = '''<svg viewBox="...">...</svg>''';

class CurvePainter extends CustomPainter {
  MyState _myState;
  DrawableRoot svgRoot;
  CurvePainter(MyState myState) {
    this._myState = myState;
    this.loadAsset();
  }

  void loadAsset() async {
    this.svgRoot = await svg.fromSvgString(rawSvg, rawSvg);// The canvas that is your board.
  }

  @override
  void paint (Canvas canvas, Size size) {
    canvas.translate(_myState.translateX, _myState.translateY);
    if(this.svgRoot != null){
        svgRoot.scaleCanvasToViewBox(canvas, size);
        svgRoot.clipCanvasToViewBox(canvas);
        // svgRoot.draw(canvas, size);
    }
}

有人知道如何在内部绘画方法中绘制SVG吗? 我找到了这个库https://pub.dev/packages/flutter_svg#-readme-tab-。 使用我的代码,我得到错误:未处理的异常:错误的状态:viewBox元素必须为4个元素长

如果可以缩放和旋转画布内的svg,我会很好。但这是可选的。

3 个答案:

答案 0 :(得分:3)

最终,我发现直接在Canvas中绘制SVG很麻烦。相反,我使用path_drawing复制了SVG路径并转换为Dart代码,并使用Path将其渲染为Canvas.drawPath。这样做的好处是根本不算是资产。此时,SVG数据实际上就是代码。而且,您可以轻松地转换回SVG。这个过程有点像这样:

  1. 在您从path_drawing: 0.4.1渲染的文件中,将pubspec.yaml添加到flutter pub get的{​​{1}}中。
  2. 使用方法import 'package:path_drawing/path_drawing.dart';从SVG复制并粘贴所有路径作为路径常量。 (路径字符串看起来像parseSvgPathData
    • 如果SVG中有多个路径,则可以组合多个路径:
      • M 86.102000,447.45700 L 86.102000,442.75300 .....
  3. 通常,SVG将被包裹在某些翻译中(static final Path complexPathToDraw = parseSvgPathData("path_1").addPath(parseSvgPathData("path_2"));)。 <g transform='translate()>只能从左上角渲染路径。因此,您必须平移到合适的位置。渲染时,请先对画布进行平移,然后再进行绘制(1),以更正SVG中的平移,(2)旁边,缩放至所需大小,(3)转到画布上您真正想要的位置。然后绘制,并将“画布”还原到其未变形状态。但是请记住,由于线性代数是愚蠢的,因此这些矩阵以相反的顺序添加到我们如何在逻辑上进行分解。
    • drawPath
    canvas.save();
    canvas.translate(dxToRenderPosition, dyToRenderPosition);
    canvas.scale(sxFromSvgSizeToDesiredRenderSize);
    canvas.translate(dxFromSvg, dyFromSvg);
    canvas.drawPath(complexPathToDraw, Paint());
    canvas.restore();
    

答案 1 :(得分:2)

来自README

import 'package:flutter_svg/flutter_svg.dart';
final String rawSvg = '''<svg viewBox="...">...</svg>''';
final DrawableRoot svgRoot = await svg.fromSvgString(rawSvg, rawSvg);

// If you only want the final Picture output, just use
final Picture picture = svgRoot.toPicture();

// Otherwise, if you want to draw it to a canvas:
// Optional, but probably normally desirable: scale the canvas dimensions to
// the SVG's viewbox
svgRoot.scaleCanvasToViewBox(canvas);

// Optional, but probably normally desireable: ensure the SVG isn't rendered
// outside of the viewbox bounds
svgRoot.clipCanvasToViewBox(canvas);
svgRoot.draw(canvas, size);

您可以采用以下哪种方式

class CurvePainter extends CustomPainter {
  CurvePainter(this.svg);

  final DrawableRoot svg;
  @override
  void paint(Canvas canvas, Size size) {
       canvas.drawLine(...);
       svg.scaleCanvasToViewBox(canvas);
       svg.clipCanvasToViewBox(canvas);
       svg.draw(canvas, size);
  }
}

我建议您找到一种方法,可以使用FutureBuilder或ValueListenableBuilder在您的应用程序中更早地获取异步部分。

披露:我是Flutter SVG的作者/主要维护者。

答案 2 :(得分:0)

我遇到了类似的问题,我想在 canvas 的一小部分上绘制一个按比例缩小的 Svg。

为了让它工作,我不得不使用这个代码:

Size desiredSize = Size(60, 40);
// get the svg from a preloaded array of DrawableRoot corresponding to all the Svg I might use
final DrawableRoot svgRoot = drawables[i];
canvas.save();
// [center] below is the Offset of the center of the area where I want the Svg to be drawn
canvas.translate(center.dx - desiredSize.width / 2, center.dy - desiredSize.height / 2);
Size svgSize = svgRoot.viewport.size;
var matrix = Matrix4.identity();
matrix.scale(desiredSize.width / svgSize.width, desiredSize.height / svgSize.height);
canvas.transform(matrix.storage);
svgRoot.draw(canvas, Rect.zero); // the second argument is not used in DrawableRoot.draw() method
canvas.restore();

通过这种方式,您可以在画布上渲染复杂的 Svg,并且仍然可以对其进行一些工作。

例如:您可以在同一个画布上绘制多个 Svg 并在它们上进行绘制。