普通视图

发现新文章,点击刷新页面。
昨天以前首页

微信小程序开发02:原始人也能看懂的着色器与视频处理

作者 海石
2026年3月21日 18:41

往期回顾:

微信小程序开发01:XR-FRAME的快速上手

1、背景

还记得01时,3.3.3章节的成果展示吗?

image.png

虽然图片识别成功了,并且视频加载完毕了

但是视频存在大规模的绿色背景,这是业务不期望展示的

期望的效果是抠除绿色背景,仅保留人物主体,如下图

e1ea2feb31f439a3ea638f80d006b27d.jpg

今天我们就来尝试对视频图层做调整

2、温故知新

为了快速实现MVP,我们忽略了很多信息,但这些信息对我们基于MVP二次开发时,比较重要

我们需要了解一下当前demo工程的结构:

mermaid-1774086497015.png

清晰的分层架构 :

  • Pages 层 (如 xr-template-water/index.wxml ):

    • 负责页面配置和展示
    • 定义标题、介绍等元数据
    • 处理页面级别的交互
  • Components 层 (如 xr-template-water/index.wxml ):

    • 包含实际的 XR 场景逻辑
    • 处理 3D 渲染、AR 追踪等核心功能
    • 实现具体的业务逻辑
  • 共享行为机制 share-behavior.js 的设计 :

    • 提供统一的分享功能实现
    • 统一处理 AR 追踪状态初始化
    • 减少重复代码,提高一致性
    • 被所有 template 组件复用

再来看看数据流向:

用户交互
    │
    ▼
Pages 层 (页面配置)
    │
    ▼
xr-demo-viewer (容器组件)
    │
    ├─► 显示 UI (标题、介绍、代码)
    │
    └─► <slot> (主内容)
            │
            ▼
Components/Template (业务组件)
    │
    ├─► share-behavior (共享功能)
    │       │
    │       ├─► 分享初始化
    │       └─► AR 状态管理
    │
    └─► xr-scene (XR 场景)
            │
            ├─► 资源加载
            ├─► 3D 渲染
            └─► AR 追踪

像我们在第一期做的改造,得益于此demo工程的优秀设计,当我们想要新增功能时,只要做4步操作:

  • 创建 pages/template/xr-template-newFeature
  • 创建 components/template/xr-template-newFeature
  • 使用 xr-demo-viewer 包裹
  • 引入 share-behavior 获得共享功能

3、透明视频

ok,接下来我们进入正题,如何让绿幕视频可以扣除绿幕,实现一些付费AR软件提供的功能?

有两条路:

  1. 直接导入微信小程序支持的透明视频
  2. 通过自定义着色器计算每个像素颜色与绿色背景的距离,使用 smoothstep 函数根据距离动态调整透明度,使绿色背景变为透明而其他内容保持不透明。

第一条路需要使用AE等视频处理软件,导出成果,对素材的质量要求较高,也就是对上游有依赖

因此,不想被上游依赖,我们便选择第二条路,自己实现视频扣除纯色背景的功能

况且,XR FRAME本就支持着色器

// XR-Frame 提供的 API
wx.getXrFrameSystem().registerEffect("chroma-key", createChromaKeyEffect);

scene.createEffect({
  "name": "chroma-key",
  "shaders": [vertexShader, fragmentShader]  // 支持 GLSL 着色器
})

ok,写到这里,大家应该还是困惑,着色器和视频有什么关系?

着色器就像一个超级快的修图师,把视频的每一帧图片都检查一遍,把绿色的像素变成透明,然后把处理好的图片贴在3D模型这块"布料"上,纹理材质就是这块布料和修图师的组合

大白话说完,我们来看看处理的过程

我们创建一个新的资源,让它被包裹在xr-assets下,这个新的资源就是我们刚刚提到的“修图师”,它在小程序里的体现就是“材质”,即xr-asset-material

  <xr-assets>
    <xr-asset-load type="video-texture" asset-id="ayuan-video" src="https:/xxxx.mp4" options="autoPlay:true,loop:true" />
    <xr-asset-material asset-id="chroma-key-mat" effect="chroma-key" />
  <xr-assets>

asset-id我们很熟悉了,对应于材质的名字,就和视频的asset-id一样

effect是效果,即材质的模板

通过对effect的设置,我们可以调整光照模式,等等

image.png

"chroma-key"是我们通过scene.createEffect方法创造出来的一种自定义效果

部分源码如下:

function createChromaKeyEffect(scene) {
  return scene.createEffect({
    "name": "chroma-key",  // 给这个修图师起个名字叫"绿幕扣除"
    
    // 定义要用的工具:视频图片
    "images": [{
      "key": "u_baseColorMap",  // 视频纹理的代号
      "default": "white",
      "macro": "WX_USE_BASECOLORMAP"
    }],
    
    // 定义修图规则(着色器代码)
    "shaders": [
      // 第一个着色器:负责把3D模型放到屏幕上
      `顶点着色器...`,
      
      // 第二个着色器:负责给每个像素上色(这里是关键!)
      `片元着色器...
        vec4 color = texture2D(u_baseColorMap, vTextureCoord);  // 取出视频的像素颜色
        
        vec3 greenKey = vec3(0.055, 0.816, 0.294);  // 绿幕的颜色
        float dist = distance(color.rgb, greenKey);  // 算一下这个像素离绿色有多远
        
        float threshold = 0.40;  // 设定一个距离标准
        float alpha = smoothstep(threshold - 0.005, threshold + 0.005, dist);
        // 如果离绿色很近,透明度就变成0(看不见)
        // 如果离绿色很远,透明度就保持1(看得见)
        
        color.a *= alpha;  // 把算好的透明度应用到像素上
      `
    ]
  })
}

写完之后,别忘了在系统中注册,这样之后到处都可以使用

// 在组件加载时执行
lifetimes: {
  async attached() {
    const xrFrameSystem = wx.getXrFrameSystem();
    
    // 把这个修图师注册到系统里,以后可以随时用
    xrFrameSystem.registerEffect("chroma-key", createChromaKeyEffect);
  }
}

然后我们就要把之前的视频,和我们刚刚创建的材质,组合起来

  • 创建一个3D平面模型
  • 给这个模型穿上"chroma-key-mat"这件衣服
  • 把视频"ayuan-video"贴在衣服上
  • 把模型放到场景里
handleARReady: async function ({ detail }) {
  // 创建一个3D平面(就像一块板子)
  const videoPlane = this.scene.createElement(xr.XRMesh, {
    geometry: 'plane',           // 形状:平面
    material: 'chroma-key-mat',  // 材质:用刚才创建的“布料”
    uniforms: 'u_baseColorMap: video-ayuan-video',  // 把视频贴在布料上
    position: '0 0.5 0',      // 位置
    scale: '0.8 0.45 1',      // 大小
  });
  
  // 把这个平面添加到场景中
  lockItemEle.addChild(videoPlane);
}

一句话总结 :代码先创建了一个"绿幕扣除"的修图方案,然后创建一块用这个方案的布料,最后把视频贴在这块布料上,视频的每一帧都会自动被修图师处理,绿色背景就变透明了!

mermaid-1774089305244.png

附录

架构图

mermaid-1774084518187.png

【uniapp】小程序支持分包引用分包 node_modules 依赖产物打包到分包中

2026年3月19日 15:31

前言

5.04 版本之前的 uniapp 和 uniappx,小程序端不支持分包引用的 node_modules 依赖打包到分包中,这对于很多备受小程序主包体积超出困扰的开发者来说,显然不是一个好消息。为了解决这一问题,5.04 版本开始,hx项目或者 cli 项目支持分包引用的 node_modules 依赖打包到分包中。下面介绍下具体的操作步骤,示例项目请点击 ask.dcloud.net.cn/article/424…

分包优化

首先,需要在 mainfest.json 指定小程序节点下添加如下配置,例如:

{
  "mp-weixin": {
         "optimization": {
            "subPackages": true
          }
   }
}

筛选分包用的依赖

这一步尤为重要,要先梳理出哪些依赖是分包用到的,哪些是主包用到的,以及你期望的主包分包产物引用关系。

我们举一个简单的例子,主包用到了 lodash-esaddsubtract 函数,分包 sub 用到了 lodash-esmultiply 函数,这种分包用到的内容主包没用,就可以考虑使用这种策略,把 分包 sub 用到的 lodash-esmultiply 函数打包到 分包 sub 下,我们来看下 5.04 版本之前的效果

首先是项目结构

project.jpg

打包的产物体积

before.jpg

可以看到,用到的 lodash-es 的三个函数都被打包到了主包的 vendor.js 文件中。下面我们看下 5.04 如何解决这种问题

首先进入到分包的根目录,创建一个 package.json 文件,这里写分包需要用到的依赖,然后安装依赖

sub_node_modules.jpg

然后重新打包即可。

可以看到 分包 sub 根目录下面多了 vendor.js 文件,里面就是 lodash-esmultiply 函数

sub_vendor.jpg

after.jpg

注意事项

  • 该优化只对 vue3 项目生效
  • 支持 uniapp 和 uniappx 的小程序项目
  • 支持 hx 项目和 cli 项目,测试项目是 hx 项目,cli 项目同理
  • 仅支持 node_modules 中的 js 相关文件,不支持其他文件
  • 测试项目为附件六
  • 5.04 是指 hx 的版本号,uniapp 对应的依赖版本为 3.0.0-5000420260318001

小程序-下拉刷新不走回调函数

作者 喂_balabala
2026年3月18日 15:15

下拉刷新

配置与回调

  • .json 文件中添加配置开启下拉刷新
{
  "enablePullDownRefresh": true,//开启下拉刷新
  "backgroundTextStyle": "dark" //配置颜色
}
  • onPullDownRefresh 是下拉刷新的回调函数
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
    wx.showNavigationBarLoading();
},
  • stopPullDownRefresh 是自己写的停止下拉动效函数
stopPullDownRefresh() {
    wx.stopPullDownRefresh();
    wx.hideNavigationBarLoading();
},

Question

Q1: 下拉动效出来了,但是没有触发回调函数
原因: 页面问题:页面高度 = 屏幕高度,没有任何可滚动空间
  • 代码里的布局逻辑(必然是有这种结构):
page { height: 100%; }
.container { height: 100vh; }
.full_screen_container { height: 100%; }
  • 这种写法会导致:

  • 页面高度 = 手机屏幕高度 → 页面无法滚动 → 系统认为 “没有下拉动作” → 不触发 onPullDownRefresh 回调

  • 但!系统依然会播放下拉动画(因为配置开着)。

  • 下拉刷新动画是 【系统全局自动触发】 的,只要配置了 enablePullDownRefresh:true,不管页面能不能滚动、不管回调写没写,动画都会出现!

  • onPullDownRefresh ()回调函数是业务逻辑触发,必须满足页面存在可滚动区域 + 页面真的发生了下拉滚动行为才会执行!

解决方案
方案一:
  • 把根容器改成这样
/* 必须去掉固定 100% 高度!!! */
page {
  height: auto; /* 关键 */
  min-height: 100%;
}

.container {
  min-height: 100vh; /* 不能写死 height */
  overflow: visible;
}
方案二:
/* 给页面加一个看不见的高度,强制让页面可滚动 */
page::after {
  content: '';
  display: block;
  height: 1rpx;
}
❌
❌