Flutter 在 CustomPaint 中绘制 SVG (Canvas)
Flutter draw SVG in CustomPaint (Canvas)
我有这样的东西:
CustomPaint(
painter: CurvePainter(),
)
在这个class我正在画我的画:
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);
}
}
有人知道如何在 paint 方法中绘制 SVG 吗?
我找到了这个图书馆 https://pub.dev/packages/flutter_svg#-readme-tab- 。
使用我的代码我得到错误:未处理的异常:错误状态:viewBox 元素必须是 4 个元素长
如果我可以在 canvas 中缩放和旋转 svg 就好了。但这是可选的。
来自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。
披露:我是 author/primary Flutter SVG 的维护者。
最终我发现直接在 Canvas 中绘制 SVG 很麻烦。相反,我复制了 SVG 路径并使用 path_drawing
转换为 Dart 代码,并使用 Canvas.drawPath
将它们渲染为 Path
s。这样做的好处甚至根本不是资产;此时 SVG 数据实际上是代码。您可以轻松地转换回 SVG。这个过程有点像这样:
- 在您从
import 'package:path_drawing/path_drawing.dart';
渲染的文件中将 path_drawing: 0.4.1
添加到 pubspec.yaml
、flutter pub get
。
- 使用方法
parseSvgPathData
从您的 SVG 中复制并粘贴所有路径作为路径常量。 (路径字符串看起来像 M 86.102000,447.45700 L 86.102000,442.75300 .....
)
- 如果 SVG 中有多个路径,您可以组合多个路径:
static final Path complexPathToDraw = parseSvgPathData("path_1").addPath(parseSvgPathData("path_2"));
。
- 通常 SVG 会包含在一些翻译中 (
<g transform='translate()>
)。而 drawPath
只能从左上角位置渲染路径。所以你必须翻译到合适的位置。渲染时,在绘制之前翻译 canvas (1) 首先纠正 SVG 中的翻译,(2) 缩放到你想要的大小,(3) 转到你真正想要的位置Canvas。然后绘制,将Canvas恢复到未变形状态。但请记住,这些矩阵以 逆序 添加到我们逻辑分解的方式,因为线性代数是愚蠢的。
canvas.save();
canvas.translate(dxToRenderPosition, dyToRenderPosition);
canvas.scale(sxFromSvgSizeToDesiredRenderSize);
canvas.translate(dxFromSvg, dyFromSvg);
canvas.drawPath(complexPathToDraw, Paint());
canvas.restore();
我遇到了类似的问题,我想在 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();
这样,您可以在 canvas 上渲染复杂的 Svg,并且仍然可以对其进行一些处理。
例如:您可以在同一个 canvas 上绘制多个 Svg 并绘制它们。
我有这样的东西:
CustomPaint(
painter: CurvePainter(),
)
在这个class我正在画我的画:
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);
}
}
有人知道如何在 paint 方法中绘制 SVG 吗? 我找到了这个图书馆 https://pub.dev/packages/flutter_svg#-readme-tab- 。 使用我的代码我得到错误:未处理的异常:错误状态:viewBox 元素必须是 4 个元素长
如果我可以在 canvas 中缩放和旋转 svg 就好了。但这是可选的。
来自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。
披露:我是 author/primary Flutter SVG 的维护者。
最终我发现直接在 Canvas 中绘制 SVG 很麻烦。相反,我复制了 SVG 路径并使用 path_drawing
转换为 Dart 代码,并使用 Canvas.drawPath
将它们渲染为 Path
s。这样做的好处甚至根本不是资产;此时 SVG 数据实际上是代码。您可以轻松地转换回 SVG。这个过程有点像这样:
- 在您从
import 'package:path_drawing/path_drawing.dart';
渲染的文件中将path_drawing: 0.4.1
添加到pubspec.yaml
、flutter pub get
。 - 使用方法
parseSvgPathData
从您的 SVG 中复制并粘贴所有路径作为路径常量。 (路径字符串看起来像M 86.102000,447.45700 L 86.102000,442.75300 .....
)- 如果 SVG 中有多个路径,您可以组合多个路径:
static final Path complexPathToDraw = parseSvgPathData("path_1").addPath(parseSvgPathData("path_2"));
。
- 如果 SVG 中有多个路径,您可以组合多个路径:
- 通常 SVG 会包含在一些翻译中 (
<g transform='translate()>
)。而drawPath
只能从左上角位置渲染路径。所以你必须翻译到合适的位置。渲染时,在绘制之前翻译 canvas (1) 首先纠正 SVG 中的翻译,(2) 缩放到你想要的大小,(3) 转到你真正想要的位置Canvas。然后绘制,将Canvas恢复到未变形状态。但请记住,这些矩阵以 逆序 添加到我们逻辑分解的方式,因为线性代数是愚蠢的。canvas.save(); canvas.translate(dxToRenderPosition, dyToRenderPosition); canvas.scale(sxFromSvgSizeToDesiredRenderSize); canvas.translate(dxFromSvg, dyFromSvg); canvas.drawPath(complexPathToDraw, Paint()); canvas.restore();
我遇到了类似的问题,我想在 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();
这样,您可以在 canvas 上渲染复杂的 Svg,并且仍然可以对其进行一些处理。
例如:您可以在同一个 canvas 上绘制多个 Svg 并绘制它们。