@lazycatcloud/vue-media-preview 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/components/MediaPreview.vue.d.ts +2 -2
- package/dist/components/PreviewStage.vue.d.ts +8 -4
- package/dist/core/gestures/dismissGesture.d.ts +3 -0
- package/dist/core/gestures/openingGesture.d.ts +18 -0
- package/dist/core/gestures/pointerVelocity.d.ts +7 -0
- package/dist/core/gestures/zoomBounds.d.ts +5 -0
- package/dist/core/rendering/image.d.ts +17 -0
- package/dist/core/resources/bitmap/client.d.ts +4 -0
- package/dist/core/resources/bitmap/decode.worker.d.ts +1 -0
- package/dist/core/resources/bitmap/types.d.ts +14 -0
- package/dist/core/resources/imageLimits.d.ts +1 -1
- package/dist/core/resources/imageOwnership.d.ts +9 -0
- package/dist/core/resources/imageRequest.d.ts +1 -1
- package/dist/core/resources/imageResource.d.ts +11 -0
- package/dist/core/resources/imageStore.d.ts +3 -0
- package/dist/core/resources/imageTransport.d.ts +4 -4
- package/dist/core/resources/validation/image.d.ts +13 -0
- package/dist/core/types.d.ts +6 -0
- package/dist/core/usePreviewViewport.d.ts +1 -1
- package/dist/core/video/source/session.d.ts +8 -0
- package/dist/core/video/source/types.d.ts +8 -0
- package/dist/core/video/timeline/buffered.d.ts +12 -0
- package/dist/core/video/timeline/index.d.ts +15 -0
- package/dist/core/video/timeline/time.d.ts +3 -0
- package/dist/core/video/useVideoPlayback.d.ts +22 -6
- package/dist/core/video/useVideoSource.d.ts +3 -3
- package/dist/index.d.ts +3 -1
- package/dist/renderers/ImageCanvasRenderer.vue.d.ts +15 -4
- package/dist/renderers/LivePhotoRenderer.vue.d.ts +7 -2
- package/dist/renderers/VideoDomRenderer.vue.d.ts +29 -8
- package/dist/vue-media-preview.cjs +2 -1
- package/dist/vue-media-preview.css +1 -1
- package/dist/vue-media-preview.js +1907 -1516
- package/docs/testing.md +12 -2
- package/docs/vue-media-preview.md +35 -7
- package/package.json +1 -1
package/docs/testing.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
执行 `pnpm test`。所有测试为纯本地前端验证,不依赖相册服务、数据库或远程工具链。
|
|
6
6
|
|
|
7
|
-
测试按职责收敛为
|
|
7
|
+
测试按职责收敛为 11 个文件。相同契约只保留一处断言,不按历史问题持续追加同类用例;会话层保留组件装配和异步交接才能验证的行为。
|
|
8
8
|
|
|
9
9
|
| 文件 | 保留的核心契约 |
|
|
10
10
|
| --- | --- |
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
| `viewport.test.ts` | 鼠标与双指缩放、长按竞争、平移与边缘翻页、下拉和手势取消 |
|
|
14
14
|
| `pager.test.ts` | 翻页阈值、连续接管、反向、首尾阻尼和过期动画 |
|
|
15
15
|
| `renderer.test.ts` | 解码与绘制交接、资源升级、过期结果、卸载释放、原图保护和长图缩放策略 |
|
|
16
|
+
| `imageBitmap.test.ts` | Worker 解码/缩放、可转移位图交接、取消和错误时终止 |
|
|
17
|
+
| `imageResource.test.ts` | JPEG Blob 位图准备、取消后释放、非 JPEG 格式保留元素路径 |
|
|
16
18
|
| `imageStore.test.ts` | 共享所有权、当前页优先、并发/内存预算、取消与传输大小限制 |
|
|
17
19
|
| `video.test.ts` | 播放/进度操作、异步拒绝、错误重试、宿主传输生命周期和有像素预算的当前帧捕获 |
|
|
18
20
|
| `livePhoto.test.ts` | 封面与一次播放、失败/切页复位、当前帧、长按与松手、加载中点击及迟到结果 |
|
|
@@ -116,6 +118,8 @@ AI、OCR、云端分享等入口显示明确的演示状态,不计为业务功
|
|
|
116
118
|
|
|
117
119
|
## 海量列表、视频缩放和原图保护
|
|
118
120
|
|
|
121
|
+
大图绘制回归入口:`http://localhost:2391/qa/image-motion.html`。先生成真实的 6000×6000 本地 JPEG,再模拟双指放大到 4 倍。页面检查手势中使用低开销采样、松手后恢复高清采样,以及移动端与 PC loading 的计算样式一致;390×520 视口下最终可见源区域约 300 万像素。该数字表示传给 drawImage 的源矩形面积,不代表解码内存减少,也不是帧率提升倍数。“验证越过屏幕边缘”检查图片由留白放大至超出视口时的连续位移,不应突然补偿先前被居中限制的位移;自动测试另覆盖旋转方向及反向跨界。可手动拖动、双击复位;手机真机手感仍需使用实际相册图片验收。自动测试覆盖四个旋转方向、平移、长图边界、松手后的高清重绘及不重复请求原图。
|
|
122
|
+
|
|
119
123
|
首次缩放回归:打开尚未浏览过的图片,立即双击或双指放大;运动期间保持同一张位图,任何倍率都不因缩放触发原图请求。宿主按自身策略加载原图,已在加载的适屏图或原图若在运动中完成,等操作结束再替换当前画面。连续滚轮停止 120ms 后才恢复升级。慢网下放大、缩小、关闭和换页均不能被等待资源阻塞。渲染器测试覆盖延后请求与延后替换;视口测试覆盖动画中断、指针释放和滚轮静止。
|
|
120
124
|
|
|
121
125
|
边界回归:在 PC 和移动端滚动列表,使卡片靠近顶部、底部或左右边缘,再打开和关闭。缩略图端只取列表内实际可见的部分,展开后完整画幅不受列表边界裁切;动画末尾不能突然露出大图的一部分。覆盖半露卡片、嵌套滚动容器、偏移 PC 页面与完全不可见的目标。
|
|
@@ -126,7 +130,7 @@ AI、OCR、云端分享等入口显示明确的演示状态,不计为业务功
|
|
|
126
130
|
|
|
127
131
|
- `/?view=mobile&count=10000` → 我的 → 定位第 5000 张。卡片应定位到视口中间;打开、翻页、关闭应收回当前卡片,追加分页不应跳到列表末尾。可在开发者工具查看 `.mp-virtual-timeline` 的 data-total-items/data-rendered-items,DOM 数量保持在视口附近。
|
|
128
132
|
- 视频筛选 → 打开视频 → 双击画面(避开中心播放按钮),应放大;再次双击复位。双指缩放、边缘翻页、播放中缩放还需手机测试。系统原生全屏手势归浏览器管理。
|
|
129
|
-
- `/?view=mobile&test` → 测试场景“超大原图保护” → 打开一张测试 →
|
|
133
|
+
- `/?view=mobile&test` → 测试场景“超大原图保护” → 打开一张测试 → 点击原图,应保留适屏图、不显示分辨率提示,原图操作切换为下载原图。该场景模拟像素元数据,不下载/解码真实百兆原文件。
|
|
130
134
|
- “大图慢 5 秒”检查原图按钮加载状态;“卡片也未加载”检查当前页等待期间仍可关闭/换页。真实网络取消及优先级由 ImageStore 测试覆盖;浏览器网络面板可看资源请求与取消。
|
|
131
135
|
- 自动测试覆盖并发优先级、最后持有者取消、无 Content-Length 的流量上限、超大图请求拦截、原图 ready 状态、相册源解析器实际视口参数、当前画面后邻页加载、列表增删重排、十万条布局定位与视频缩放。
|
|
132
136
|
|
|
@@ -139,3 +143,9 @@ AI、OCR、云端分享等入口显示明确的演示状态,不计为业务功
|
|
|
139
143
|
## PC 裁切合成回归
|
|
140
144
|
|
|
141
145
|
共享裁切窗口的布局宽高在动画期间固定,只改变 transform;图片使用反向缩放保持比例。几何测试验证 70–80% 区间及关键帧之间的中心点、宽高误差,保留方形收回、旋转及中途反向。PC 相册沿用用户自行启动的前端服务,不由组件库测试重启。
|
|
146
|
+
|
|
147
|
+
### 真机首次缩放性能
|
|
148
|
+
|
|
149
|
+
在 Android WebView 远程调试中录制 Performance:刷新页面,打开尚未缩放的本地 JPEG,双指放大缩回,停两秒,再次放大缩回。按 pointerdown/pointerup 划分两轮,分别检查主线程 RunTask、GPU 图片解码/上传与 RAF 回调;不能用第二轮缓存后的手感代表首次性能。JPEG 在独立 Worker 从 Blob 解码并生成完整/运动 ImageBitmap 后才交给 Canvas,观察缩放区间是否仍出现同步 JPEG 解码长任务。额外验证切图、关闭、取消下载和缓存淘汰时没有使用已关闭的位图;检查 EXIF 旋转照片与非 JPEG 图片的方向、画幅和颜色。
|
|
150
|
+
|
|
151
|
+
2026-10-29 在 Redmi Note 12 Turbo 的相册 WebView 实测:优化前首轮缩放有约 245ms 主线程任务,其中 JPEG 解码约 172ms。完整解码和运动位图缩放放入 Worker 后,五次快速打开后缩放的录制区间内,没有主线程 JPEG 解码或 GPU 图片解码缓存事件,RAF 绘制回调最大 0.32–0.99ms。仍观察到约 38–56ms 的整页样式更新,不能把绘制回调耗时当成完整帧耗时,也不能据此承诺所有设备无掉帧。用户真机反馈为“基本上不卡顿了”。
|
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
useVideoPlayback / VideoControls:原生播放状态、进度定位与独立控制层
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
辅助能力分别位于 `src/core/gestures/`、`src/core/resources/`、`src/core/transition/`。相册的 MediaInfo、Store、权限、云端切换和 API 不进入核心库。
|
|
17
|
+
辅助能力分别位于 `src/core/gestures/`、`src/core/resources/`、`src/core/rendering/`、`src/core/transition/`。相册的 MediaInfo、Store、权限、云端切换和 API 不进入核心库。
|
|
18
|
+
|
|
19
|
+
图片绘制由 `core/rendering/image.ts` 计算旋转、平移后的可见源区域。放大时只将可见部分交给 Canvas 采样,完整入屏时直接绘制整图;不创建额外大位图,也不改变原图资源。手势与动画期间使用低开销平滑采样,结束后主动请求高清重绘,即使没有资源升级也恢复清晰度。Canvas 的像素预算和 DPR 不随手势改变,避免触摸期间重新分配画布。相册外观下的图片 loading 在组件内共用 PC 的圆环尺寸、线宽和配色,移动端不再依赖另一套宿主样式。
|
|
20
|
+
|
|
21
|
+
双指缩放每步以实际显示的变换重建基准。某个方向尚未超出视口时,被居中限制拒绝的位移不会累计;跨过屏幕边缘后只应用后续手势增量,避免突然补偿旧位移。横图、竖图和旋转后的边界使用同一规则,剩余单指仍不能误触翻页。
|
|
18
22
|
|
|
19
23
|
## 转场
|
|
20
24
|
|
|
@@ -102,13 +106,21 @@ function resolveAnchor(media: PreviewMedia): PreviewAnchor {
|
|
|
102
106
|
|
|
103
107
|
### 视频播放
|
|
104
108
|
|
|
109
|
+
`videoLoader(video, media, reportError, reportDuration)` 可由宿主绑定 HLS 等传输,返回释放函数。宿主可通过 `reportDuration(seconds)` 回传点播清单总时长;有效的正数优先于浏览器时长,传入 0 或无效值恢复使用浏览器时长。时间、拖动定位及缓冲比例共用这一时长,播放时间限制在 0 到总时长之间。切源和重试清除旧时长,已释放源的延迟回调不生效。仅当前可加载视频绑定,离开或卸载立即释放;重试重新绑定。默认使用原生视频地址。组件库不依赖 HLS 播放库。现有三参数 loader 可继续使用。
|
|
110
|
+
|
|
111
|
+
时间显示向下取整到秒,使用补零的“分:秒”,例如 2.99 秒显示 `00:02`,3600 秒显示 `60:00`;小数时长仍保留用于进度计算。
|
|
112
|
+
|
|
113
|
+
视频缓冲条表示真实的 `video.buffered` 时间区间,按各区间起止位置分别绘制,不把区间间隙涂成已缓冲。浏览器 `readyState >= 4` 且尾差不超过 `min(250ms, duration × 5%)` 时补齐最后一段;收到 `ended` 后,末尾已保留的缓冲段直接延伸到总时长,重播后该段仍补齐,解决音视频轨道或清单时长差异造成的尾部留空。播放结束时当前进度同步到总时长。已被浏览器回收的区间不会继续显示;`canplaythrough` 会刷新缓冲状态。
|
|
114
|
+
|
|
115
|
+
视频内部按职责拆分:`core/video/timeline/` 是不依赖 Vue 的时间轴,统一时长、时间限制、缓冲区间及完成尾段;`useVideoPlayback` 管理响应式播放状态和拖动交互;`core/video/source/session.ts` 管理传输所有权并拦截已释放源的错误/时长回调;`useVideoSource` 仅将 Vue 生命周期绑定到源会话。普通视频与实况使用同一源观察接口,组件库不解析业务地址或 HLS 清单。缓冲完成使用明确的 pending/retained 状态,切源重置,重播保留实际缓存尾段。
|
|
116
|
+
|
|
105
117
|
按相册的控件和行为实现,不依赖 video.js。普通视频默认保留封面,点击播放,有声音、不循环;播放结束显示重播。暂停时显示居中播放按钮,缓冲与定位时显示转圈。底部提供播放/暂停、当前时间、缓冲进度、拖动定位、总时长、静音和全屏。拖动时显示目标时间,并暂时暂停;松手后只有原本正在播放的视频恢复播放。取消拖动不会提交时间,切页和关闭会取消恢复请求。
|
|
106
118
|
|
|
107
119
|
控制栏通过 Teleport 挂载到翻页轨道之外,横滑时位置固定;单击画面的工具栏显隐同时作用于视频控制栏。视频与图片共用双击、双指、平移和边缘翻页缩放能力,仍可横滑和下拉收回;系统原生视频全屏中的手势由浏览器接管。Space 切换播放,Enter 切换全屏;进度条获得焦点时左右键定位 5 秒,Home/End 定位首尾,不触发翻页。全屏优先使用标准 Fullscreen API,iOS 使用系统视频全屏入口。离开当前项、切到后台和卸载时暂停。
|
|
108
120
|
|
|
109
121
|
`VideoDomRenderer` 可独立使用,支持 `active`、`enabled`、`autoplay`、`muted`、`loop`、`controls`、`controlsVisible`;默认内联控制层,`teleportControls` 和 `controlsTarget` 允许外壳指定固定挂载位置。挂载目标必须先存在。外壳可用 `--mp-video-controls-bottom` 调整控制栏与业务操作的间距。实况在入场完成后预载动态资源,点击实况按钮或按 Space 播放一次,也可在画面内长按约 650ms 播放、松手停止,保留声音;结束/停止时以 300ms 淡回静态封面并复位,不显示普通视频进度条。切页、关闭及切后台会暂停。加载和错误状态通过 toolbar 的 `liveStatus`(loading/idle/playing/error)提供,`livePlaying` 表示本次播放请求。播放中关闭会先合成当下封面与视频的混合帧,带上实况轻微放大的实际比例,继续共享元素收回。
|
|
110
122
|
|
|
111
|
-
当前使用浏览器原生媒体解码,Demo 验证 MP4
|
|
123
|
+
当前使用浏览器原生媒体解码,Demo 验证 MP4;HLS/MSE、自动转码、清晰度切换与云端资源回退由宿主通过 videoLoader 接入。跨域视频也须允许匿名 CORS,才能将当前帧用于收回动画。
|
|
112
124
|
|
|
113
125
|
### 图片与翻页
|
|
114
126
|
|
|
@@ -141,18 +153,24 @@ preview.13 的长按与单击/双击、翻页、下拉和双指共用手势仲
|
|
|
141
153
|
|
|
142
154
|
`PreviewStage.inactiveResetDuration` 默认为 0(即时复位);MediaPreview 传入当前页轨道的实际过渡时长,让退出缩放和切页同时执行。`core.resetTransform(duration = 0)` 支持指定平滑复位时长。通用边界分配位于 core/gestures/edgePaging.ts。
|
|
143
155
|
|
|
144
|
-
|
|
145
156
|
## 资源调度与大原图
|
|
146
157
|
|
|
158
|
+
图片的元数据规划仍由 `imageRequest.ts` 决定,实际像素验收与超限提示映射集中在 `core/resources/validation/image.ts`,下载解码和每个缓存持有者使用同一验收函数。`ImageStore` 负责共享、排队和缓存释放,`imageOwnership.ts` 独立管理当前请求与正在显示的租约,渲染器只消费请求计划与加载结果;宿主的自动原图阈值及预览 URL 不进入组件库。
|
|
159
|
+
|
|
160
|
+
原图超限时静默保留预览,不显示分辨率调整提示;优化预览由宿主通过 `previewSrc` 提供,相册超限时提供 `size=0` 资源。原图地址与优化预览地址应分开,避免把优化资源当作超限原图过滤。
|
|
161
|
+
|
|
162
|
+
JPEG 资源在传输层校验实际尺寸后,在独立 Worker 从同一 Blob 解码并生成 ImageBitmap,Canvas 直接复用准备好的像素,避免首轮缩放触发 Chromium 对 HTMLImageElement 的惰性 JPEG 解码。元素继续负责尺寸及公开媒体句柄;其他图片格式沿用元素解码路径。运动阶段使用最多 600 万像素的独立位图,静止后恢复完整分辨率;收回动画也复用运动位图,不重新从 JPEG 元素解码。缓存同时计算元素、完整位图和运动位图的像素预算,淘汰时显式关闭位图;取消后才返回的位图也立即关闭。Worker 通信协议集中在 `core/resources/bitmap/types.ts`,client 负责任务生命周期,worker 负责解码及缩放。解码与生成运动位图都在 Worker 内执行,避免部分 WebView 将异步解码完成后的缩放回调放在主线程;每个加载任务拥有独立 Worker,由已有并发队列约束,完成或取消后终止。渲染器单独持有正在显示的资源租约,重载或升级期间直到新画面接替才释放旧租约,防止缓存淘汰关闭仍在绘制的像素。`ImageLease.ready` 仍返回图片元素,新增 `resource` 返回包含绘制源和释放方法的资源;自定义 `ImageStore` loader 也须返回此资源结构。
|
|
163
|
+
|
|
164
|
+
|
|
147
165
|
当前页缩略图与适屏图先发起,先到先显示。当前页已有可用画面(包括卡片种子)且入场结束后,才启动左右邻页缩略图。全局最多同时处理 3 个图片请求/解码任务,排队任务按当前页优先;没有持有者的下载与排队任务会取消。原图仅当前页请求,离开当前页不为邻页主动加载原图。视频的 preload 是浏览器提示,封面和视频传输不受 ImageStore 队列控制。
|
|
148
166
|
|
|
149
167
|
`originalStatus` 为 idle/loading/ready/error/blocked,只有原图真实解码完成才报告 ready。失败可以再次调用 original() 重试。Demo 按状态显示加载中、重试原图或下载原图,不再点击即标记成功。
|
|
150
168
|
|
|
151
|
-
默认保护参数:原图最多
|
|
169
|
+
默认保护参数:原图最多 6400 万像素、传输最多 32MiB;缩略/适屏传输最多 12MiB。`resourcePolicy.maxOriginalPixels` 同时控制原图元数据预检查和实际图片尺寸检查,不再叠加固定的 2400 万像素上限。传输同时检查 Content-Length 和累计流量,无长度响应也受限制;同一 URL 的共享缓存仍按各调用方的像素策略分别验收。视频封面只请求缩略/适屏图,不用视频原始宽高判断能否请求封面,但封面的实际图片尺寸仍受策略约束。图片尺寸验收发生在图片加载后,不保证限制浏览器初始解码的峰值内存。Canvas 绘图表面上限 600 万像素;96MiB 是可回收缓存预算,正在显示/持有的位图不强制销毁,不能视为浏览器进程的内存硬上限。双击缩放按画幅与适屏比例计算,长图可获得更大倍率。
|
|
152
170
|
|
|
153
|
-
**50/100MB 原文件应由相册提供独立缩略图和适屏图。** 此版本可先看这些衍生图并保留下载原文件入口;不会直接解码超限原文件,也未实现瓦片式高清浏览。若大原图的 thumbnail/previewSrc 仍指向 src
|
|
171
|
+
**50/100MB 原文件应由相册提供独立缩略图和适屏图。** 此版本可先看这些衍生图并保留下载原文件入口;不会直接解码超限原文件,也未实现瓦片式高清浏览。若大原图的 thumbnail/previewSrc 仍指向 src,会跳过这些危险地址并保留可用预览。像素判断依赖正确元数据;资源实际像素检查发生在浏览器加载后,无法保证恶意/错误元数据图像的解码峰值。JPEG 任务取消时终止所属 Worker;其他格式的元素解码仍受浏览器控制。真实百兆原图、低内存手机、iOS Safari 仍需使用相册真实数据专项验收。
|
|
154
172
|
|
|
155
|
-
图片加载现使用 fetch + Blob,CORS、签名 URL 和浏览器 CSP
|
|
173
|
+
图片加载现使用 fetch + Blob,CORS、签名 URL 和浏览器 CSP 必须允许相应请求、blob: 图片及内联 blob: Worker;同源请求携带同源凭据,跨域资源应提供可访问的 CORS URL。当前不提供自定义鉴权请求头或云端失败回退协议。
|
|
156
174
|
|
|
157
175
|
```ts
|
|
158
176
|
import {defaultImagePolicy} from '@lazycatcloud/vue-media-preview'
|
|
@@ -190,4 +208,14 @@ const resolveImageSource = (media, quality, targetWidth) =>
|
|
|
190
208
|
|
|
191
209
|
`goTo(index)` 在会话打开时同步宿主下标;相邻项平滑翻页,跨多项直接定位。列表窗口更新按稳定 ID 保留当前项,仅调整其下标,不取消翻页动画或拖动;页面引用也以媒体 ID 管理。删除当前媒体才重新定位到邻项。`element` 是预览根元素,`canvasElement()` 返回当前图片画布,`mediaElement()` 返回当前解码媒体,`togglePlayback()` 控制视频播放。`ready(index, quality)` 仅报告当前页真实就绪。
|
|
192
210
|
|
|
193
|
-
|
|
211
|
+
### 缩放边界
|
|
212
|
+
|
|
213
|
+
图片以适应屏幕时的大小作为 1 倍,默认最大缩放为 16 倍;双击仍按画幅选择 2–8 倍。双指越过 1 倍或 16 倍时允许少量阻尼拉伸,松手或取消后平滑回到边界。剩余单指只平移,不会把这次缩放转成翻页或下拉关闭;再次接手回弹时从当前可见大小继续。
|
|
214
|
+
|
|
215
|
+
### 快速下滑与关闭画面
|
|
216
|
+
|
|
217
|
+
下滑关闭统一采用屏幕短边 14% 的距离阈值;快速下甩超过 16 CSS 像素且速度超过 0.35 CSS 像素/毫秒也会关闭。打开阶段与打开后的速度采样规则一致,松手前停顿 100 毫秒后不再按轻甩判定。
|
|
218
|
+
|
|
219
|
+
打开动画期间的单指接触会保留到打开交接完成,再继续跟手下滑;若下滑已达到关闭阈值并提前松手,直接反向收回打开动画。宿主应在 `opening` 和 `open` 阶段允许 `gesturesEnabled`,关闭及其他交互覆盖层仍禁用。
|
|
220
|
+
|
|
221
|
+
关闭动画通过 `retainFrame()` 持有像素,清晰图切换、请求取消及渲染器卸载不会提前释放该帧。持有者必须在完成、失败或取消时调用返回值的 `release()`;MediaPreview 统一在 `finally` 中释放。原有 `captureFrame()` 保持即时借用语义,跨异步操作持有画面应使用 `retainFrame()`。独立视频、实况合成帧和普通图片采用同一个帧持有协议。
|
package/package.json
CHANGED