Flutter三维图形与SVG渲染插件three_js_svg的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter三维图形与SVG渲染插件three_js_svg的使用

这是一个允许用户向项目中添加或导出SVG文件的类型插件,基于three.js和three_dart。最初由@mrdoob创建,并由@wasabia进行了dart转换。

老虎SVG图片

该插件是three.js和three_dart的一个dart版本,最初由@mrdoob创建,并由@wasabia进行了dart转换。

开始使用

pubspec.yaml文件中添加以下依赖项,同时还需要添加three_js_maththree_js_corethree_js_core_loaders

dependencies:
  three_js_svg: ^x.x.x
  three_js_math: ^x.x.x
  three_js_core: ^x.x.x
  three_js_core_loaders: ^x.x.x

然后初始化场景并加载SVG文件:

late Scene scene;

void init() {
    scene = Scene();
    scene.background = Color.fromHex32(0xf0f0f0);
        
    final loader = SVGLoader();
    final data = await loader.fromAsset('assets/${fileName}.svg');

    List<ShapePath> paths = data!.paths;

    Group group = Group();
    group.scale.scale(0.25);
    group.position.x = -25;
    group.position.y = 25;
    group.rotateZ(math.pi);
    group.rotateY(math.pi);
    //group.scale.y *= -1;

    for (int i = 0; i < paths.length; i++) {
      ShapePath path = paths[i];

      final fillColor = path.userData?["style"]["fill"];
      if (guiData["drawFillShapes"] == true && fillColor != null && fillColor != 'none') {
        MeshBasicMaterial material = MeshBasicMaterial.fromMap({
          "color":tmath.Color().setStyle(fillColor).convertSRGBToLinear(),
          "opacity": path.userData?["style"]["fillOpacity"].toDouble(),
          "transparent": true,
          "side": tmath.DoubleSide,
          "depthWrite": false,
          "wireframe": guiData["fillShapesWireframe"]
        });

        final shapes = SVGLoader.createShapes(path);

        for (int j = 0; j < shapes.length; j++) {
          final shape = shapes[j];

          ShapeGeometry geometry = ShapeGeometry([shape]);
          Mesh mesh = Mesh(geometry, material);

          group.add(mesh);
        }
      }

      final strokeColor = path.userData?["style"]["stroke"];

      if (guiData["drawStrokes"] == true &&
          strokeColor != null &&
          strokeColor != 'none') {
        MeshBasicMaterial material = MeshBasicMaterial.fromMap({
          "color":tmath.Color().setStyle(strokeColor).convertSRGBToLinear(),
          "opacity": path.userData?["style"]["strokeOpacity"].toDouble(),
          "transparent": true,
          "side": tmath.DoubleSide,
          "depthWrite": false,
          "wireframe": guiData["strokesWireframe"]
        });

        for (int j = 0, jl = path.subPaths.length; j < jl; j++) {
          Path subPath = path.subPaths[j];
          final geometry = SVGLoader.pointsToStroke(subPath.getPoints(), path.userData?["style"]);

          if (geometry != null) {
            final mesh = Mesh(geometry, material);

            group.add(mesh);
          }
        }
      }
    }

    scene.add(group);
}

使用方法

这个项目是一个用于three.js的SVG模型加载器和导出器。

示例

可以在以下链接找到这个API的示例:这里

示例代码

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:three_js_core/three_js_core.dart' as three;
import 'package:three_js_math/three_js_math.dart' as tmath;
import 'package:three_js_svg/three_js_svg.dart';
import 'package:three_js_helpers/three_js_helpers.dart';
import 'package:three_js_geometry/three_js_geometry.dart';
import 'package:three_js_curves/three_js_curves.dart';
import 'dart:math' as math;

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const WebglLoaderSvg(),
    );
  }
}

class WebglLoaderSvg extends StatefulWidget {
  const WebglLoaderSvg({super.key});

  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<WebglLoaderSvg> {
  late three.ThreeJS threeJs;

  [@override](/user/override)
  void initState() {
    threeJs = three.ThreeJS(
      onSetupComplete: () { setState(() {}); },
      setup: setup
    );
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    threeJs.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return threeJs.build();
  }

  final guiData = {
    "currentURL": 'assets/tiger.svg',
    // "currentURL": 'assets/energy.svg',
    // "currentURL": 'assets/hexagon.svg',
    // "currentURL": 'assets/lineJoinsAndCaps.svg',
    // "currentURL": 'assets/multiple-css-classes.svg',
    // "currentURL": 'assets/threejs.svg',
    // "currentURL": 'assets/zero-radius.svg',
    "drawFillShapes": true,
    "drawStrokes": true,
    "fillShapesWireframe": false,
    "strokesWireframe": false
  };

  Future<void> setup() async {
    threeJs.camera = three.PerspectiveCamera(50, threeJs.width / threeJs.height, 1, 1000);
    threeJs.camera.position.setValues(0, 0, 200);

    loadSVG(guiData["currentURL"]);
  }

  void loadSVG(url) {
    threeJs.scene = three.Scene();
    threeJs.scene.background = tmath.Color.fromHex32(0xb0b0b0);

    final helper = GridHelper(160, 10);
    helper.rotation.x = math.pi / 2;
    threeJs.scene.add(helper);

    SVGLoader loader = SVGLoader();

    loader.fromAsset(url).then((data) {
      List<ShapePath> paths = data!.paths;

      three.Group group = three.Group();
      group.scale.scale(0.25);
      group.position.x = -25;
      group.position.y = 25;
      group.rotateZ(math.pi);
      group.rotateY(math.pi);
      //group.scale.y *= -1;

      for (int i = 0; i < paths.length; i++) {
        ShapePath path = paths[i];

        final fillColor = path.userData?["style"]["fill"];
        if (guiData["drawFillShapes"] == true &&
            fillColor != null &&
            fillColor != 'none') {
          three.MeshBasicMaterial material = three.MeshBasicMaterial.fromMap({
            "color": tmath.Color().setStyle(fillColor).convertSRGBToLinear(),
            "opacity": path.userData?["style"]["fillOpacity"],
            "transparent": true,
            "side": tmath.DoubleSide,
            "depthWrite": false,
            "wireframe": guiData["fillShapesWireframe"]
          });

          final shapes = SVGLoader.createShapes(path);

          for (int j = 0; j < shapes.length; j++) {
            final shape = shapes[j];

            ShapeGeometry geometry = ShapeGeometry([shape]);
            three.Mesh mesh = three.Mesh(geometry, material);

            group.add(mesh);
          }
        }

        final strokeColor = path.userData?["style"]["stroke"];

        if (guiData["drawStrokes"] == true &&
            strokeColor != null &&
            strokeColor != 'none') {
          three.MeshBasicMaterial material = three.MeshBasicMaterial.fromMap({
            "color": tmath.Color().setStyle(strokeColor).convertSRGBToLinear(),
            "opacity": path.userData?["style"]["strokeOpacity"],
            "transparent": true,
            "side": tmath.DoubleSide,
            "depthWrite": false,
            "wireframe": guiData["strokesWireframe"]
          });

          for (int j = 0, jl = path.subPaths.length; j < jl; j++) {
            Path subPath = path.subPaths[j];
            final geometry = SVGLoader.pointsToStroke(
                subPath.getPoints(), path.userData?["style"]);

            if (geometry != null) {
              final mesh = three.Mesh(geometry, material);

              group.add(mesh);
            }
          }
        }
      }

      threeJs.scene.add(group);
    });
  }
}

更多关于Flutter三维图形与SVG渲染插件three_js_svg的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter三维图形与SVG渲染插件three_js_svg的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,虽然three_js_svg并不是一个广泛认知或官方支持的库,用于直接结合Three.js和SVG渲染,但我们可以通过一些方法实现三维图形和SVG的渲染。Flutter本身没有直接支持Three.js的插件,但可以通过平台通道(Platform Channels)与原生代码(如Android的Java/Kotlin或iOS的Swift/Objective-C)进行交互,从而使用Three.js进行渲染。

以下是一个大致的思路和代码示例,展示如何在Flutter中嵌入一个使用Three.js渲染的WebView,同时展示如何在Flutter中单独渲染SVG。请注意,这不会直接结合Three.js和SVG,但可以作为实现各自功能的基础。

1. 使用WebView加载Three.js渲染的内容

首先,确保你的Flutter项目中包含webview_flutter插件:

dependencies:
  flutter:
    sdk: flutter
  webview_flutter: ^3.0.4  # 请检查最新版本

然后,在你的Flutter应用中创建一个WebView来加载包含Three.js渲染的HTML页面:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Three.js in WebView'),
        ),
        body: WebViewExample(),
      ),
    );
  }
}

class WebViewExample extends StatefulWidget {
  @override
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<WebViewExample> {
  late WebViewController _controller;

  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: Uri.dataFromString(
        '''
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Three.js Example</title>
          <style>body { margin: 0; }</style>
        </head>
        <body>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
          <script>
            // Three.js rendering code here
            let scene, camera, renderer, cube;
            function init() {
              scene = new THREE.Scene();
              camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
              renderer = new THREE.WebGLRenderer();
              renderer.setSize(window.innerWidth, window.innerHeight);
              document.body.appendChild(renderer.domElement);

              let geometry = new THREE.BoxGeometry();
              let material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
              cube = new THREE.Mesh(geometry, material);
              scene.add(cube);

              camera.position.z = 5;
              animate();
            }

            function animate() {
              requestAnimationFrame(animate);
              cube.rotation.x += 0.01;
              cube.rotation.y += 0.01;
              renderer.render(scene, camera);
            }

            init();
          </script>
        </body>
        </html>
        ''',
        mimeType: 'text/html',
        encoding: Encoding.getByName('utf-8')
      ).toString(),
      javascriptMode: JavascriptMode.unrestricted,
      onWebViewCreated: (WebViewController webViewController) {
        _controller = webViewController;
      },
    );
  }
}

2. 在Flutter中渲染SVG

对于SVG的渲染,可以使用flutter_svg插件:

dependencies:
  flutter:
    sdk: flutter
  flutter_svg: ^1.0.3  # 请检查最新版本

在你的Flutter应用中渲染一个SVG文件:

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SVG Rendering'),
        ),
        body: Center(
          child: SvgPicture.asset(
            'assets/sample.svg', // 确保在pubspec.yaml中声明了assets
            width: 200,
            height: 200,
          ),
        ),
      ),
    );
  }
}

pubspec.yaml中声明SVG资源:

flutter:
  assets:
    - assets/sample.svg

总结

虽然直接在Flutter中使用three_js_svg这样的库来结合Three.js和SVG渲染并不现实,但通过上述方法,你可以在Flutter应用中分别实现Three.js的三维图形渲染和SVG的渲染。如果需要更复杂的交互或结合,可能需要更深入的自定义开发,比如通过平台通道在原生代码中实现更复杂的功能,并在Flutter中调用这些功能。

回到顶部