fix: 动作详情GIF动图播放 + 收藏按钮避让胶囊

This commit is contained in:
2026-07-22 16:02:04 +08:00
parent 98126c62ec
commit 47c4801f34
9 changed files with 924 additions and 6 deletions

View File

@@ -0,0 +1,83 @@
// gif-player微信 <image> 组件不播放 GIF 动画(只渲染首帧),
// 这里用 Canvas 2D + 纯 JS 解码器(omggif)逐帧绘制,实现真正的动图播放。
const { GifReader } = require('../../utils/vendor/omggif.js');
Component({
properties: {
// GIF 地址(通常是 ex.gifUrl原始 180px 动图)
src: { type: String, value: '' }
},
data: {
failed: false
},
lifetimes: {
attached() {
this._aborted = false;
this._timer = null;
if (this.data.src) this._load();
},
detached() {
this._aborted = true;
if (this._timer) { clearTimeout(this._timer); this._timer = null; }
}
},
methods: {
_load() {
const src = this.data.src;
if (!src) return;
wx.request({
url: src,
responseType: 'arraybuffer',
success: (res) => {
if (this._aborted) return;
try {
this._play(res.data);
} catch (e) {
console.error('[gif-player] decode fail', e);
this.setData({ failed: true });
}
},
fail: () => {
if (!this._aborted) this.setData({ failed: true });
}
});
},
_play(buffer) {
const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer);
const reader = new GifReader(bytes);
const numFrames = reader.numFrames();
if (!numFrames) { this.setData({ failed: true }); return; }
const w = reader.width;
const h = reader.height;
const q = wx.createSelectorQuery().in(this);
q.select('#gifCanvas').fields({ node: true, size: true }).exec((rs) => {
if (this._aborted || !rs || !rs[0] || !rs[0].node) return;
const canvas = rs[0].node;
const ctx = canvas.getContext('2d');
// 画布内部分辨率=GIF 原始分辨率CSS 负责缩放到容器
canvas.width = w;
canvas.height = h;
const rgba = new Uint8ClampedArray(w * h * 4);
let frame = 0;
const render = () => {
if (this._aborted) return;
try {
// decodeAndBlitFrameRGBA 会按 GIF 的 disposal 规则在 rgba 上累积绘制
reader.decodeAndBlitFrameRGBA(frame, rgba);
const img = ctx.createImageData(w, h);
img.data.set(rgba);
ctx.putImageData(img, 0, 0);
} catch (e) {
console.error('[gif-player] frame fail', e);
return;
}
const info = reader.frameInfo(frame);
const delay = info && info.delay ? info.delay : 100;
frame = (frame + 1) % numFrames;
this._timer = setTimeout(render, Math.max(16, delay));
};
render();
});
}
}
});

View File

@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@@ -0,0 +1,3 @@
<view class="gif-player">
<canvas type="2d" id="gifCanvas" class="canvas"></canvas>
</view>

View File

@@ -0,0 +1,2 @@
.gif-player { width: 100%; height: 100%; }
.canvas { width: 100%; height: 100%; display: block; }