您的位置:

Scenic的多方面分析

随着移动互联网的发展,越来越多的APP需要显示丰富的场景,这就需要我们有一种以高效、灵活为特点的界面框架,Scenic就是这样的一种框架。Scenic它是一种以OpenGL为基础的图形库,用于构建高效、灵活的用户接口。

一、Scenic的特点

1. 矢量渲染:Scenic使用OpenGL绘制2D图形,支持不同的变形和旋转,这使得图形很容易适应不同大小的屏幕。同时,Scenic支持基于路径的渲染技术,使开发者可以更轻松的绘制各种复杂的图形。

2. 丰富的图形元素:Scenic提供了各种常见的UI元素,如文本、图片、按钮、列表和滚动视图等,这些元素可供组合成丰富的用户界面。

3. 异步布局:Scenic支持异步布局,这意味着当界面使用布局时,布局的计算是在单独的线程中进行的,可以大大减少界面卡顿的情况。

4. 支持GPU加速:Scenic使用OpenGL,可以充分利用GPU的性能,提高性能,同时也可以减少CPU的工作量。

5. 高度可定制:Scenic允许开发者自定义UI元素以及界面样式,这使得开发者可以轻松构建适应各种需求的用户界面。

二、Scenic的基本用法

安装Scenic: Scenic可以使用Flutter的package manager进行安装,只需要输入以下命令即可:

flutter packages add scenic

创建Scenic Widget: Scenic的UI元素被封装为Widget,开发者可以轻松创建一个Scenic Widget:

class ScenicWidget extends StatelessWidget {
  const ScenicWidget();
 
  @override
  Widget build(BuildContext context) {
    return Scenic.custom(
      sizeCallback: (size) {},
      onPaint: (canvas) {},
    );
  }
}

在上面的代码中,我们创建了一个简单的Scenic Widget,并重写了build方法,返回一个Scenic.custom Widget。通过这个Widget,我们可以自定义画布大小以及视图绘制方法。

这个Widget现在什么也不显示,我们需要自定义视图的绘制方法,来让其显示出来:

return Scenic.custom(
      sizeCallback: (size) {},
      onPaint: (canvas) {
        canvas.drawRect(
            rect: Offset.zero & Size(100, 100),
            style: Paint.Style.fill,
            color: ScenicColor.fromHex('#FFFFFF'));
      },
    );

这里的canvas就是用于绘制的画布,我们在画布上绘制了一个白色的矩形,这个矩形的大小是100x100。

三、Scenic实现ListView

我们可以使用Scenic轻松地构建一个ListView,下面是一个简单的例子:

class CustomListViewItemWidget extends ScenicWidget {
  const CustomListViewItemWidget(
    this.index, {
    this.color = 'FF0000',
  });
  
  final int index;
  final String color;
  
  @override
  void onPaint(Canvas canvas) {    
    canvas.drawRect(
      rect: Offset.zero & size,
      style: Paint.Style.fill,
      color: ScenicColor.fromHex('#$color'),
    );

    canvas.drawText(
      text: 'Item $index',
      position: Offset(16, size.height / 2 - 16),
      textStyle: TextStyle(color: ScenicColor.fromHex('#FFFFFF')),
    );
  }
  
  @override
  get width => 320;
  
  @override
  get height => 64;
}

class CustomListViewWidget extends ScenicWidget {
  const CustomListViewWidget();
  
  @override
  void sizeCallback(Size size) {
    super.sizeCallback(size);
    final width = size.width;
    final height = size.height / CustomListViewItemWidget.height;
    sizeCallback(Size(width, height));
  }

  @override
  void onPaint(Canvas canvas) {
    for (var i = 0; i < size.height / CustomListViewItemWidget.height; i++) {
      canvas.drawRRect(
        rect: Offset(0, i * CustomListViewItemWidget.height) &
            Size(size.width, CustomListViewItemWidget.height),
        radius: 4,
        style: Paint.Style.fill,
        color: ScenicColor.fromHex(
          i.isEven ? '#F5F5F5' : '#FFFFFF',
        ),
      );
      canvas.save();
      canvas.translate(0, i * CustomListViewItemWidget.height);
      canvas.clipRRect(
          rect: Offset.zero &
              Size(size.width, CustomListViewItemWidget.height),
          radius: 4);
      CustomListViewItemWidget(i, color: i.isEven ? '0087FF' : 'FF0000')
        ..paint(canvas);
      canvas.restore();
    }
  }
}

在上面的代码中,我们创建了两个Widget,CustomListViewItemWidget和CustomListViewWidget,分别用于显示ListView中每一个Item和整个ListView。CustomListViewItemWidget表示ListView的Item,我们在这里绘制了一个矩形和文本;CustomListViewWidget表示整个ListView,我们在这里使用onPaint方法绘制了多个CustomListViewItemWidget,然后每一个CustomListViewItemWidget都需要调用paint方法绘制到画布上。

四、Scenic实现表单

Scenic同样也非常适合用于实现表单,为开发者提供了很多便利和高度的灵活性。下面是一个简单的例子:

class ScenicFormWidget extends ScenicWidget {
  const ScenicFormWidget();

  @override
  double get width => 240;
  @override
  double get height => 200;

  final TextEditingController _nameController = TextEditingController();

  void submit() {
    print(_nameController.text);
  }

  @override
  void onPaint(Canvas canvas) {
    final inputWidth = width - 32;
    final inputOffset = Offset(16, 32);

    canvas.drawRect(
      rect: Offset.zero & size,
      style: Paint.Style.fill,
      color: ScenicColor.fromHex('#FFFFFF'),
    );

    canvas.drawText(
      text: 'Scenic Form',
      position: Offset(width / 2 - 50, 16),
      textStyle: TextStyle(
          color: ScenicColor.fromHex('#000000'), fontSize: 18),
    );

    canvas.drawText(
      text: 'Name',
      position: inputOffset - Offset(0, 16),
      textStyle: TextStyle(
        color: ScenicColor.fromHex('#000000'),
        fontSize: 14,
      ),
    );

    canvas.drawRect(
      rect: Rect.fromLTWH(inputOffset.dx, inputOffset.dy, inputWidth, 32),
      style: Paint.Style.stroke,
      color: ScenicColor.fromHex('#000000'),
      strokeWidth: 1,
    );

    canvas.drawText(
      text: _nameController.text,
      position: inputOffset,
      textStyle: TextStyle(
        color: ScenicColor.fromHex('#000000'),
        fontSize: 14,
      ),
    );
  }
}

在上面的代码中,我们创建了一个ScenicFormWidget,这个Widget里面绘制了一个表单,包含一个输入框和一个提交按钮。在onPaint方法中,我们使用ScenicDraw来绘制各种UI元素,如文本、矩形、线条等。同时,我们为表单绑定了一个该Controller,使用submit方法来处理其提交事件。

五、Scenic实现动画

Scenic使用Flutter的Animation框架来支持动画,提供了ScenicAnimationBuilder Widget,可方便地创建动画并将其连接到UI元素:

class ScenicAnimationDemo extends StatefulWidget {
  const ScenicAnimationDemo();
  
  @override
  State
    createState() => _ScenicAnimationDemoState();
}

class _ScenicAnimationDemoState extends State
    
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation
      _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );
    _animation = Tween(begin: 0.0, end: 1.0).animate(_controller);
  }

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

  @override
  Widget build(BuildContext context) {
    return ScenicAnimationBuilder(
      animation: _animation,
      builder: (context, child, value) {
        return Scenic.custom(
          onPaint: (canvas) {
            canvas.drawRect(
              rect: Offset.zero & Size(100, 100),
              style: Paint.Style.fill,
              color: ScenicColor.fromHex('#000000').withOpacity(value),
            );
          },
        );
      },
    );
  }
}


     
    
   

在上面的代码中,我们使用ScenicAnimationBuilder Widget来创建动画,并使用ScenicDraw中的canvas.drawRect方法绘制一个黑色矩形。在动画执行过程中,我们修改矩形的透明度,将其从完全透明变为完全不透明。

总结

Scenic是一个轻量级的UI框架,其特点在于支持矢量渲染、异步布局、GPU加速、高度可定制等方面。Scenic可以轻易地创建各种丰富的用户界面,例如ListView、表单和动画等。同时,Scenic也与Flutter配合得非常好,为Flutter开发者提供了一个高度定制的解决方案。