Flutter弹幕显示插件canvas_danmaku的使用

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

Flutter弹幕显示插件canvas_danmaku的使用

概述

canvas_danmaku 是一个使用 CustomPainter 进行直接绘制的简易高性能 Flutter 弹幕组件。它通过底层的 CustomPainter 直接绘制弹幕,减少了 Flutter 框架中组件的数量,降低了组件树的复杂度,从而提高性能。

示例

依赖添加

在你的 pubspec.yaml 文件中添加以下依赖:

dependencies: 
  canvas_danmaku: ^0.2.6

基本使用示例

下面是一个简单的示例,展示了如何在 Flutter 应用中集成 canvas_danmaku 插件:

import 'package:canvas_danmaku/canvas_danmaku.dart';

class _DanmakuPageState extends State<DanmakuPage> {
    late DanmakuController _controller;

    [@override](/user/override)
    Widget build(BuildContext context) {
        return Stack(
            children: [
                // 你的自定义组件,例如一个播放器
                Container(),
                // 弹幕组件
                DanmakuScreen(
                    createdController: (e) {
                        _controller = e;
                    },
                    option: DanmakuOption(),
                ),
            ],
        );
    }
}

完整示例 Demo

下面是一个更完整的示例,包含了控制面板和各种交互功能:

import 'dart:io';
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CanvasDanmaku Demo',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  late DanmakuController _controller;
  final _danmuKey = GlobalKey();

  bool _running = true;
  bool _showStroke = true;
  bool _massiveMode = false;
  double _opacity = 1.0;
  int _duration = 8;
  double _fontSize = Platform.isIOS || Platform.isAndroid ? 16 : 25;
  int _fontWeight = 4;
  bool _hideScroll = false;
  bool _hideTop = false;
  bool _hideBottom = false;
  bool _safeArea = true;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('CanvasDanmaku Demo'),
      ),
      body: Column(
        children: [
          Wrap(
            children: [
              IconButton(
                icon: Icon(Icons.add),
                tooltip: 'Add Scroll',
                onPressed: () {
                  _controller.addDanmaku(
                    DanmakuContentItem(
                      "这是一条超长弹幕ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789这是一条超长的弹幕,这条弹幕会超出屏幕宽度",
                      color: getRandomColor(),
                    ),
                  );
                },
              ),
              IconButton(
                icon: Icon(Icons.add),
                tooltip: 'Add Top',
                onPressed: () {
                  _controller.addDanmaku(
                    DanmakuContentItem("这是一条顶部弹幕", color: getRandomColor(), type: DanmakuItemType.top),
                  );
                },
              ),
              IconButton(
                icon: Icon(Icons.add),
                tooltip: 'Add Bottom',
                onPressed: () {
                  _controller.addDanmaku(
                    DanmakuContentItem("这是一条底部弹幕", color: getRandomColor(), type: DanmakuItemType.bottom),
                  );
                },
              ),
              IconButton(
                icon: Icon(Icons.play_circle_outline_outlined),
                onPressed: startPlay,
                tooltip: 'Start Player',
              ),
              IconButton(
                icon: Icon(_running ? Icons.pause : Icons.play_arrow),
                onPressed: () {
                  if (_running) {
                    _controller.pause();
                  } else {
                    _controller.resume();
                  }
                  setState(() {
                    _running = !_running;
                  });
                },
                tooltip: 'Play Resume',
              ),
              IconButton(
                icon: Icon(_showStroke ? Icons.font_download : Icons.font_download_rounded),
                onPressed: () {
                  _controller.updateOption(_controller.option.copyWith(showStroke: !_showStroke));
                  setState(() {
                    _showStroke = !_showStroke;
                  });
                },
                tooltip: 'Stroke',
              ),
              IconButton(
                icon: Icon(Icons.clear),
                onPressed: () {
                  _controller.clear();
                },
                tooltip: 'Clear',
              ),
            ],
          ),
          Expanded(
            child: Container(
              color: Colors.grey,
              child: DanmakuScreen(
                key: _danmuKey,
                createdController: (DanmakuController e) {
                  _controller = e;
                },
                option: DanmakuOption(
                  opacity: _opacity,
                  fontSize: _fontSize,
                  fontWeight: _fontWeight,
                  duration: _duration,
                  showStroke: _showStroke,
                  massiveMode: _massiveMode,
                  hideScroll: _hideScroll,
                  hideTop: _hideTop,
                  hideBottom: _hideBottom,
                  safeArea: _safeArea,
                ),
              ),
            ),
          ),
        ],
      ),
      endDrawer: Drawer(
        child: SafeArea(
          child: ListView(
            padding: EdgeInsets.all(8),
            children: [
              Text("Opacity : $_opacity"),
              Slider(
                value: _opacity,
                max: 1.0,
                min: 0.1,
                divisions: 9,
                onChanged: (e) {
                  setState(() {
                    _opacity = e;
                  });
                  _controller.updateOption(_controller.option.copyWith(opacity: e));
                },
              ),
              Text("FontSize : $_fontSize"),
              Slider(
                value: _fontSize,
                min: 8,
                max: 36,
                divisions: 14,
                onChanged: (e) {
                  setState(() {
                    _fontSize = e;
                  });
                  _controller.updateOption(_controller.option.copyWith(fontSize: e));
                },
              ),
              Text("Duration : $_duration"),
              Slider(
                value: _duration.toDouble(),
                min: 4,
                max: 20,
                divisions: 16,
                onChanged: (e) {
                  setState(() {
                    _duration = e.toInt();
                  });
                  _controller.updateOption(_controller.option.copyWith(duration: e.toInt()));
                },
              ),
              SwitchListTile(
                title: const Text('MassiveMode'),
                value: _massiveMode,
                onChanged: (e) {
                  setState(() {
                    _massiveMode = e;
                  });
                  _controller.updateOption(_controller.option.copyWith(massiveMode: e));
                },
              ),
              SwitchListTile(
                title: const Text('SafeArea'),
                value: _safeArea,
                onChanged: (e) {
                  setState(() {
                    _safeArea = e;
                  });
                  _controller.updateOption(_controller.option.copyWith(safeArea: e));
                },
              ),
            ],
          ),
        ),
      ),
    );
  }

  Timer? timer;
  int sec = 0;

  void startPlay() async {
    String data = await DefaultAssetBundle.of(context).loadString('assets/132590001.json');
    List<DanmakuContentItem> _items = [];
    var jsonMap = json.decode(data);
    for (var item in jsonMap['comments']) {
      _items.add(DanmakuContentItem(item['m'], color: Colors.white));
    }
    if (timer == null) {
      timer = Timer.periodic(Duration(seconds: 1), (timer) {
        if (!_controller.running) return;
        _controller.addDanmaku(_items[sec]);
        sec++;
      });
    }
  }

  Color getRandomColor() {
    final Random random = Random();
    return Color.fromARGB(
      255, // 固定 alpha 为 255(完全不透明)
      random.nextInt(256),
      random.nextInt(256),
      random.nextInt(256),
    );
  }

  [@override](/user/override)
  void dispose() {
    timer?.cancel();
    super.dispose();
  }
}

更多关于Flutter弹幕显示插件canvas_danmaku的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter弹幕显示插件canvas_danmaku的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用canvas_danmaku插件来显示弹幕的示例代码。这个插件允许你在Flutter应用中实现类似于视频网站的弹幕效果。

首先,确保你已经在pubspec.yaml文件中添加了canvas_danmaku依赖:

dependencies:
  flutter:
    sdk: flutter
  canvas_danmaku: ^最新版本号 # 请替换为实际发布的最新版本号

然后运行flutter pub get来安装依赖。

接下来是一个简单的示例代码,展示了如何使用canvas_danmaku插件来显示弹幕:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Danmaku Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DanmakuPage(),
    );
  }
}

class DanmakuPage extends StatefulWidget {
  @override
  _DanmakuPageState createState() => _DanmakuPageState();
}

class _DanmakuPageState extends State<DanmakuPage> {
  DanmakuController? _controller;

  @override
  void initState() {
    super.initState();
    _initDanmakuController();
  }

  void _initDanmakuController() {
    _controller = DanmakuController(
      viewportWidth: 375, // 设置视口宽度
      viewportHeight: 667, // 设置视口高度
      danmakuCount: 100, // 设置弹幕数量
      danmakuConfig: DanmakuConfig(
        fontSize: 24,
        speed: 300, // 弹幕速度(毫秒)
        lifeTime: 5000, // 弹幕生命周期(毫秒)
        direction: DanmakuDirection.rtl, // 从右到左
        isLimitInScreen: true, // 是否限制在屏幕内
        gravity: DanmakuGravity.bottom, // 底部显示
        maxLines: 3, // 最大行数
      ),
    );

    // 添加一些示例弹幕
    _addDanmakus();
  }

  void _addDanmakus() {
    final List<String> messages = [
      'Hello, Flutter!',
      'This is a test message.',
      'Canvas Danmaku is awesome!',
      // 添加更多消息...
    ];

    messages.forEach((message) {
      _controller?.addDanmaku(DanmakuData(text: message));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Danmaku Demo'),
      ),
      body: Stack(
        children: [
          Positioned.fill(
            child: DanmakuWidget(
              controller: _controller!,
            ),
          ),
          // 你可以在这里添加其他UI元素,比如视频播放器等
        ],
      ),
    );
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }
}

解释

  1. 依赖管理:在pubspec.yaml文件中添加canvas_danmaku依赖。
  2. 初始化:在_DanmakuPageStateinitState方法中初始化DanmakuController,并设置一些配置参数,如视口宽度、高度、弹幕数量、字体大小、速度等。
  3. 添加弹幕:通过_addDanmakus方法添加一些示例弹幕。
  4. UI构建:使用DanmakuWidget来显示弹幕,并将其放置在Stack中,以便可以在其上添加其他UI元素(例如视频播放器)。
  5. 资源释放:在dispose方法中释放DanmakuController资源。

这个示例提供了一个基本的框架,你可以根据需要进行进一步的自定义和扩展。

回到顶部