Files
fitness-coach/miniprogram/components/gif-player/gif-player.js

84 lines
2.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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();
});
}
}
});