Predidit / canvas_danmaku

简易高性能的flutter弹幕组件
https://pub.dev/packages/canvas_danmaku
MIT License
8 stars 3 forks source link

IOS上弹幕描边带尖刺? #7

Open MCDFsteve opened 1 month ago

MCDFsteve commented 1 month ago

很奇怪。同一份代码,其中macOS的描边宽度是3,IOS和Android设为的1.5. 但是手机上显示出来就有奇怪的尖刺,不知道是因为什么导致的。 尖刺容易出现在字的尖锐处(例如大写的M,2的左下角) 0e4d4a1458ac068dc795877ee4ea1d24

8a46b15e186ffc53ab2336fc639112e9
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'danmaku_content_item.dart';
import 'dart:io'; // 用于检测平台

double getStrokeWidth() {
  if (Platform.isIOS || Platform.isAndroid) {
    return 1.5; // 如果是iOS或Android设备
  } else {
    return 3.0; // 其他平台,例如Web、桌面
  }
}
class Utils {
  static generateParagraph(
      DanmakuContentItem content, double danmakuWidth, double fontSize) {
    final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle(
      textAlign: TextAlign.left,
      fontSize: fontSize,
      textDirection: TextDirection.ltr,
    ))
      ..pushStyle(ui.TextStyle(
        color: content.color,
      ))
      ..addText(content.text);
    return builder.build()
      ..layout(ui.ParagraphConstraints(width: danmakuWidth));
  }

  static generateStrokeParagraph(
      DanmakuContentItem content, double danmakuWidth, double fontSize) {
    final Paint strokePaint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = getStrokeWidth()
      ..color = (content.color == Colors.black || content.color.value == const Color.fromARGB(255, 0, 0, 0).value)
          ? Colors.white
          : Colors.black;

    final ui.ParagraphBuilder strokeBuilder =
        ui.ParagraphBuilder(ui.ParagraphStyle(
      textAlign: TextAlign.left,
      fontSize: fontSize,
      textDirection: TextDirection.ltr,
    ))
          ..pushStyle(ui.TextStyle(
            foreground: strokePaint,
          ))
          ..addText(content.text);

    return strokeBuilder.build()
      ..layout(ui.ParagraphConstraints(width: danmakuWidth));
  }
}
Predidit commented 1 month ago

可以使用整数的描边宽度。但无法完全消除尖刺。

这和 flutter 引擎的底层抗锯齿机制相关。

如果你想要获得尽可能好的视觉效果, 大概只能选择一些较为圆润的字体。尖刺会不那么明显。

Predidit commented 1 month ago

如果找到了解决方法,欢迎在这里留言。

这个 Issue 保持开启,因为这是一个实际存在的问题。且其他流行的弹幕库似乎也没有解决。

MCDFsteve commented 1 month ago

我将描边绘制方法更改为使用shadow绘制以后,尖刺现象就完全消失了。并且整体视觉效果也很不错。

image

62d91a35a3cbeda8b76aa2895e133e48_720

MCDFsteve commented 1 month ago
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'danmaku_content_item.dart';
import 'dart:io'; // 用于检测平台

// 返回根据平台的描边偏移值
double getStrokeOffset() {
  if (Platform.isIOS || Platform.isAndroid) {
    return 1.0; // iOS 和 Android 使用较小的偏移
  } else {
    return 1.5; // 其他平台使用较大的偏移
  }
}

class Utils {
  // 根据文字颜色判断使用的描边颜色
  static Color getShadowColor(Color textColor) {
    // 如果文字是黑色,使用白色描边
    if (textColor == Colors.black || textColor.value == const Color.fromARGB(255, 0, 0, 0).value) {
      return Colors.white;
    }
    // 否则使用黑色描边
    return Colors.black;
  }

  static generateParagraph(DanmakuContentItem content, double danmakuWidth, double fontSize) {
    // 获取描边颜色
    final shadowColor = getShadowColor(content.color);
    final strokeOffset = getStrokeOffset(); // 动态获取偏移量

    final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle(
      textAlign: TextAlign.left,
      fontSize: fontSize,
      textDirection: TextDirection.ltr,
    ))
      ..pushStyle(ui.TextStyle(
        color: content.color,
        shadows: [
          ui.Shadow(offset: Offset(-strokeOffset, -strokeOffset), color: shadowColor), // 左上
          ui.Shadow(offset: Offset(strokeOffset, -strokeOffset), color: shadowColor),  // 右上
          ui.Shadow(offset: Offset(-strokeOffset, strokeOffset), color: shadowColor),  // 左下
          ui.Shadow(offset: Offset(strokeOffset, strokeOffset), color: shadowColor),   // 右下
          ui.Shadow(offset: Offset(0, -strokeOffset), color: shadowColor),  // 上
          ui.Shadow(offset: Offset(0, strokeOffset), color: shadowColor),   // 下
          ui.Shadow(offset: Offset(-strokeOffset, 0), color: shadowColor),  // 左
          ui.Shadow(offset: Offset(strokeOffset, 0), color: shadowColor),   // 右
        ],
      ))
      ..addText(content.text);

    return builder.build()
      ..layout(ui.ParagraphConstraints(width: danmakuWidth));
  }

  static generateStrokeParagraph(DanmakuContentItem content, double danmakuWidth, double fontSize) {
    final Paint strokePaint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = getStrokeOffset()  // 使用偏移值来控制描边宽度
      ..color = (content.color == Colors.black || content.color.value == const Color.fromARGB(255, 0, 0, 0).value)
          ? Colors.white
          : Colors.black;

    final ui.ParagraphBuilder strokeBuilder =
        ui.ParagraphBuilder(ui.ParagraphStyle(
      textAlign: TextAlign.left,
      fontSize: fontSize,
      textDirection: TextDirection.ltr,
    ))
          ..pushStyle(ui.TextStyle(
            foreground: strokePaint,
          ))
          ..addText(content.text);

    return strokeBuilder.build()
      ..layout(ui.ParagraphConstraints(width: danmakuWidth));
  }
}
Predidit commented 1 month ago

这样字体在低分辨率设备上会发虚。

不过是很棒的思路。我想想有没有什么更好的解法。

以及这段代码是 GPT 生成的吗,看上去有微妙的不协调。

MCDFsteve commented 1 month ago

没在低分辨率测试过。代码确实是gpt写的

Predidit commented 1 month ago

我还没有做进一步测试。

不过这种渲染方式应该可以作为一个可选项,在 iOS/macOS/Android 上启用,在 windows/linux 上禁用。