WebAV icon indicating copy to clipboard operation
WebAV copied to clipboard

[RFC] AVCanvas 支持自定义 UI 组件

Open hughfenghen opened this issue 1 year ago • 6 comments

时间:2024-11-16

背景/动机

@webav/av-cliper 提供了基础的视频处理能力,不需要 UI(如:自动化视频处理)或 UI 非常简单的产品可以直接基于 av-cliper 构建。

在需要用户介入视频编辑创作的众多场景中,如视频剪辑、直播推流、视频相册、视频模板等;

@webav/av-canvas 提供了画布与精灵(Sprite)等 API,且内置了最基础的 UI(移动、缩放、旋转),开发者可基于 av-canvas 快速构建相关产品。

@webav/av-canvas 内置的 UI 终究无法满足多样化的诉求;

av-canvas 定位是一个与 UI 框架无关的库,无法选择某个成熟框架在项目内部构建复杂 UI。

所以需要设计新的 API,允许开发者使用任意技术栈构建丰富或定制化 UI。

开放的 API 能满足:

  1. 与 UI 框架无关;开发者可以选择任意 UI 框架,如 React、Vue、Svelte 等
  2. 能实现已知场景的常见 UI 组件和功能,如
    • 右键菜单:锁定、删除、Sprite 层级调整
    • 裁剪素材,只保留素材的特定区域
  3. 能自定义内置 UI 组件的样式
    • 自定义 Sprite 的选中效果
    • 自定义 Sprite 的控制点
  4. 能复用社区中的 AVCanvas 自定义 UI 组件

相关 issue:#78, #224

详细设计

创建 AVCanvas 时可以传入 UI Plugin,插件行为将覆盖 AVCanvas 的默认实现。

目前仅支持覆盖选中 Sprite 的边框 和 缩放、旋转控制点的绘制效果。

image

new AVCanvas(attchEl, {
  uiPlugins: [customPlugin]
});

// customPlugin 的类型
type Plugin = (ctx: PluginContext) => {
  // AVCanvas 销毁时调用
 destroy: () => void
 // 自定义激活 sprite 的边框、控制点的样式
 acviteSpriteBoxRender: () => void
}

interface PluginContext {
  // 获取当前选中的 Sprite
  getActiveSprtie: () => VisibleSprite | null
  // 监听 Sprite 选中事件
  on: (evtType: 'ActiveSprite', (spr: VisibleSprite | null) => void) => void
  // 获取控制点的位置
  getCtrlsPosition: () => {}
}

示例:自定义边框、控制点的样式

const CustomSpriteBox = (ctx) => {
  return {
    acviteSpriteBoxRender(){
      // todo:初始化绘制 box 所需要的 dom 或 canvas
      // 监听 Sprite 选中或取消选中事件
      ctx.on('ActiveSprite', (spr) => {
        if (spr == null) return
        // todo:根据 spr 的状态(普通选中、锁定、裁剪)绘制特定效果
        // todo:将绘制的边框与 Sprite.rect 坐标
        // todo:将绘制的控制点与 ctx.getCtrlsPosition 进行同步
      })
    }
    destroy() {
      // todo:回收创建的 DOM 节点等
    }
  }
}

new AVCanvas(attchEl, {
  uiPlugins: [CustomSpriteBox]
});

这是内置的边框绘制源代码

可能存在问题

  1. 存在未考虑的场景, 以上设计的 API 难以实现。
  2. 插件开发成本;如果只需要修改一下边框颜色、控制点大小等小调整,相对来说成本较高
  3. 拖拽移动、缩放 Sprite 的逻辑目前无法自定义,如果以当前的 API 范式开放,实现自定义缩放、移动的难度较大

可替代的方案

  1. 直接通过 AVCanvas 暴露 PluginContext 的 API,不限制上层的实现方式;
    可能导致自定义混乱,无法复用
  2. 针对场景,开放更细粒度的 API,如选中 Sprite 的边框、控制点样式;
    降低了自定义成本,但牺牲了 API 通用性

未来的可能性

如果社区有诉求与足够的理由,可以考虑逐渐放开限制,允许覆盖 AVCanvas 内置的所有 UI,包括:

  1. 自定义选中 Sprite 逻辑
  2. 自定义缩放、移动 Sprite 的逻辑(但如上文描述有一定的实现成本
  3. 自定义光标(鼠标)的样式
  4. 自定义参考线及样式

hughfenghen avatar Nov 18 '24 09:11 hughfenghen

有自定义 UI 诉求的同学,可以看看是否满足自己的场景,参与讨论。

hughfenghen avatar Nov 19 '24 03:11 hughfenghen

目前使用情况看,有一定的自定义需求,主要集中在自定义光标,控制点样式调整,未来可能会涉及到旋转中辅助线的展示。 缩放、移动的逻辑还是可以的,有些麻烦的就是没有缩放比例,需要在程序逻辑中存储原始宽高来计算缩放比例数值。 #78 中提到的问题我是通过修改VisibleSprite,添加一些裁切点属性,在canvas绘制时直接进行裁剪展示,我更希望通过这种方式实现裁剪,当然需要配合一个裁剪组件界面,在rect中通过控制点进行裁剪会跟原有功能产出冲突,造成用户体验混乱。

caohongzhi1 avatar Nov 20 '24 02:11 caohongzhi1

没有缩放比例

@caohongz 这句什么意思?

独立画布实现裁剪组件这个想法我是赞同的

  1. 保持独立互不干扰
  2. 技术实现会简单许多,不用考虑 Sprite 的旋转状态

@wendraw

hughfenghen avatar Nov 20 '24 06:11 hughfenghen

没有缩放比例

@caohongz 这句什么意思?

独立画布实现裁剪组件这个想法我是赞同的

  1. 保持独立互不干扰
  2. 技术实现会简单许多,不用考虑 Sprite 的旋转状态

@wendraw

有一种需求是需要按照缩放比例调整视频尺寸而不是通过拖拽,所以就需要保存一个比例参数,但应该不是av-canvas解决的问题,还是需要用户自己解决。

caohongzhi1 avatar Nov 20 '24 07:11 caohongzhi1

有一个疑问,目前rect只能在av-canvas的容器内移动,容器外不展示也不可点选,这个有什么办法修改吗?目前移动是有一定安全边界的,但是会造成过小的素材无法移动到边缘位置,我觉得rect不可以完全移出容器是合理的做法。

caohongzhi1 avatar Nov 22 '24 03:11 caohongzhi1

目前的自定义 UI 组件需求是:

  1. 只需要修改一下边框颜色、控制点大小等小调整
  2. 现在的 UI 只能在 av-canvas 的容器内使用,容器外不展示也不可点选

如果没有 UI,只有开放的 API,自己构建 UI 也是很不错的,这样也能解决我现在的需求

billyct avatar Feb 21 '25 01:02 billyct