fix: 动作详情GIF动图播放 + 收藏按钮避让胶囊
This commit is contained in:
83
miniprogram/components/gif-player/gif-player.js
Normal file
83
miniprogram/components/gif-player/gif-player.js
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user