You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter使用canvas.saveLayer实现类PS图层功能的问题排查

Flutter简化版Photoshop图层功能实现问题与解决方案

需求描述

在Flutter中实现简化的图层功能:

  • 每个图层内的所有绘图使用同一种固定颜色
  • 同一图层内的多次绘图无叠加加深效果
  • 不同图层颜色区分明显,上层图层内容可覆盖下层

现有图层结构代码

import 'package:flutter/material.dart';
import 'package:label_up_study/models/sketch.dart';
import 'dart:math';

Color randomColor() {
  return Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
}

class LayerInfo {
  List<Sketch> drawLayer; // 存储该图层的所有绘图路径
  String label;
  bool selected;
  bool visible;
  Color color;

  LayerInfo({
    required this.drawLayer,
    required this.label,
    required this.selected,
    this.visible = true,
    required this.color,
  });
}

class Layers {
  final Map<int, LayerInfo> layers = {};

  // 修正语法错误:将=改为=>
  int get selected => layers.keys.where((key) => layers[key]!.selected).toList().first;

  void addLayer() {
    final nextKey = layers.keys.toList().last + 1;
    layers.updateAll((key, value) {
      value.selected = false;
      return value;
    });
    layers[nextKey] = LayerInfo(
        drawLayer: [],
        label: 'Layer $nextKey',
        selected: true,
        color: randomColor());
  }

  Layers() {
    layers[1] = LayerInfo(
        drawLayer: [], label: 'Layer 1', selected: true, color: randomColor());
  }
}

问题重现

尝试使用canvas.saveLayer实现图层效果,但编写的WholeSketchPainter代码无法达到预期:

  • 单图层显示正常,但多图层叠加时颜色互相混合,图层区分不明显

原WholeSketchPainter代码:

class WholeSketchPainter extends CustomPainter {
  final Layers imageLayer;

  WholeSketchPainter({required this.imageLayer});

  @override
  void paint(Canvas canvas, Size size) {
    for (var value in imageLayer.layers.values) {
      print(value.color);
      if (value.visible) {
        canvas.saveLayer(null, Paint()..color = value.color.withOpacity(0.5));
        drawSketch(canvas, value.drawLayer, value.color);
        canvas.restore();
      } else {
        // nothing
      }
    }
  }

  void drawSketch(Canvas canvas, List<Sketch> sketches, Color layerColor) {
    Paint strokePaint = Paint()
      ..style = PaintingStyle.stroke
      ..color = layerColor.withOpacity(1);
    Paint fillPaint = Paint()
      ..style = PaintingStyle.fill
      ..color = layerColor.withOpacity(1);

    for (int i = 0; i < sketches.length; i++) {
      final offsets = sketches[i].points;
      if (offsets.isEmpty) {
        return;
      }
      final path = Path();
      final firstOffset = offsets.first;
      final lastOffset = offsets.last;

      path.moveTo(firstOffset.dx, firstOffset.dy);

      if (sketches[i].sketchType == SketchType.pencil) {
        for (int i = 1; i < offsets.length - 1; i++) {
          final p0 = offsets[i];
          final p1 = offsets[i + 1];
          path.quadraticBezierTo(
              p0.dx, p0.dy, (p0.dx + p1.dx) / 2, (p0.dy + p1.dy) / 2);
        }
        canvas.drawPath(path, fillPaint);
      } else if (sketches[i].sketchType == SketchType.square) {
        final rect = Rect.fromPoints(firstOffset, lastOffset);
        canvas.drawRect(rect, fillPaint);
      }
    }
  }

  @override
  bool shouldRepaint(WholeSketchPainter delegate) {
    return false;
  }
}

问题原因

  1. saveLayer的Paint参数设置了半透明图层颜色,导致当前图层内容与下方图层强制混合,破坏了图层独立的视觉效果
  2. 同一图层内的绘图使用默认的BlendMode.srcOver,多次绘制同一区域会叠加加深,不符合“同一图层无叠加”的需求

解决方案与修改后代码

修改要点

  • 移除saveLayer中带颜色的Paint参数,恢复正常的图层合成逻辑
  • 给绘图画笔添加BlendMode.src,确保同一图层内的绘制直接覆盖,无叠加加深
  • 修正Layers类中selected getter的语法错误
  • 优化绘图循环逻辑,避免单个空路径中断整个图层绘制

修改后的WholeSketchPainter代码

class WholeSketchPainter extends CustomPainter {
  final Layers imageLayer;

  WholeSketchPainter({required this.imageLayer});

  @override
  void paint(Canvas canvas, Size size) {
    final layerBounds = Offset.zero & size;
    for (var value in imageLayer.layers.values) {
      if (value.visible) {
        // 保存独立图层,使用默认合成参数
        canvas.saveLayer(layerBounds, Paint());
        // 绘制当前图层的所有草图
        drawSketch(canvas, value.drawLayer, value.color);
        canvas.restore();
      }
    }
  }

  void drawSketch(Canvas canvas, List<Sketch> sketches, Color layerColor) {
    Paint strokePaint = Paint()
      ..style = PaintingStyle.stroke
      ..color = layerColor
      ..blendMode = BlendMode.src; // 同一图层内绘制直接覆盖,无叠加

    Paint fillPaint = Paint()
      ..style = PaintingStyle.fill
      ..color = layerColor
      ..blendMode = BlendMode.src;

    for (int i = 0; i < sketches.length; i++) {
      final offsets = sketches[i].points;
      if (offsets.isEmpty) continue; // 避免单个空路径中断整个图层绘制

      final path = Path();
      final firstOffset = offsets.first;
      final lastOffset = offsets.last;

      path.moveTo(firstOffset.dx, firstOffset.dy);

      if (sketches[i].sketchType == SketchType.pencil) {
        for (int j = 1; j < offsets.length - 1; j++) { // 变量名避免与外层冲突
          final p0 = offsets[j];
          final p1 = offsets[j + 1];
          path.quadraticBezierTo(
              p0.dx, p0.dy, (p0.dx + p1.dx) / 2, (p0.dy + p1.dy) / 2);
        }
        canvas.drawPath(path, fillPaint);
      } else if (sketches[i].sketchType == SketchType.square) {
        final rect = Rect.fromPoints(firstOffset, lastOffset);
        canvas.drawRect(rect, fillPaint);
      }
    }
  }

  @override
  bool shouldRepaint(WholeSketchPainter delegate) {
    // 图层内容变化时触发重绘
    return delegate.imageLayer.layers != imageLayer.layers;
  }
}

效果验证

  • 单图层内多次绘制同一区域,颜色始终保持图层纯色,无叠加加深
  • 多图层叠加时,上层图层内容清晰覆盖下层,不同图层颜色区分明显,符合预期

内容的提问来源于stack exchange,提问作者jungeyonoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 22:55:55